Flask redirect page after response is complete - python

So I currently have button in my html script that calls the route /return-files. This route returns a response with a zipped file and I also disabled any caching since I had issue with trying to get a new file each time this response is made. This is working well, but since i cannot return multiple things ( a response and a redirect) I cannot refresh my current page after sending the user a the zip file. Now I have read many solutions such as using javascript to create consecutive responses. I am looking for the simplest technique. Source code bellow thanks for all help.
#app.route('/return-files')
def creareturn_files_tut():
global itemsList
global clientName
try:
if len(itemsList) <= 0:
flash("The itemList was found to contain no items")
else:
taxPercent = 0.13
ziped = FileHandler(clientName, itemsList, taxPercent)
file = ziped.addToZip()
os.remove(ziped.excelFileName)
os.remove(ziped.wordFileName)
# os.remove(file)
itemsList = []
clientName = None
response = make_response(send_file(file, file, as_attachment=True))
# remove cache for the file so that a new file can be sent each time
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
response.headers['Cache-Control'] = 'public, max-age=0'
return response
# return out
except Exception as e:
return str(e)

You can use send_from_directory() or send_file() like this:
file = ziped.addToZip()
os.remove(ziped.excelFileName)
os.remove(ziped.wordFileName)
itemsList = []
clientName = None
return send_from_directory(directory='', filename=file, as_attachment=True, cache_timeout=0)
or
file = ziped.addToZip()
os.remove(ziped.excelFileName)
os.remove(ziped.wordFileName)
itemsList = []
clientName = None
return send_file(file, cache_timeout=0, as_attachment=True)
Note that caching is disabled by setting cache_timeout to 0.

Related

Flask - cookies won't create

so i have two 'cookies' functions outside of routes to make code more organized
def get_cookie():
# ip_address = request.environ['REMOTE_ADDR']
ip_address = "X"
access_token = os.environ.get('ipinfo_key')
handler = ipinfo.getHandler(access_token=access_token)
details = handler.getDetails(ip_address=ip_address)
print(details.all)
resp = make_response(render_template('base.html'))
resp.set_cookie('ip',ip_address)
app.logger.info("Creating cookies...")
print(details.all['city'])
print(details.all['postal'])
print(details.all['latitude'])
print(details.all['longitude'])
resp.set_cookie('city',details.all['city'])
resp.set_cookie('postal',details.all['postal'])
resp.set_cookie('latitude',details.all['latitude'])
resp.set_cookie('longitude',details.all['longitude'])
app.logger.info("Cookies has been created.")
return resp
def check_cookie():
app.logger.info("Checking cookies...")
if 'city' in request.cookies:
app.logger.info("Cookies already exists.")
else:
app.logger.info("No cookie found.Creating new ones...")
return get_cookie()
check_cookie() function check whether user have cookie named city and if don't triggers get_cookie() function
There's my simple main route.
#app.route('/')
def base():
check_cookie()
return render_template('base.html')
If i try to log my location info from API everything is going right but cookies aren't created... Any tips? ;/

Using a variable from a dictionary in a loop to attach to an API call

I'm calling a LinkedIn API with the code below and it does what I want.
However when I use almost identical code inside a loop it returns a type error.
it returns a type error:
File "C:\Users\pchmurzynski\OneDrive - Centiq Ltd\Documents\Python\mergedreqs.py", line 54, in <module>
auth_headers = headers(access_token)
TypeError: 'dict' object is not callable
It has a problem with this line (which again, works fine outside of the loop):
headers = headers(access_token)
I tried changing it to
headers = headers.get(access_token)
or
headers = headers[access_token]
EDIT:
I have also tried this, with the same error:
auth_headers = headers(access_token)
But it didn't help. What am I doing wrong? Why does the dictionary work fine outside of the loop, but not inside of it and what should I do to make it work?
What I am hoping to achieve is to get a list, which I can save as json with share statistics called for each ID from the "shids" list. That can be done with individual requests - one link for one ID,
(f'https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&ugcPosts=List(urn%3Ali%3AugcPost%3A{shid})
or a a request with a list of ids.
(f'https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&ugcPosts=List(urn%3Ali%3AugcPost%3A{shid},urn%3Ali%3AugcPost%3A{shid2},...,urn%3Ali%3AugcPost%3A{shidx})
Updated Code thanks to your comments.
shlink = ("https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&shares=List(urn%3Ali%3Ashare%3A{})")
#loop through the list of share ids and make an api request for each of them
shares = []
token = auth(credentials) # Authenticate the API
headers = fheaders(token) # Make the headers to attach to the API call.
for shid in shids:
#create a request link for each sh id
r = (shlink.format(shid))
#call the api
res = requests.get(r, headers = auth_headers)
share_stats = res.json()
#append the shares list with the responce
shares.append(share_stats["elements"])
works fine outside the loop
Because in the loop, you re-define the variable. Added print statments to show it
from liapiauth import auth, headers # one type
for ...:
...
print(type(headers))
headers = headers(access_token) # now set to another type
print(type(headers))
Lesson learned - don't overrwrite your imports
Some refactors - your auth token isn't changing, so don't put it in the loop; You can use one method for all LinkedIn API queries
from liapiauth import auth, headers
import requests
API_PREFIX = 'https://api.linkedin.com/v2'
SHARES_ENDPOINT_FMT = '/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn%3Ali%3Aorganization%3A77487&shares=List(urn%3Ali%3Ashare%3A{}'
def get_linkedin_response(endpoint, headers):
return requests.get(API_PREFIX + endpoint, headers=headers)
def main(access_token=None):
if access_token is None:
raise ValueError('Access-Token not defined')
auth_headers = headers(access_token)
shares = []
for shid in shids:
endpoint = SHARES_ENDPOINT_FMT.format(shid)
resp = get_linkedin_response(endpoint, auth_headers)
if resp.status_code // 100 == 2:
share_stats = resp.json()
shares.append(share_stats[1])
# TODO: extract your data here
idlist = [el["id"] for el in shares_list["elements"]]
if __name__ == '__main__':
credentials = 'credentials.json'
main(auth(credentials))

How to pass a parameter as string which include special character in flask app route

I want to pass "top3/1" as parameter in url. but it return 404 error.
here is my code:
#APP.route('/get_agent_details_on_voucher_code/<string:code>',methods=['GET'])
def get_agent_details_on_voucher_code(code):
result = []
if code == 'TOP3':
voucher_earning_list = [{'voucher_code':'TOP3/1','agent_id':12345,'voucher_id':8}]
if len(voucher_earning_list)>0:
for item in voucher_earning_list:
get_details = requests.get('http://192.168.1.55:5000/get_agent_details/'+str(item['agent_id']))
get_result = get_details.json()
if len(get_result) > 0:
get_ag_mobileno = get_result[0]['mobile']
result.append({'mobile_no':get_ag_mobileno,'agent_id':item['agent_id'],'voucher_id':item['voucher_id']})
response = jsonify(result)
else:
response = jsonify(result)
else:
response = jsonify(result)
else:
response = jsonify(result)
return response
That's a tricky question: generally with flask you have to specify the routes up to the final endpoint.
If you want to match all the sub routes you can use the type path instead of string, os you just need to change the route into:
#APP.route('/get_agent_details_on_voucher_code/<path:code>',methods=['GET'])
In this way you will obtain all the sub routes of /get_agent_details_on_voucher_code/, keep in mind the code will be a string, you have to extract the information you need parsing it.

FLASK: Serving file to browser behind API proxy

When the user enters http://example2.com:5500/?param=x the code below generates a data.csv file and serves it to the browser. It works perfectly like this.
However, I have deployed it behind an API proxy, so that the user makes a call to http://example1.com/?param=x which is internally transformed into http://example2.com:5500/?param=x.
As a result, instead of serving data.csv to the browser as before, it displays on the browser all the data.csv content. The view source-code feature shows exactly what data.csv should contain, without any HTML headers, just the data.csv content, but it is not being served as attachement. Any ideas?
from flask import make_response
#app.route('/', methods = ['GET'])
def get_file():
alldata = []
while len(new_data) > 0:
new_data = api.timeline(max_id=oldest)
alldata.extend(new_data)
oldest = alldata[-1].id - 1
outdata = ""
for data in alldata:
outdata += ",".join(data) + "\n"
response = make_response(outdata)
response.headers["Content-Disposition"] = "attachment; filename=data.csv"
return response
if __name__ == '__main__':
app.run(host = app.config['HOST'], port = app.config['PORT'])
EDIT: Included mapping code to transform request to example1.com to example2.com (secret_url)
# This is example1.com
#app.route("/api/<projectTitle>/<path:urlSuffix>", methods=['GET'])
def projectTitlePage(projectTitle, urlSuffix):
projectId = databaseFunctions.getTitleProjectId(projectTitle)
projectInfo = databaseFunctions.getProjectInfo(projectId)
redirectionQueryString = re.sub('apikey=[^&]+&?', '', request.query_string).rstrip('&')
redirectionUrl = projectInfo['secretUrl'].rstrip('/')
if urlSuffix is not None:
redirectionUrl += '/' + urlSuffix.rstrip('/')
redirectionUrl += '/?' + redirectionQueryString
redirectionHeaders = request.headers
print request.args.to_dict(flat=False)
try:
r = requests.get(redirectionUrl, data=request.args.to_dict(flat=False), headers=redirectionHeaders)
except Exception, e:
return '/error=Error: bad secret url: ' + projectInfo.get('secretUrl')
return r.text
Your homegrown proxy is not returning headers back to the application. Try this:
#app.route("/api/<projectTitle>/<path:urlSuffix>", methods=['GET'])
def projectTitlePage(projectTitle, urlSuffix):
# ...
return r.text, r.status_code, r.headers

Pyramid rendered JSON Response returned as a string

I'm using the pyramid web framework and writing some functions that receive and respond with JSON.
Here is some code I've written that returns the files and directories from a given directory
#view_config(route_name='file', renderer='json')
def file(request):
values = request.json_body
path = os.path.dirname(__file__) + '/files' + values['path']
if os.path.isdir(path):
files = [ f for f in os.listdir(path) if os.path.isfile(os.path.join(path,f)) ]
dirs = [ f for f in os.listdir(path) if os.path.isdir(os.path.join(path,f)) ]
if files is None:
files = []
if dirs is None:
dirs = []
response = dict()
response['version']='1.0'
response['files']=files
response['directories']=dirs
return response
else:
response = dict()
response['version']='1.0'
response['files']=[]
response['directories']=[]
return response
#view_config(route_name='files', renderer='templates/filesTest.jinja2')
def files(request):
if 'form.submitted' in request.params:
json_payload = json.dumps({'path':request.params['path']})
headers = {'Content-Type':'application/json; charset=utf-8'}
req = urllib2.Request(request.route_url('file'), json_payload, headers)
resp = urllib2.urlopen(req)
data = resp.read()
return dict(dirs=[], files=[], saveURL = request.route_url('files'), pathDefault=request.params['path'])
return dict(dirs=[], files=[], saveURL = request.route_url('files'), pathDefault='/')
I'm mainly having trouble with the files function. type(data) is returning <type 'str'>. Sending the string to my template displays {"files": ["file1.jpg", "file2.png"], "version": "1.0", "directories": ["Directory1"]}
Does anyone know why I'm getting a string result?
When you make a request the server sends you some bytes back. How you interpret those bytes is up to you. If you request XML instead of JSON, you also do not receive an XML object, but bytes that can be interpreted as XML.
It is superfluous to make an explicit request. This makes sense when:
Crossing languages
Crossing components and you want really little coupling
In this case all code is in the same component, and the same language. A better solution is a helper function:
def fetch_files_and_directories(path):
path = os.path.dirname(__file__) + '/files' + path
if os.path.isdir(path):
files = [ f for f in os.listdir(path) if os.path.isfile(os.path.join(path,f)) ]
dirs = [ f for f in os.listdir(path) if os.path.isdir(os.path.join(path,f)) ]
# Note that the is None check is nonsense.
# The result of an empty list comprehension is an empty list, not None
else:
files = []
directories = []
return dict(directories=dirs, files=files)
#view_config(route_name='file', renderer='json')
def file(request):
response = fetch_files_and_directories(request.json_body['path'])
# you might want to move this into the fetch_* function.
response['version']='1.0'
return response
#view_config(route_name='files', renderer='templates/filesTest.jinja2')
def files(request):
if 'form.submitted' in request.params:
path = request.params['path']
data = fetch_files_and_directories(path)
# You did not use data below here, so neither did I.
return dict(dirs=[], files=[], saveURL = request.route_url('files'), pathDefault=path)
return dict(dirs=[], files=[], saveURL = request.route_url('files'), pathDefault='/')
Keep in mind that file(request) now maybe has become obsolete.

Categories

Resources