C# cannot call python and pass parameters - python

I am a newbie in programming, and I have an MVC project. I want to use C# call the Python py file and pass parameters in order to make a chart. I refer to this article How do I run a Python script from C#? . I have used this method to make lots of charts, and they can execute and pass parameters successfully. There's only two files cannot be successfully called and passed parameters, and I think it's Python has an error that can't call the py.
Here is a failure below. When I run proportion.py alone in Spyder, it can successfully use the Fixed parameters. BUT when i use C# to call it, there will be no response. The syntax in the file has been confirmed to be executed without problems, and methods i have been tried lots of methods, but still not resolved. Please save my projetct, I will be very thankful!!Thanks for any help.
Here is how i use C# to call Python below.
public ActionResult Index(string searchString, DateTime? startdate, DateTime? enddate)
{
run_sound("D:/Python/pie.py", "" + sd + "", "" + ed + "", "" + searchString + "");
run_Emoanalysis("picture/AAApy.py", "" + sd + "", "" + ed + "", "" + searchString + "");
run_proportion("D:/Microsoft Visual Studio/MVC project/MVC project/picture /proportion.py", "" + sd + "", "" + ed + "", "" + searchString + "");
}
//The following is the function of run_proportion,
//other functions(run_sound) are the same as this method, and carefully confirmed.
private string run_proportion(string cmd, string sdate, string edate, string condition)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:/Users/user/AppData/Local/Programs/Python/Python38-32/python.exe";
start.CreateNoWindow = true;
start.Arguments = string.Format("{0} {1} {2} {3}", cmd, sdate, edate, condition);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
//Console.Write(result);
process.WaitForExit();
return result;
}
}
}
Here is proportion.py below that cannot be called and executed BY C#.
sd= sys.argv[1]
ed = sys.argv[2]
cdn = sys.argv[3]
sqlcom = "SELECT COUNT(DISTINCT url) FROM JIEBA WHERE (title LIKE '%" +str(cdn)+ "%') AND (post BETWEEN '" +str(sd)+ "' AND '" +str(ed)+ "')"
sqlcom2 = "SELECT COUNT(DISTINCT url) as KeyWordCount FROM JIEBA WHERE (title LIKE '%" +str(cdn)+ "%')"
df = pd.read_sql(sqlcom, con=cnxn)
df1 = np.array(df)
df0 = df1.tolist()
df2 = pd.read_sql(sqlcom2, con=cnxn)
df3 = np.array(df2)
df4 = df3.tolist()
df5 = str(df4[0][0])
print(df5)
df6 = str(df0[0][0])
print(df6)
c = int(df5)-int(df6)
# =============================================================================
count = float(df5)/float(df5)
print(count)
#
keyword = float(df6)/float(df5)
print(keyword)
#
keyword2 = str(round(float(df6)/float(df5)*100,2))+'%'
print(keyword2)
#
count2 = str(round((1-float(df6)/float(df5))*100,2))+'%'
print(count2)
# Change color
fig = plt.figure(figsize = (7,5))
ax = fig.add_subplot(111)
squarify.plot(sizes=[int(c),int(df6)], label=['期間"外"所佔筆數', '查詢後所佔比數'],value =(str(c)+'筆/'+str(df5)+'筆'+'\n'+'佔 '+str(count2),str(df6)+'筆/'+str(df5)+'筆'+'\n'+'佔 '+str(keyword2)), color=["red","blue"], alpha=.4)
plt.rcParams['font.sans-serif'] = 'Microsoft YaHei'
plt.rcParams['axes.unicode_minus'] = False
ax.set_title('關鍵字搜尋期間所佔比例',fontsize = 18)
plt.axis('off')
plt.tight_layout()
plt.savefig("D:\Microsoft Visual Studio\MVC project\MVC project\picture\keyproportion.png")

Also had some issues with running python, first would suggest replacing quotes in strings with a variable since it makes tracking them easier
var quote = '"';
also after doing the whole string do a
var commandUnescaped = Regex.Unescape(command);
pasting my way to call commands in case you want it, need to adapt it to windows , but same logic:
private (bool,string,string) RunCommand(string command, string args)
{
args = args.Replace("\"", "\\\"");
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = $"-c \"{args}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (string.IsNullOrEmpty(error))
{
return (true,output,error);
}
else
{
return (false,output,error);
}
}

Related

Calling a Python script from C# returns an empty output

I am creating a web application using ASP NET that runs on Windows Server 2012 using IIS 10. This application exposes a service at www.domain.com/service/execute which uses a python script executed using Python 3.9.13. Below I report the C# and Python code:
main.py
import pprint
pprint.pprint("Hello World!!")
ServiceController.cs
public class ServiceController : Controller {
public IActionResult Execute() {
var FileName = $#"C:\\path\\to\\python.exe";
var Arguments = $#"C:\\path\\to\\main.py";
var processStartInfo = new ProcessStartInfo() {
FileName = FileName,
Arguments = Arguments,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
using(var process = Process.Start(processStartInfo)) {
var taskError = process.StandardError.ReadToEndAsync();
var taskOutput = process.StandardOutput.ReadToEndAsync();
taskError.Wait();
taskOutput.Wait();
var errors = taskError.Result.Trim();
var output = taskOutput.Result.Trim();
return Ok(
$"FileName={FileName}" +
$"\n" +
$"Arguments={Arguments}" +
$"\n" +
$"errors={errors}" +
$"\n" +
$"output={output}"
);
}
}
}
Calling this service I don't get any errors or exceptions but the output is empty:
FileName=C:/path/to/python.exe
Arguments=C:/path/to/main.py
errors=
output=
If in the C# script I insert as Arguments a totally invented path to a non-existent python script I don't get any error/exception and the output is still empty.
I would expect an output like this:
FileName=C:/path/to/python.exe
Arguments=C:/path/to/main.py
errors=
output=Hello World!!

Empty output when calling a python script in C# project

I would like to call a python script in my C# project , I'm using this function to do the job but unfortunately I didn't get any result and the result variable shows always an empty output. I would like to know what's the reason of this
public string RunFromCmd(string rCodeFilePath, string args)
{
string file = rCodeFilePath;
string result = string.Empty;
try
{
var info = new ProcessStartInfo(pythonPath);
info.Arguments = #"C:\Users\MyPc\ExternalScripts\HelloWorld.py" + " " + args;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
using (var proc = new Process())
{
proc.StartInfo = info;
proc.Start();
proc.WaitForExit();
if (proc.ExitCode == 0)
{
result = proc.StandardOutput.ReadToEnd();
}
}
return result;
}
catch (Exception ex)
{
throw new Exception("R Script failed: " + result, ex);
}
}
Click Event ( Calling funtion )
private void Button1_Click(object sender, RoutedEventArgs e)
{
pythonPath = Environment.GetEnvironmentVariable("PYTHON_PATH");
RunFromCmd(pythonPath, "");
}
Python Script :
import sys
def main():
text = "Hello World"
return text
result = main()
I've fixed the issue by setting Copy if newer instead of Do Not Copy to HelloWorld.py Script

How to fix "can't find '__main__' module in '' error?

I am calling my python file from Windows Service
the code is working fine by itself
and the service works fine too
but when I call the python code from the windows service I get this error
my python code is this
import pyodbc
import pandas as pd
ConnectionString = "Driver={SQL Server};Server=XYZ;Database=ABCD;Trusted_Connection=yes;"
conn = pyodbc.connect(ConnectionString)
df_results = pd.read_sql("EXEC TestService" , conn)
and he is my windows service in c#
Log("In cmd", true);
try
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = PythonPath;
string Script = PythonSuggestedDiagnosesFile;
psi.Arguments = $"\"{Script}\"";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
string Errors = "";
string Results = "";
using (var process = Process.Start(psi))
{
Errors = process.StandardError.ReadToEnd();
Results = process.StandardOutput.ReadToEnd();
}
Log("In cmd : " + "Errors:\n" + Errors + "\n\nResults:\n" + Results);
}
catch (Exception ex)
{
Log("ERROR (cmd) : " + ex.ToString());
}
and the error I get is this
In cmd : Errors:
C:\Users\MyID\AppData\Local\Programs\Python\Python310\python.exe: can't find '__main__' module in ''
Results:
how to fix that?
You should pass proper WorkingDirectory to your ProcessStartInfo. Like this.

Not able to pass arguments with spaces to python script from c#

I am calling python script from c# using ProcessInfoStart method. As an argument it receives JSON and is input to python script.
It works fine it we pass JSON without having any spaces but if there is any space then original JSON is splitted till space and passes as argument and rest ignored
public static bool ExecutePythonScript(string jRequest, string fileType)
{
string pythonExePath = Convert.ToString(ConfigurationManager.AppSettings["PythonExe"]);
bool bIsExecutionSuccess = true;
try
{
var psi = new ProcessStartInfo();
psi.FileName = pythonExePath;
var script = #"C:Scripts\pdf-to-csv.py";
psi.Arguments = $"\"{script}\" \"{jRequest}\"";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
var errors = "";
var results = "";
using (var process = Process.Start(psi))
{
errors = process.StandardError.ReadToEnd();
results = process.StandardOutput.ReadToEnd();
}
if (!string.IsNullOrEmpty(errors))
bIsExecutionSuccess = false;
}
catch(Exception ex)
{
bIsExecutionSuccess = false;
}
return bIsExecutionSuccess;
}
Python script to accept arguments
input_params = sys.argv[1]
input_params = input_params.replace("'",'"')
data_params = json.loads(input_params)
Is there a way i can pass jRequest with spaces to python script.
Python script parameters can be wrapped in single quotes in order to read the whole string including spaces.
Try wrapping the JSON string in single quotes.

Write gpxlogger data to the same file

I have some very basic gpxlogger code that writes all the data to file quite well. (below)
gpxlogger -d -f /home/pi/Desktop/EPQ/temp_gps/gpslog
However I would like this code to always write to the same file without overwriting it. So if possible when it started logging it would go to the bottom of the file and start logging the data there, below what has already been logged.
Thanks,
Dan.
Javascript to read xml file
<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAA7_kD1t_m22HBF9feCaDPZxQwcATY4FXmxYwkk9LNWGtAQdNKTBS1kBsTEqrRPg2kWxuNdmf2JVCIkQ" type="text/javascript"></script>
<script src="http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/release/src/markermanager.js"> </script><script>
var map;
function initialize () {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(53.423027, -1.523462), 10);
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
map.addMapType(G_PHYSICAL_MAP);
map.setMapType(G_PHYSICAL_MAP);
addMarkersFromXML();
}
}
function addMarkersFromXML(){
var batch = [];
mgr = new MarkerManager(map);
var request = GXmlHttp.create();
request.open('GET', 'gpslog.xml', true);
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
var xmlDoc = request.responseXML;
var xmlrows = xmlDoc.documentElement.getElementsByTagName("trkpt");
for (var i = 0; i < xmlrows.length; i++) {
var xmlrow = xmlrows[i];
var xmlcellLatitude = parseFloat(xmlrows[i].getAttribute("lat"));
var xmlcellLongitude = parseFloat(xmlrows[i].getAttribute("lon"));
var point = new GLatLng(xmlcellLatitude,xmlcellLongitude);
//get the time of the pin plot
var xmlcellplottime = xmlrow.getElementsByTagName("time")[0];
var celltextplottime = xmlcellplottime.firstChild.data;
//get the elevation of the pin plot
var xmlcellplotelevation = xmlrow.getElementsByTagName("ele")[0];
var celltextplotelevation = xmlcellplotelevation.firstChild.data;
//get the number of satellites at the time of the pin plot
var xmlcellplotsat = xmlrow.getElementsByTagName("sat")[0];
var celltextplotsat = xmlcellplotsat.firstChild.data;
var htmlString = "Time: " + celltextplottime + "<br>" + "Elevation: " + celltextplotelevation + "<br>" + "Satellites: " + celltextplotsat;
//var htmlString = 'yes'
var marker = createMarker(point,htmlString);
batch.push(marker);
}
mgr.addMarkers(batch,10);
mgr.refresh();
}
}
request.send(null);
}
function createMarker(point,html) {
var marker = new GMarker(point);
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml(html);
});
return marker;
}
</script>
</head>
<body onload="initialize()" onunload="GUnload()">
<div id="map_canvas" style="width: 1350px; height: 800px"></div>
<div id="message"></div>
</body>
</html>
Here's another option.
Look at gps3.py, put it, and the following script into a directory.
It reads data from the gpsd; creates the gpx log file if it doesn't exist; appends "trackpoint" data to it when data exists; while maintaining the same file and appending "trackpoint" data after a restart.
Place both in the same directory and then have you javascript read the file..or put the entire structure in the same script.
#!/usr/bin/env python
# coding=utf-8
""" gpx logger to create and append a gpx formatted log of gpsd data """
import os
import time
import gps3
from datetime import datetime
the_connection = gps3.GPSDSocket()
the_fix = gps3.Fix()
the_log = '/tmp/gpx3.gpx'
creation = datetime.utcnow()
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
genesis = creation.strftime(fmt)
if not os.path.isfile(the_log):
header = ('<?xml version = "1.0" encoding = "utf-8"?>\n'
'<gpx version = "1.1" '
'creator = "GPSD 3.9 - http://catb.org/gpsd" '
'client = "gps3.py - http://github.com/wadda/gps3"'
'xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"'
'xmlns = "http://www.topografix.com/GPX/1/1"'
'xsi:schemaLocation = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">\n '
'<metadata>\n '
' <time>{}\n'
'</metadata>\n').format(genesis)
f = open(the_log, 'w')
f.write(header)
f.close()
try:
for new_data in the_connection:
if new_data:
the_fix.refresh(new_data)
if not isinstance(the_fix.TPV['lat'], str): # lat determinate of when data is 'valid'
latitude = the_fix.TPV['lat']
longitude = the_fix.TPV['lon']
altitude = the_fix.TPV['alt']
time = the_fix.TPV['time']
mode = the_fix.TPV['mode']
tag = the_fix.TPV['tag']
sats = the_fix.satellites_used()
hdop = the_fix.SKY['hdop']
vdop = the_fix.SKY['vdop']
pdop = the_fix.SKY['pdop']
trackpoint = ('<trkpt lat = {} lon = {}>\n'
' <ele>{}</ele>\n'
' <time>{}</time>\n'
' <src>GPSD tag ="{}"</src>\n'
' <fix>{}</fix >\n'
' <sat>{}</sat>\n'
' <hdop>{}</hdop>\n'
' <vdop>{}</vdop>\n'
' <pdop>{}</pdop>\n'
'</trkpt>\n').format(latitude, longitude, altitude, time, tag, mode, sats[1], hdop, vdop, pdop)
addendum = open(the_log, 'a')
addendum.write(trackpoint)
addendum.close()
except Exception as error:
print('Danger-Danger',error)
You run into the problem of a daemonised gpxlogger requiring a -f flag for a file name and that will overwrite the file. This you know.
I see there are two options. Not run gpxlogger as a daemon
gpxlogger >> /home/pi/Desktop/EPQ/temp_gps/gpslog
or run it as a daemon and cat the file to an append-able file
gpxlogger -d -f /home/pi/Desktop/EPQ/temp_gps/gpslog & cat /home/pi/Desktop/EPQ/temp_gps/gpslog >> /home/pi/Desktop/EPQ/temp_gps/gpslog_concatenated
Another way to look at it would be to create sequential logs, and then concatenate them with gpsbable, but in order to do that you need to have a script, and an index.
Make the index echo 0 > ~/.gpxfilecount
Open a favourite editor and create a file including something like:
#! /usr/bin/bash
COUNT=`cat ~/.gpxfilecount`
echo $(($COUNT + 1 )) > ~/.gpxfilecount
filename="gpxlogfile${COUNT}.gpx"
exec gpxlogger -d -f $filename
Mark the script executable chmod +x ~/bin/gpxer.sh (or favourite name).
Every time you fire up the gpxlogger an incremented filename is created. Those file can then be concatenated without tears by gpsbable gpsbabel -i geo -f gpxlogfile1.gpx -f gpxlogfile2.gpx -f gpxlogfile3.gpx -o gpx -F biglogcat.gpx ...or however gpsbable works.
I was curious what building a gpx file from scratch would look like using only minidom. Unfortunately life intervened, sorry for the delay...
When this script (below) gpex3.py, still a little crude and inefficient (read/write every second), is placed in the same directory as gps3.py it creates an appendable gpx file at /tmp/gpx3.gpx
#! /usr/bin/python3
# coding=utf-8
"""banana"""
import xml.dom.minidom
import gps3
import time
from datetime import datetime, timezone, timedelta
import os
import sys
gps_connection = gps3.GPSDSocket()
gps_fix = gps3.Fix()
the_log = '/tmp/gpx3.gpx'
def start_time():
"""time in the beginning"""
timestart = str(datetime.utcnow().replace(tzinfo=(timezone(timedelta(0)))))
return timestart
def close(doc):
"""write file to disk and close"""
log_write = open(the_log, "w")
doc.writexml(log_write)
log_write.close()
if os.path.isfile(the_log):
doc = xml.dom.minidom.parse(the_log) # opens the pre-existing
gpx_element = doc.firstChild
else:
doc = xml.dom.minidom.Document()
gpx_element = doc.createElement("gpx")
doc.appendChild(gpx_element)
trk_element = doc.createElement("trkseg")
trk_element.setAttribute("began", start_time())
gpx_element.appendChild(trk_element)
utc = alt = hdop = vdop = pdop = mode = sats = tag = 'n/a'
try:
tpv_list = {'time': utc, 'ele': alt, 'tag': tag}
sky_list = {'hdop': hdop, 'vdop': vdop, 'pdop': pdop}
# misc_list = {'sat': sats, 'fix':mode} # just an account
element = {}
x = 1 # for the 'is it working?'
for new_data in gps_connection:
if new_data:
gps_fix.refresh(new_data)
if not isinstance(gps_fix.TPV['lat'], str):
trkpt_element = doc.createElement("trkpt")
trk_element.appendChild(trkpt_element)
trkpt_element.setAttribute('lat', str(gps_fix.TPV['lat']))
trkpt_element.setAttribute('lon', str(gps_fix.TPV['lon']))
# tpv_list[key]
for key in tpv_list:
if key == 'ele':
element[key] = '{}'.format(gps_fix.TPV['alt']) # because consistency with labels is a horrible.
else:
element[key] = '{}'.format(gps_fix.TPV[key])
# sky_list[key]
for key in sky_list:
element[key] = '{}'.format(gps_fix.SKY[key])
# Misc.
element['sat'] = '{}'.format(gps_fix.satellites_used()[1])
element['fix'] = '{}'.format(("ZERO", "NO_FIX", "2D", "3D")[gps_fix.TPV['mode']])
for key in element:
trkpt_data = doc.createElement(key)
trkpt_element.appendChild(trkpt_data)
new_value = doc.createTextNode(element[key])
trkpt_data.appendChild(new_value)
# print(doc.toprettyxml())
close(doc) # write to file with every trackpoint
print('Cycle', x) # Only an "is it working?"
x += 1
time.sleep(1)
except KeyboardInterrupt:
gps_connection.close()
print("\nTerminated by user\nGood Bye.\n")
if __name__ == '__main__':
pass

Categories