I am using python-docx to save a word file and I have set a specific path but since path aren't the same for every user I would like to have a dialogue when the user clicks on download so he can choose exactly in which local repository to save the file.
What I have now:
#app.route('/download_file')
def save_doc():
filename = 'test.docx'
filepath = os.path.join(r'C:\Users\Joe\Desktop', filename)
document.save(filepath)
return 'meh'
Implementing the logic you described requires work on the front-end. Let's simplify the problem by assuming that the user manually types in the download target directory. (In practice, it would make more sense for there to be a pop-up window allowing the user to browse the directory on a file explorer.)
<form action="/download" method="post">
<input type="text" value="" name="dir">
<input type="submit" value="Download">
</form>
Then, in Flask, you might specify the following:
from flask import Flask, request
#app.route('/download', methods=['GET', 'POST'])
def save_doc():
if request.method=='POST':
download_dir = request.form['dir'] # Access user input from backend
filename = 'test.docx'
filepath = os.path.join(download_dir, filename)
document.save(filepath)
return 'Download complete'
else:
pass # Whatever implementation you want for 'GET' method
This is incomplete code that I wrote without knowledge of the structure of your project, and so may not work if it is simply copied and pasted into your source code. The implementation is also quite basic, so consider it a baseline model to help you get started on baking the user interactive dialogue system.
I'd be happy to answer any questions you might have.
Related
I am creating a flask web application which asks users to complete a quiz through a form and collect data from each session. I use a main.py file, 1 html file with the quiz, and then another one which is supposed to display dynamic results. When the submit button is clicked, it automatically directs users to the second html page with their results. It is my intention to display results based upon what options the user clicks. I attempted to accomplish this by:
Collecting data:
session["symptom1"] = form.symptom1.data
Writing this into a file
user_data.write(form.symptom1.data)
Reading file contents into a variable (3 characters into each variable)
data1 = user_data.read(3)
Assigning a Boolean value to a python variable based upon the data using a conditional:
if data1 == "op3" or data1 == "op4":
global adhd1
adhd1 = True
Rendering this to an html file:
return render_template("takethequiz.html", form = form,adhd1=adhd1,adhd2=adhd2,adhd3=adhd3,adhd4=adhd4)
Creating a conditional in a second html file (treatmentplan.html)=
{% if adhd1 == True %}
Use productivity apps to increase your focus
{% else %}
Great job! You are skilled at staying on task!
{% endif %}
However this does not do anything. Meaning, no matter what I click, the treatment plan is always set to adhd1 = False. How do I combat this?
Code Screenshots on Google Doc: https://docs.google.com/document/d/1uF1Q-zfKNx-d5jzbaIK_BKGbLRm0tCGgaz92-V2hDjE/edit?usp=sharing
Thank you!
I have a site using GCP python API, which is quite slow at making pull requests. So I have cached the area of the template using this data, and in the view I check if the cache is still active before making any more requests (not a copy n pasted code so if theres any typos ignore em :) )
def gcpProjectsView(request):
gcpprojects = None
cached_time = None
if cache.get(make_template_fragment_key('gcp')) is None:
cached_time=timezone.now()
gcpprojects = get_gcp_projects()
return render (request , 'gcp/gcpprojects.html', {'gcpprojects':gcpprojects,'last_update':cache_time})
To manually update the data, I have a refresh button that points to this view:
def refresh_gcp(request):
cache.delete(make_template_fragment_key('gcp'))
return redirect('gcpProjectsView')
The problem is that if the user clicks the refresh button 5 times the view makes 5 GCP Api calls, it needs to only do 1. How do I resolve this issue?
My suggestion would be to use a simple form with a button. Buttons can be disabled using simple javascript when clicked.
Because you're redirecting back to the same template in the view the button should be re-enabled again once it has 'refreshed'.
In this example snippet replace the action with whatever url routes to refresh_gcp.
<form method="GET" action="<url that points to refresh_gcp>">
<button type="submit" onclick="this.disabled = true;">Refresh</button>
</form>
This was the simplest solution I could think of that didn't involve implementing some sort of token validation for requests.
Use django session :
def refresh_gcp(request):
clicked = request.session.get("click")
if clicked:
return render(request,"your_html",{"disable":True})
# Set Button disable or not in your html by using context...
else:
request.session["click"]="user_click"
cache.delete(make_template_fragment_key('gcp'))
return render(request,"your_html",{"disable":False})
I have a flask view function as below:
#app.route('/myfunc', methods = ['POST', 'GET'])
def myfunc():
var = request.form["samplename"]
selected_ecg=ecg.loc[ecg['Patient ID'].isin([var])]
selected_ecg = selected_ecg.drop('Patient ID', 1)
arr = np.array(selected_ecg)
y = arr.T
x=np.array(range(1,189))
plot.plot(x,y)
#Remove the old file
os.remove("static\graph.png")
#Now save the new image file
plot.savefig("static\graph.png")
return render_template("outputs.html")
Outputs.html:
<html>
<head>
</head>
<body>
<h1>Output page</h1>
<img src="static/graph.png" />
</body>
</html>
I use the flask view function to display an image through the outputs.html file. The catch here is that the static image file that is served keeps changing every time based on user inputs. In other words, I keep overwriting the image file based on the inputs the user has selected.
But the problem is that the changing image file is not served. The old image file that was used for first time render is only displayed for every new input of the user.
I have already referred to old posts regarding serving dynamic content in flask. But none of them served useful.
thebjorn's solution is valid. I have found multiple posts on Stack Overflow which suggest identical solutions. To view them, search for how to not cache images on Google. link link2 link3
Below is my solution to your problem. This will delete graph file and create new one with plot.savefig on every GET request to /myfunc. I was not sure on which request you wanted this behavior.
#app.route('/myfunc', methods = ['POST', 'GET'])
def myfunc():
var = request.form["samplename"]
selected_ecg=ecg.loc[ecg['Patient ID'].isin([var])]
selected_ecg = selected_ecg.drop('Patient ID', 1)
arr = np.array(selected_ecg)
y = arr.T
x=np.array(range(1,189))
plot.plot(x,y)
new_graph_name = "graph" + str(time.time()) + ".png"
for filename in os.listdir('static/'):
if filename.startswith('graph_'): # not to remove other images
os.remove('static/' + filename)
plot.savefig('static/' + new_graph_name)
return render_template("outputs.html", graph=new_graph_name)
Outputs.html
<html>
<head>
</head>
<body>
<h1>Output page</h1>
<img src="{{ url_for('static', filename=graph) }}" />
</body>
</html>
You're running into a caching issue. Static resources, like images, are cached at every point in the chain between your server and the browser. This is a good thing. Most reasonable systems are set up to cache images for at least 1 year at the server (and that's if they're not cached in the browser).
To bust through this cache issue, you'll need to either (i) give the files new names, (ii) reconfigure Vary headers to indicate they shouldn't be cached, or (iii) add a uniqueness fragment -- e.g. instead of using static/graph.png, add a timestamp 'static/graph.png?v=' + (new Date()).valueOf() or a md5 hash.
update: Dinko has given you a fine answer (do read the links he provides). To add cache-busting on the server side, without creating new files, you can calculate an md5 checksum (disadvantage: you'll need to read the entire file):
from hashlib import md5
fname = 'static/graph.png'
with open(fname, 'rb') as fp:
checksum = md5.new(fp.read()).hexdigest()
fname += "?v" + checksum
or use the last-modified attribute (not always reliable):
from hashlib import md5
fname = 'static/graph.png'
modified_tstamp = str(int(os.stat(fname).st_mtime * 10**6))
fname += "?v" + checksum
both of these methods will serve a cached version as long as the file doesn't change.
I have tried to get the autoprefixer filter to work with flask_assets by following the instructions in the Flask_Assets documentation, but it does not appear to apply the filter. Here is my code:
# construct flask app object
from flask import Flask, render_template_string
flask_args = { 'import_name': __name__ }
flask_app = Flask(**flask_args)
from flask_assets import Environment, Bundle
assets = Environment(flask_app)
assets.config['AUTOPREFIXER_BIN'] = 'postcss'
assets.config['AUTOPREFIXER_BROWSERS'] = [ '> 1%' ]
css_min = Bundle('../styles/mycss.css', filters='autoprefixer', output='styles/test.css')
assets.register('css_assets', css_min)
#flask_app.route('/')
def landing_page():
html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\
<head>{% assets "css_assets" %}\
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">\
{% endassets %}\
<title>Hello</title>\
</head>\
<h1>Hello World</h1>\
<p>Just a test of flask</p>'
return render_template_string(html), 200
if __name__ == '__main__':
flask_app.run(host='0.0.0.0', port=5000)
I have been able to apply the cssmin, pyscss, uglifyjs and jsmin filters successfully. I can also run autoprefixer on the command line to successfully compile a transformed output:
postcss --use autoprefixer --autoprefixer.browsers "> 1%" -o test.css mycss.css
However, when trying to run autoprefixer through flask_assets registration, the process neither throws an error nor does it seem to take the required time to compile. It does produce the output file but when I examine the resulting file, none of the prefixes have been applied.
UPDATE: This problem seems to occur whenever attempting to configure options for ANY filter. I have not been able to get uglifyjs to accept 'UGLIFYJS_EXTRA_ARGS' or for the pyscss filter to adopt a new style using 'PYSCSS_STYLE' either. I have tried to set these configuration as environmental variables using os.environ['AUTOPREFIXER_BIN'] as well as attempting to pass them through flask.config['AUTOPREFIXER_BIN']. But none of the configuration settings have been applied when the filter is run. It is also not clear to me where in the code itself the configuration options are constructed by either Bundle or Environment.
One SO post claims to have found a way to get a configuration setting to work, but the post does not show the entire workflow of how flask_assets needs to be setup to ingest these options.
Perhaps someone can help me understand what I am doing wrong?
Autoprefixer:
There is nothing wrong with your code1. You are just not using the correct filter for the latest version of Autoprefixer. If you look at the history of the releases in that link, since version 6.0.0, it started using postcss. Your code will work for versions older than 6.0.0.
Webassets has provided support for versions after 6.0.0 (inclusive), by providing the autoprefixer6 filter.
Therefore all you have to do is change the filter(s) while initializing your bundle, like so:
css_min = Bundle('../styles/mycss.css', filters='autoprefixer6', output='styles/test.css')
Other Filters' Configurations:
Don't use os.environ, that is not the way to set configuration variables for Flask and flask-extensions. The most common (and preferred) way to set configuration for extensions is by using the flask Config itself, and in large projects this is done using a separate config file. The extensions will pickup its configuration options from flask's config.
Depending on which extension you use, you can also set the config separately like you have done, but that is rarely used, from what I have seen so far.
Please check the Flask's Configuration related documentation for some good examples on how to setup configuration for your app "properly".
from flask import Flask, render_template_string
from flask_assets import Environment, Bundle
# construct flask app object
flask_args = {'import_name': __name__}
flask_app = Flask(**flask_args)
assets = Environment(flask_app)
# specify the bin path (optional), required only if not globally installed
assets.config['AUTOPREFIXER_BIN'] = 'path/to/postcss'
assets.config['AUTOPREFIXER_BROWSERS'] = ['> 1%', ]
# use the autoprefixer6 updated filter
css_min = Bundle('../styles/mycss.css', filters='autoprefixer6',
output='styles/test.css')
assets.register('css_assets', css_min)
#flask_app.route('/')
def landing_page():
html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\
<head>{% assets "css_assets" %}\
<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">\
{% endassets %}\
<title>Hello</title>\
</head>\
<h1>Hello World</h1>\
<p>Just a test of flask</p>'
return render_template_string(html), 200
if __name__ == '__main__':
flask_app.run(host='0.0.0.0', port=5000)
Remember to clean out the previously generated files, if the source css/js has not changed, i.e remove the output files, and the .webassets-cache folder.
1Except for code style & formatting conventions!
I register URL with callback function like this url(r'^G2S/TestList$', testList)
and this is callback function
def testList(request):
fi = open('testlist.html', 'r')
html = fi.read()
fi.close();
#html = '<html><body> <FORM ACTION="/G2S/HostReceiver" METHOD="POST"> First name: <INPUT TYPE="TEXT" NAME="firstName" VALUE="J. Random"><BR> Last name: <INPUT TYPE="TEXT" NAME="lastName" VALUE="Hacker"><P> <INPUT TYPE="SUBMIT"></FORM> </body></html>'
return HttpResponse(html)
the problem i have suffered is when calling open('testlist.html', 'r') in the callback function(if i use eclipse dejango run)
django is saying Exception Value : [Errno 2] No such file or directory: 'testlist.html'
but on the contrary, if i use command line with python manage.py runserver
it work normally with no the error.
the project hierarchy is like below
project folder
|
src folder
|
manage.py
testlist.html
why it only makes the error with eclipse
and one more thing, why does eclipse break point on debug mode seem not working?
for example i check a break point at the callback function above(testList), and enter to
http://127.0.0.1:8000/G2S/TestList on debug mode, i think eclipse should stop at the point i check, though error occur, but does't, why?
Read about how to use templates in your projects. Using file operations like that is not recommended.
Add this to your settings.py file
TEMPLATE_DIRS = (
os.path.join(PROJECT_ROOT, '<app>/templates'))
Then add your html files to the project/<app>/templates folder.
Then render the templates in your view using
return render(request, 'testlist.html')