How to stop a Python subprocess using a HTML form button? - python

I'm using Django as the web framework. On a single HTML page, I have a form whose purpose is to start a Python subprocess when I click on its submit button. The fields of the form will be filled with parameters which will be passed to this subprocess.
Now, I want to be able to stop the same subprocess using another form button, preferably within the same form. How can I do this? It's probably not that difficult, but I don't know at the moment how to accomplish that in a simple but elegant way. Any ideas? Thank you very much!

You have to keep a reference to the process in the session. Give each submit button a different "name" attribute, and if this signals stop, kill the subprocess.
from subprocess import Popen
if 'start' in request.POST:
p = Popen("do something")
request.session['myprocess'] = p
elif 'stop' in request.POST:
request.session['myprocess'].kill()
Form:
<form method="POST">
<input type="submit" name="start" value="start"/>
<input type="submit" name="stop" value="stop"/>
</form>
Whether this works depends on your session-storage backend though.

Related

How to pause and stop celery task from django template

I am working on a django application that uses celery to run tasks asynchronously. Right now a user can submit a form from the webpage to start a celery task. But there is no way to pause or stop the task on the click of a button inside a django template.
this is my code so far
celery task
#shared_task
def get_website(website):
website_list = return_website_list(website)
return website_list
In the above task I am calling a return_website_list() function that scrapes the required website and returns a list of links from the website.
output.html template
<div class="container">
<button class="pause_btn" type="button">Pause task</button>
<button class="resume_btn" type="button">Resume task</button>
<button class="stop_btn" type="button">Stop task</button>
</div>
I want the ability to pause the task indefinitely when the pause button is clicked and resume the task when the resume button is clicked or the ability to stop the task completely when the stop button is clicked.
views.py
def index(request):
if request.method == 'POST':
website = request.POST.get('website-name')
get_website.delay(website)
return redirect('output')
return render(request, 'index.html')
I searched online, like these link1, link2, link3. But these links did not help me achieve what I am trying to do.
Thanks in advance
my idea is a field named state and state will set from pause button click. and in celery task in each action check state and if it is in paused state wait for 1 second and check again. i used this idea previously for cancel action

Submit form that uses Intercooler in Django StaticLiveServerTestCase

I am trying to write a selenium test for the submission of a form, which uses intercooler.js when it is submitted. The main problem I am having, is that when I navigate to the page, the form has class="disabled", which is not expected behaviour, and I can't submit the form. The relevant part from the intercooler docs says:
By default, intercooler will apply the disabled class to the element
that triggers an intercooler request. This can be used to give a
visual hint to the user that they should not click or otherwise
trigger the request again, and is Bootstrap-friendly.
However, it seems to me that the disabled class is being added to the form element before I actually submit the form, and as I understand it should only be added after a request is in-flight.
The form currently looks like this:
<form ic-post-to="/dashboard/calculate/2/exports/" ic-select-from-response="#content" ic-target="#content" method="post" ic-src="/dashboard/calculate/2/exports/" ic-verb="POST" ic-trigger-on="default" ic-deps="ignore" class="disabled">
<input type="hidden" name="csrfmiddlewaretoken" value="...">
<input type="submit" name="new" value="New" class="btn btn-primary float-right ml-1" id="submit-id-new">
</form>
I have tried adding explicit and implicit waits so that the entire page will load but the problem is still there.
Any help with this would be much appreciated.
Try to use Javascript to click on the button:
submit = driver.find_element_by_id("submit-id-new")
driver.execute_script("arguments[0].click();", submit)
It turns out that I was waiting in the wrong place. Instead of waiting when the page loads, I should have been waiting after the form was submitted to allow the page content to be updated.

Button click in HTML to update SQLAlchemy db in Flask

I'm trying to update a database value called ''favorites'' for the logged in user of a Flask web app using a button click. Essentially, the favorites column is a single string that looks like this: Apples, Bananas, Oranges where on the button click, I would want to append a value (say Cherries) by breaking apart the string into a list in my #app.routes(), appending the value, and rejoining it back into a string before committing the changes. I'm not sure what the proper way is to do this, here's what I have:
HTML snippet
<button action="{{ url_for('add') }}" method="post" type="submit">Favorite</button>
#app.routes()
#app.route('/add', methods=['POST'])
def add():
star_faves = current_user.favorites
star_faves_list = star_faves.split(', ')
star_faves_list.append('Cherries')
', '.join(star_faves_list)
current_user.favorites = star_faves_list
db.session.commit()
return render_template('add.html')
The problem is that I don't really understand how the HTML is communicating with Python/Jinja, if anybody can help clear that up I would greatly appreciate it.
It looks like you have some elements confused.
If you want to submit a POST request to the /add page, the easiest way is to create a form. (Buttons do not have an action or method attribute, forms do.) When you create the form, you also specify the HTTP method to use when submitting the form. So in your case, it should look something like this:
<form action="{{ url_for('add') }}" method="POST">
<input type="submit" value="Favorite">
</form>
You can use a button instead of an input with type submit, they are interchangeable.
If you don't want the page to reload while submitting the request, a more advanced technique you can use with JavaScript is something called AJAX.
This example code sends the same POST request to the /add page:
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
// this method gets called if the state of the request changes (it succeeds of fails)
// you will probably want to update your page accordingly here
};
request.open('POST', '/add');
request.send();

Create a Flask Search Bar that Inserts URI Variable with url_for()

I have a Flask site that has a 'search bar' where you type in the location ID of a particular location and then click Submit to be taken to the page for that location, if it exists. Here's the current form action:
<form id="locationinfo" action="{{ url_for('location') }}">
When you click Submit you are taken to /location?info=SITEID and that works just fine. What I want to do is change this behavior slightly so that when a user clicks Submit they are taken to /location/SITEID/ instead. I have the decorator set up in my main Flask routes file, but I'm struggling to put the pieces together to get this simple form together.
#app.route("/location/<locationid>/")
def locations(locationid):
...
return locationid
Any direction would be greatly appreciated!
[Edit with current full form code]
#app.route("/location")
def location():
location_id = request.args.get("info")
<form id="info" action="{{ url_for('location') }}">
<input type="text" name="info" id="locationfield">
<button type="submit">Go!</button>
</form>
You can't change how HTML forms submit their fields, they will always be in the query string or body (POST). One option is to use JavaScript to override the submit event to perform your own submit and re-render with the results.
A simpler solution is to redirect to the nice url after submit. This keeps the "search" action separate from the "show" action (even if they are handled by the same view).
#app.route('/location/')
#app.route('/location/<int:id>/')
def location(id=None):
# redirect to the second form if the id wasn't in the path
# raises 400 error if id wasn't in the query
if id is None:
return redirect(url_for('location', id=request.args['info']))
# id is in the path, continue
...
You can expand this later if you want to search by something besides id. Perform the search then redirect to the found id.

How to Distinguish Button Presses in Tornado?

In the process of teaching myself how to use python's tornado web framework, I am trying to create a simple web server and some web pages. On one of the web pages, I have two buttons: one to log users out and redirect them back to the login page and one to submit a blog post. They are both "post" requests and have their name values in html set to "logout" and "new_post".
My questions is, how can I tell which button was pressed so that the post() method for the page's RequestHandler can perform the correct actions in each case? Is there a way to grab the "name" of the button pressed?
When you submit a form with a button click, a parameter with the name of the clicked button gets added to the request.
You can check, if the parameter exists and then do your stuff.
def post(self):
if self.get_argument("logout", None) != None:
# do logout stuff
if self.get_argument("new_post", None) != None:
# do submit a blog post stuff
On Python3 using Tornado, the setup looks as follows with two submit buttons (which is very similar to the above answer).
First the HTML and note both buttons are similar with differing display values. The name parameter is what will be passed to Tornado's POST handler:
<p><input type="submit" class="button" name="basic" value="Basic Query"></p>
<p><input type="submit" class="button" name="advanced" value="Advanced Query"></p>
Next, the POST handler. Using get_argument, you can specify the name and select it if the input is not null. Remember to include the ", None" in get_argument so that it defaults to None should the option not be selected:
def post(self):
if self.get_argument('basic', None) is not None:
self.write('Basic Query')
elif self.get_argument('advanced', None) is not None:
self.write('Advanced Query')
That's it! 2 forms and the ability to differentiate between. Happy coding!

Categories

Resources