I have spent more than 4 days trying to figure out this, but my every attempt failed, so I thought I could really use some of your helps.
In the code below, Storm is an app with function submit_data() which fetches data from databases according to pagename parameter passed at instantiation and idx parameter passed from xmlhttp GET request in Javascript.
Basically I want to have an index page with Root class, and a bunch of similar Storm pages with different pagename parameters read from an XML file.
I am using RoutesDispatcher because I am parametrizing page name(address) using the XML file too.
When I used /storm as the address it didn't even work probably because the name clashes, so I set the address as /st/.
Now it displays the header, but it does not find submit_data function at all. The Chrome console throws the following error.
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:8080/st/submit_data?idx=0
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:8080/st/submit_data?idx=2
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:8080/st/submit_data?idx=1
How can I structure this so that when I type localhost:8080/st on my browser, the html/javascript template storm.html looks up submit_data function and call it and display it on the webpage? Before I started routing, it worked totally fine.
class Storm(object):
pagename = ""
def __init__(self, pagename):
self.pagename = pagename
#cherrypy.expose
#cherrypy.tools.mako(filename="storm.html", directories=MEDIA_DIR)
def index(self, name=None):
return {'size': len(params["dashboards"][self.pagename])}
#cherrypy.expose
def submit_data(self, idx):
idx = int(idx)
chart = params["dashboards"][self.pagename][idx]
# Sample page that displays the number of records in "table"
# Open a cursor, using the DB connection for the current thread
c = cherrypy.thread_data.dbdict[chart["dbname"]].cursor()
print 'query being executed......'
c.execute(chart["command"])
print 'query being fetched......'
res = c.fetchall()
c.close()
# construct a JSON object from query result
jsres = []
jsres.append(chart["selected"])
.
.
.
return json.dumps(jsres)
class Root(object):
#storm = Storm("first")
#cherrypy.expose
#cherrypy.tools.mako(filename="index.html", directories=MEDIA_DIR)
def index(self, name=None):
return {}
config = {'/media':
{'tools.staticdir.on': True,
'tools.staticdir.dir': MEDIA_DIR,
}
}
root = Root()
storm = Storm("first")
mapper = cherrypy.dispatch.RoutesDispatcher()
mapper.connect('home','/',controller=root, action='index')
mapper.connect('st','/st/',controller=storm, action='index')
conf = {'/': {'request.dispatch': mapper}}
app=cherrypy.tree.mount(root=None,config=conf)
cherrypy.quickstart(app)
mapper.connect('st','/st/submit_data',controller=storm, action='submit_data')
Routes explicitly list all their handlers, and because of that also don't need to be exposed. Based on this example, you might not want the routes dispatcher. The default dispatcher with multiple roots mounted would work fine.
Related
So, in my main app.py, I have :
app = create_app()
...
app.run()
In __init__.py I have:
def create_app():
...
app = Flask(__name__)
setup_logging(app)
return app
def setup_logging(app):
RequestID(app)
handler = logging.FileHandler(app.container.config.get("app.LOG_FILE"))
handler.setFormatter(logging.Formatter("%(asctime)s : %(levelname)s : %(request_id)s - %(message)s"))
handler.addFilter(RequestIDLogFilter()) # << Add request id contextual filter
logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(level="DEBUG")
And in my routes.py, I have: (This wiring/linking is done with the help of dependency injection)
def configure(app):
app.add_url_rule("/", "check", check)
...
def check():
logging.info("You hit /")
# Here I want to return the Request UUID that's generated by RequestLogIDFilter
return make_response(jsonify(dict(ok=True)), 200)
How do I access the Request UUID generated by the RequestLogIDFilter? In my log file I correctly see the log messages as:
2022-08-15 07:00:18,030 : INFO : 27b437fd-98be-4bc9-a609-912043e3a38e - Log message test memberId=9876
I want to take this value 27b437fd-98be-4bc9-a609-912043e3a38e and include it in the response headers as X-REQUEST-UUID: 27b437fd-98be-4bc9-a609-912043e3a38e
An alternative was to rip out RequestLogIDFilter and only work with flask-request-id-header except, there's no way to write it to log file (logging.FileHandler does not have a write() method so it fails)
This is something trivial I'm sure but the official docs nowhere mention how to access these request ids for logging to file or sending back as a response header.
If you are using flask_log_request_id then you probably want this as given in their github repo
Code copied here in case link does not work
from flask_log_request_id import current_request_id
#app.after_request
def append_request_id(response):
response.headers.add('X-REQUEST-ID', current_request_id())
return response
I have a successfully compiled and run a django rest consuming cocktaildb api. On local server when I run http://127.0.0.1:8000/api/ I get
{
"ingredients": "http://127.0.0.1:8000/api/ingredients/",
"drinks": "http://127.0.0.1:8000/api/drinks/",
"feeling-lucky": "http://127.0.0.1:8000/api/feeling-lucky/"
}
But when I go to one of the links mentioned in the json result above, for example:
http://127.0.0.1:8000/api/ingredients/
I get an empty [] with a status 200OK!
I need an endpoint to GET drinks and ingredients before I can destructure to specific details using angular.
I implemented helper folder in the app with the the API function as below:
class TheCoctailDBAPI:
THECOCTAILDB_URL = 'https://www.thecocktaildb.com/api/json/v1/1/'
async def __load_coctails_for_drink(self, drink, session):
for i in range(1, 16):
ingredientKey = 'strIngredient' + str(i)
ingredientName = drink[ingredientKey]
if not ingredientName:
break
if ingredientName not in self.ingredients:
async with session.get(f'{TheCoctailDBAPI.THECOCTAILDB_URL}search.php?i={ingredientName}') \
as response:
result = json.loads(await response.text())
self.ingredients[ingredientName] = result['ingredients'][0]
What was your expected responce?
Add the function that is called by this API as well as the DB settings in the question, so that we can properly help you.
Are you sure that you are connecting and pulling data from a remote location? It looks to me like your local DB is empty, so the API has no data to return.
I am currently writing tests for our project, and I ran into an issue. We have this section of a view, which will redirect the user back to the page where they came from including an error message (that's being stored in the session):
if request.GET.get('error_code'):
"""
Something went wrong or the call was cancelled
"""
errorCode = request.GET.get('error_code')
if errorCode == 4201:
request.session['errormessage'] = _('Action cancelled by the user')
return HttpResponseRedirect('/socialMedia/manageAccessToken')
Once the HttpResponseRedirect kicks in, the first thing that the new view does is scan the session, to see if any error messages are stored in the session. If there are, we place them in a dictionary and then delete it from the session:
def manageAccessToken(request):
"""
View that handles all things related to the access tokens for Facebook,
Twitter and Linkedin.
"""
contextDict = {}
try:
contextDict['errormessage'] = request.session['errormessage']
contextDict['successmessage'] = request.session['successmessage']
del request.session['errormessage']
del request.session['successmessage']
except KeyError:
pass
We should now have the error message in a dictionary, but after printing the dictionary the error message is not there. I also printed the session just before the HttpResponseRedirect, but the session is an empty dictionary there as well.
This is the test:
class oauthCallbacks(TestCase):
"""
Class to test the different oauth callbacks
"""
def setUp(self):
self.user = User.objects.create(
email='test#django.com'
)
self.c = Client()
def test_oauthCallbackFacebookErrorCode(self):
"""
Tests the Facebook oauth callback view
This call contains an error code, so we will be redirected to the
manage accesstoken page. We check if we get the error message
"""
self.c.force_login(self.user)
response = self.c.get('/socialMedia/oauthCallbackFacebook/',
data={'error_code': 4201},
follow=True,
)
self.assertEqual('Action cancelled by the user', response.context['errormessage'])
It looks like the session can not be accessed or written to directly from the views during testing. I can, however, access a value in the session by manually setting it in the test by using the following bit of code:
session = self.c.session
session['errormessage'] = 'This is an error message'
session.save()
This is however not what I want, because I need the session to be set by the view as there are many different error messages in the entire view. Does anyone know how to solve this? Thanks in advance!
After taking a closer look I found the issue, it is in the view itself:
errorCode = request.GET.get('error_code')
if errorCode == 4201:
request.session['errormessage'] = _('Action cancelled by the user')
The errorCode variable is a string, and I was comparing it to an integer. I fixed it by changing the second line to:
if int(errorCode) == 4201:
On GAE, I need to make some REST calls to a PiCloud server, then create an output page based on PiCloud return values. However, it will take several minutes for PiCloud to process the model. Thus I am wondering if I could create a 'loading' page first, and after finishing calculation, present the real output page.
In detail, the questions is how do I keep checking the status of my REST service and then generate different HTML pages based upon.
I appreciate any suggestions and comments!
PS: jQuery BlockUI seems to be good example, but it requires to estimate the timeout duration, which I could not guess...
Function to Call REST Service:
def get_jid(pdf_t, pdf_nop, pdf_p):
response = urlfetch.fetch(url=url, payload=data, method=urlfetch.POST, headers=http_headers)
jid= json.loads(response.content)['jid']
output_st = "running"
while output_st!="done":
response_st = urlfetch.fetch(url='https://api.picloud.com/job/?jids=%s&field=status' %jid, headers=http_headers)
output_st = json.loads(response_st.content)['info']['%s' %jid]['status']
url_val = 'https://api.picloud.com/job/result/?jid='+str(jid)
response_val = urlfetch.fetch(url=url_val, method=urlfetch.GET, headers=http_headers)
output_val = json.loads(response_val.content)['result']
return(jid, output_st, output_val)
Generate HTML Page:
class pdfPage_loading(webapp.RequestHandler):
def post(self):
final_res=get_jid(pdf_t, pdf_nop, pdf_p)[2]
html = html + template.render(templatepath + 'popup_pdf_eco.html', {
'title':'Ubertool',
'model_page':'',
'model_attributes':'Please wait','text_paragraph':''})
self.response.out.write(html)
class pdfPage_done(webapp.RequestHandler):
def post(self):
final_res=get_jid(pdf_t, pdf_nop, pdf_p)[2]
html = html + template.render(templatepath + 'popup_pdf_eco.html', {
'title':'Ubertool',
'model_page':final_res,
'model_attributes':'Please download your PDF here','text_paragraph':''})
self.response.out.write(html)
app_loading = webapp.WSGIApplication([('/.*', pdfPage_loading)], debug=True)
app_done = webapp.WSGIApplication([('/.*', pdfPage_done)], debug=True)
def main():
##Here is the problematic part:
if get_jid(pdf_t, pdf_nop, pdf_p)!='done':
run_wsgi_app(app_pre)
else:
run_wsgi_app(app)
if __name__ == '__main__':
main()
First off, you do not need multiple WSGIApplication handlers. You can base requests off of URLs and GET/POST params.
I would use a mixture of tasks and the Channel API:
When a user visits the page:
Create a channel token for the user to use
Make the REST API calls
Kick off a task (described below) passing it the created token and the id of the PiCloud job
Show the "loading page" and have the user connect to the Channel API with the token created in step 1
In the task, have it check on the status of the PiCloud job. If the status is not done, have the task setup a new task to call itself again, and check on the status of the job. Once the job is complete, pass the data along through the Channel API to the user so that they can then load the contents of the page or redirect to a page that will have the contents ready for them.
Example status check code (this is using the example code from PiCloud, you can use your own requests as you have been to get the status through urlfetch - this is merely meant as an example so you can plug your code in to work as needed):
import cloud
from google.appengine.api import taskqueue
from google.appengine.api import channel
#<Your other hanlders here>
class CheckStatus(webapp.RequestHandler):
def post(self):
job_id = self.request.get('job_id')
token = self.request.get('token')
status = cloud.get('status')
if status != 'done':
taskqueue.add(url='/checkstatus', params={'job_id': job_id, 'token': token}, method="POST", countdown=3)
return
channel.send_message(token, '<some message here>')
app = webapp.WSGIApplication([('/done', pdfPage_done),
('/checkstatus', CheckStatus),
('/.*', pdfPage_loading)], debug=True)
def main():
run_wsgi_app(app)
if __name__ == '__main__':
main()
I'm trying to subscribe to a tag. It appears that the callback URL is being called correctly with a hub.challenge and hub.mode, and I figured out how to access the challenge using self.request.get('hub.challenge'). I thought I was just supposed to echo the challenge, but that doesn't appear to work since I receive the following errors in the GAE logs:
InstagramAPIError: (400) APISubscriptionError-Challenge verification failed. Sent "647bf6dbed31465093ee970577ce1b72", received "
647bf6dbed31465093ee970577ce1b72
".
Here is the full handler:
class InstagramHandler(BaseHandler):
def get(self):
def process_tag_update(update):
update = update
mode = self.request.get('hub.mode')
challenge = self.request.get('hub.challenge')
verify_token = self.request.get('hub.verify_token')
if challenge:
template_values = {'challenge':challenge}
path = os.path.join(os.path.dirname(__file__), '../templates/instagram.html')
html = template.render(path, template_values)
self.response.out.write(html)
else:
reactor = subscriptions.SubscriptionsReactor()
reactor.register_callback(subscriptions.SubscriptionType.TAG, process_tag_update)
x_hub_signature = self.request.headers.get('X-Hub-Signature')
raw_response = self.request.data
try:
reactor.process('INSTAGRAM_SECRET', raw_response, x_hub_signature)
except subscriptions.SubscriptionVerifyError:
logging.error('Instagram signature mismatch')
So returning it as a string worked. I should have payed closer attention to the error message, but it took a helpful person on the Python IRC to point out the extra line breaks in the message. Once I put the template files on one line, it seemed to work. I can now confirm that my app is authorized via Instagram's list subscription URL.