Problems with django api on second request - python

I am working on a flutter app for creating schedules for teachers. I created a Django project to generate a schedule based on the data in the post request from the user. This post request is sent from the flutter app. The Django project doesn't use a database or anything, It simply receives the input data, creates the schedule and returns the output data back to the user.
The problem is that the process of creating the schedule only works 1 time after starting the Django server. So when I want another user to send a request and receive a schedule I have to restart the server... Maybe the server remembers part of the data from the previous request?? I don't know. Is there somekind of way to make it forget everything after a request is done?
When I try to repeatedly run the scheduler without being in a Django project it works flawlessly. The Scheduler is based on the Google cp_model sat solver. (from ortools.sat.python import cp_model). The error I get when running the scheduler the second time in a Django project is 'TypeError: LonelyDuoLearner_is_present_Ss9QV7qFVvXBzTe3R6lmHkMBEWn1_0 is not a boolean variable'.
Is there some kind of way to fix this or mimic the effect of restarting the server?
The django view looks like this:
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .scheduler.planning import Planning
from .scheduler.planning import print_json
import json
# Convert the data and creates the schedule.
#csrf_exempt
def generate_schedule(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
planning = Planning()
planning.from_json(data)
output_json = planning.output_to_json()
print_json(output_json)
response = json.dumps(output_json)
except Exception as e:
print(e)
print("The data provided doesn't have the right structure.")
response = json.dumps([{'Error': "The data provided doesn't have the right structure."}])
else:
response = json.dumps([{'Error': 'Nothing to see here, please leave.'}])
return HttpResponse(response, content_type='text/json')

There is no beautiful way to restart the server (aside from just killing it by force, which is hardly beautiful).
You're probably using some global state somewhere in the code you're not showing, and it gets screwed up.
You should fix that instead, or if you can't do so, run the solving in a subprocess (using e.g. subprocess.check_call(), or multiprocessing.Process()).

The CP-SAT solver is stateless. The only persistent/shared object is the Ctrl-C handler, which can be disabled with a sat parameter. (catch_sigint if my memory is correct).

Related

Flask POST Request to start an asynchronous process/task (or use websockets)

I have a Flask endpoint that starts an automated email campaign to a list of contacts I have. The body of the POST request has a "emailAmount" key which tells the process how many emails to send out.
Essentially what I want it to do is start the process of sending these emails out, and after each email sent out, show live feedback on the frontend like a loading bar, or just a live count of how many emails have been sent since the process started, and then some sort of live feedback on the front end telling the user when the process has completed. The email process is abstracted to a class I built called "AutomateEmailClient".
from flask import Blueprint
from flask_login import login_required, current_user
from forms.startProcess import ProcessForm
from ..emails.client import AutomateEmailClient
email_router = Blueprint("email",__name__)
#email_router.route('/start', methods=['POST'])
#login__required()
def start_process():
form = ProcessForm() #Validate body of req with WTForms
if form.validate_on_submit():
#Interact with AutomateEmailClient object from different package a few levels up
return #? Return synchronously after email process has finished? or asynchronously and use websockets for live feedback to frontend?
However, after I return from the start_process() function, obviously the request/response cycle is dead, and if I just run the email process and have the return synchronously after, I would basically just have a hanging response for several minutes.
I'm assuming I need to use websockets, and possibly some sort of asynchronous functionality or multi processing, but I'm not sure what is the best approach or how to implement it.

How to wait in django until a request for endpoint arrives?

Tell me with what you can wait for a response to another endpoint?
I am on the main page (index), entering something into the form. The POST request is sent to another server. At this moment:
another server processes the data and, depending on their correctness, makes a POST request to my url /answer (True or False).
I will be redirected, for example, to another page.
How to register the logic of another page (another) so that Django waits for a POST request from another server to /answer and depending on this request True/False, I output everything OK or everything Bad on this page?
url.py
urlpatterns = [
path('index/', index, name='index'),
path('page_2/', page_2, name='page_2'),
path('answer/', answer, name='answer'),
]
-------------------------------------------------
views.py
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
def page_2(request):
# wait request in answer
if request.session['answer'] is True:
return 'Ok'
retunr 'Bad'
def answer(request):
data = request.data
# send to page_2 or save in request.session['answer']
return Response(status=200)
I reckon it's a strange situation and it's better if you could redesign the logic of your code so that the view functions process the request ASAP and not busily wait for external events to be triggered as it increases response time.
However, in order to achieve this purpose we need a communication channel between index and answer view. So to implement a communication like this:
index: Hey answer! I've sent the request. I'm going to sleep, wake me up if you got its result.
answer: Oh I got it man. Here you are. Wake up!
index: Thanks. Now I process it and return my response.
So this channel might be anything! A model in database, some entities in redis, some files in filesystem, etc.
One possible solution using the models might be:
Create a model(name it ExampleRequest for example) consisting of a boolean field named received
In index view, create an instance of ExampleRequest with received = False before sending the request.
In answer view, find the previously created ExampleRequest and set its received field to True
In index view, after sending the request, in a while loop, query the database and check if the created ExampleRequest instance has received = True? If yes, then the external server has called answer. So break the while and do the rest of the work; otherwise, just time.sleep(1) and continue the while loop.
Just note:
When multiple clients are using your website, some of them might request index view and then there will be more than one instance of ExampleRequest. In answer view, you have to be able to find out the current request is related to which one of those instances. You might need to store a unique data related to that request in ExampleRequest model.
You might consider the situation where the other server doesn't call answer view ever. So there might be an upper bound for the iterations of index view's while loop.
Also you may remove ExampleRequest instances after capturing them in index view in order to optimize disk usage of your database.
I say it again, it's better if you can do the polling stuff in frontend instead of backend to avoid high response time and other syncing issues.
This might not the complete answer, but it gives you way.
def index(request):
requests.post(example.com, data='My data')
return redirect('page_2')
Change it to following
import httpx
async def index(request):
async with httpx.AsyncClient() as client:
response = await client.post(example.com, data='My data')
print(response.json())

Publish a script online

I have a Python script written up and the output of this script is a list.
Right now I need to get it online and make it accessible to others. I looked at Django , but then I realized that it may be kind of hard to create the UI. Is there any simple way to create a UI in Django and map it to an existing Python script.
Right now I using nltk, numpy, sqlite3 and things like that. Or is there a simpler way by which I can proceed?
In your case, Django is redundant.
You can use something smaller, Flask or maybe Aiohttp.
For example, all you need in aiohttp:
basic hmtl template
handler for one url (here you will call your script)
aiohttp webserver
The main idea:
Your server catch some url (for example /),
start your script, receives result,
respond with your html template (also render script result in it).
You can try creating a flask App.
Just do a pip install Flask and try the code below
from flask import Flask
import flask
import json
from flask import Response
app = Flask(__name__)
#app.route('/test',methods=['GET'])
def test():
'''
GET: Receives the request in /test route and returns a response containing {"response": [1,2,3]}
'''
my_list = [1,2,3]
resp = Response(response=json.dumps({"response": my_list}), status=200, mimetype='application/json')
return resp
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8082)
Then to test your app from your browser accessing
localhost:8082/test
or also through some app like postman
I would suggest looking into something like React for creating the UI. This way your UI would only make calls to your Flask server.

Flask JSON request is None

I'm working on my first Flask app (version 0.10.1), and also my first Python (version 3.5) app. One of its pieces needs to work like this:
Submit a form
Run a Celery task (which makes some third-party API calls)
When the Celery task's API calls complete, send a JSON post to another URL in the app
Get that JSON data and update a database record with it
Here's the relevant part of the Celery task:
if not response['errors']: # response comes from the Salesforce API call
# do something to notify that the task was finished successfully
message = {'flask_id' : flask_id, 'sf_id' : response['id']}
message = json.dumps(message)
print('call endpoint now and update it')
res = requests.post('http://0.0.0.0:5000/transaction_result/', json=message)
And here's the endpoint it calls:
#app.route('/transaction_result/', methods=['POST'])
def transaction_result():
result = jsonify(request.get_json(force=True))
print(result.flask_id)
return result.flask_id
So far I'm just trying to get the data and print the ID, and I'll worry about the database after that.
The error I get though is this: requests.exceptions.ConnectionError: None: Max retries exceeded with url: /transaction_result/ (Caused by None)
My reading indicates that my data might not be coming over as JSON, hence the Force=True on the result, but even this doesn't seem to work. I've also tried doing the same request in CocoaRestClient, with a Content-Type header of application/json, and I get the same result.
Because both of these attempts break, I can't tell if my issue is in the request or in the attempt to parse the response.
First of all request.get_json(force=True) returns an object (or None if silent=True). jsonify converts objects to JSON strings. You're trying to access str_val.flask_id. It's impossible. However, even after removing redundant jsonify call, you'll have to change result.flask_id to result['flask_id'].
So, eventually the code should look like this:
#app.route('/transaction_result/', methods=['POST'])
def transaction_result():
result = request.get_json()
return result['flask_id']
And you are absolutely right when you're using REST client to test the route. It crucially simplifies testing process by reducing involved parts. One well-known problem during sending requests from a flask app to the same app is running this app under development server with only one thread. In such case a request will always be blocked by an internal request because the current thread is serving the outermost request and cannot handle the internal one. However, since you are sending a request from the Celery task, it's not likely your scenario.
UPD: Finally, the last one reason was an IP address 0.0.0.0. Changing it to the real one solved the problem.

Using web.webopenid with web.py to authenticate users

I have a web.py app I'm running through mod_wsgi locally (http://localhost/...). I've gotten to the point of adding authentication to my app and wanted to use web.py's builtin module. I started with a brief example found here: http://log.liminastudio.com/programming/howto-use-openid-with-web-py
import web, web.webopenid
urls = (
r'/openid', 'web.webopenid.host',
r'/', 'Index'
)
app = web.application(urls, globals())
class Index:
def GET(self):
body = '''
<html><head><title>Web.py OpenID Test</title></head>
<body>
%s
</body>
</html>
''' % (web.webopenid.form('/openid'))
return body
if __name__ == "__main__": app.run()
This works well enough running in the terminal and going to http://localhost:8080/. Another example http://c-farrell.blogspot.com/2010/11/usrbinenv-pythonimport-webfrom-web.html does a similar technique but makes more sense to me.
#!/usr/bin/env python
import web
from web import webopenid
urls = (
'/', 'index',
'/openid', 'webopenid.host',
)
... more code ...
class index:
def GET(self):
oid = webopenid.status()
if not oid:
return 'please log in: ' + \
webopenid.form('/openid')
else:
return 'you are logged in as:' + \
webopenid.form('/openid')
Here's where I get a little lost. From what I can tell, the argument passed to form is the return URL after signing in. For example, if I put 'http://www.yahoo.com/' it will take me there after every login attempt. I feel like this should point back to my own controller and just check there, but the convention seems to be to use the web.webopenid.host controller, which I guess handles the id and returns to the base '/' url. I think I'm getting there, but the status returned is always None.
From what I gather then, this is either a code issue, or there's something in my apache configuration that is keeping the authentication from working. In web.webopenid, the library creates a .openid_secret_key file in the same directory as the web server. When I run the example code, this gets created. When I run my code through apache, it does not (at least not in the cgi-bin. Somewhere else?) Anyway, if this file isn't being generated or being regenerated every time, it will keep me from logging in. I believe it's an apache issue as I tried running my app through the web.py webserver and I did get the file created and I can authenticate. All I can conclude is this file isn't being written and every subsequent query tries a new file and I can never authentication. Can any apache/mod_wsgi gurus explain to me where this file is being written or if this is the actual problem?
Most likely obvious causes for this were given in answer to same question on mod_wsgi list. See:
https://groups.google.com/d/msg/modwsgi/iL65jNeY5jA/KgEq33E8548J
It is probably a combination of the first two, current working directory and Apache user access rights.

Categories