Flask optional routes putting /None/None in URL - flask

I've been trying to set up some optional parameters like below. The user should be able to browse either the top 100, the states or the cities at any point in the heirarchy.
#app.route("/guide", defaults={'state': None, 'city': None})
#app.route("/guide/<state>", defaults={'state': None})
#app.route("/guide/<state>/<city>")
def guide_route(state, city):
if state == 'top_100':
return render_template('top_100.html')
elif state:
return render_template('state.html', data={'state': state})
elif state and city:
return render_template('city.html', data={'state': state, 'city': city})
else:
return render_template('something_else.html')
However, when I go to /guide/top_100 or /guide in my web browser, it redirects me to /guide/None/None which is a 404. What's also interesting is that I've now put print statements inside every part of the guide_route() function and none of them fire. So something isn't working in the routing table at all.
How can I get these optional parameters to work?

There is a mistake in the way you input the default values. You have to do it in the function definition (and not in routes) :
#app.route("/guide",)
#app.route("/guide/<string:state>")
#app.route("/guide/<string:state>/<string:city>")
def guide_route(state=None, city=None):
if state == 'top_100':
return render_template('top_100.html')
elif state and city:
return render_template('city.html', data={'state': state, 'city': city})
elif state:
return render_template('state.html', data={'state': state})
else:
return render_template('something_else.html')
https://flask.palletsprojects.com/en/2.0.x/quickstart/#variable-rules.
An example with default value : https://flask.palletsprojects.com/en/2.0.x/quickstart/#rendering-templates
(I also inverted two elif conditions)

Related

Flash message not showing because of conditon

In flask, im trying to flash a message when there are no results from the user's search. Problem is, the flash is not doing its job. I believe its because of the if condition but im not sure why.
If i make this the condition: if counter == 0: then flash , it works. But even when the user is just reloading the search page, the message gets flashed so its not desirable. That why im trying to create the condition based on book.query & this is where im stuck.
#app.route("/search", methods=['GET', 'POST'])
def search():
keyword = escape(request.form.get("keyword"))
books = Book.query.filter(or_(Book.isbn.like(f'%{keyword}%'), Book.title.like(
f'%{keyword}%'), Book.author.like(f'%{keyword}%'))).all()
counter = Book.query.filter(or_(Book.isbn.like(f'%{keyword}%'), Book.title.like(
f'%{keyword}%'), Book.author.like(f'%{keyword}%'))).count()
if books is None:
flash('No matches found!', 'info')
return render_template('search.html', title='Search', books=books)
In your query for books you use .all() at the end.
.all() returns a list - in case of no result, this will be an empty list, but you compare the result to None.
While both None and [] are falsy values, they are not identical.
But you explicitly compare object identity with the is keyword.
So you could change your if guard to
if not books:
or
if len(books) == 0:
Make sure to tell us how it worked out!

Amazon Lex not prompting for missing variables when using CodeHook Validation

I am building an agent in Amazon Lex with around 3 intents. All the 3 intents have a variable which has been ticked as 'required', meaning the agent has to prompt for those variables when the user query is missing it.
However when I am using a lambda function as codehook validation the , function gets triggered without prompting for the missing variable.
For example: Intent which describes call notes from a call with a specific person:
The prompt is " Specify the name of the person whose notes you want to see'
The objective of the lambda function is to print out "Call notes for the person is XYZ'
When I don't use any lambda function through codehook validation, I get a prompt for the name of the person,
but when I use codehook validation, the lambda function gets triggered and I get the reply as "Call notes for None is XYZ".
None because , there wasn't any mention of the person's name in the user query nor am I getting prompted for the person's name.
Can anybody help regarding this? I have tried all sorts of modifications in the lambda function , but shouldn't the prompt be an independent functionality from the lambda function?
I have been browsing and trying things regarding this since 2~3 days and have hit a dead end.
This is happening because Lambda initialization and validation happens before Slot filling in Amazon Lex.
You can still check that user have provided the "person" slot in DialogCodeHook i.e validation part.
Something like below code will get your work done:
def build_validation_result(is_valid, violated_slot, message_content):
if message_content == None:
return {
'isValid': is_valid,
'violatedSlot': violated_slot
}
return {
'isValid': is_valid,
'violatedSlot': violated_slot,
'message': {'contentType': 'PlainText', 'content': message_content}
}
def validate_person(person):
# apply validations here
if person is None:
return build_validation_result(False, 'person', 'Please enter the person name')
return build_validation_result(True, None, None)
def get_notes(intent_request):
source = intent_request['invocationSource']
output_session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {}
slots = intent_request['currentIntent']['slots']
person = slots['person']
if source == 'DialogCodeHook':
# check existence of "person" slot
validation_result = validate_person(person)
if not validation_result['isValid']:
slots[ticket_validation_result['violatedSlot']] = None
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
slots,
validation_result['violatedSlot'],
validation_result['message']
)
return delegate(output_session_attributes, slots)
If the "person" slot is empty, user will be prompted the error message. This way you don't need to tick on "Required" on the slot.
This was the only workaround I could think of when I faced this problem while using DialogCodeHook.
Hope it helps.

How to start a new request after the item_scraped scrapy signal is called?

I need to scrap the data of each item from a website using Scrapy(http://example.com/itemview). I have a list of itemID and I need to pass it in a form in example.com.
There is no url change for each item. So for each request in my spider the url will always be the same. But the content will be different.
I don't wan't a for loop for handling each request. So i followed the below mentioned steps.
started spider with the above url
added item_scraped and spider_closed signals
passed through several functions
passed the scraped data to pipeline
trigerred the item_scraped signal
After this it automatically calls the spider_closed signal. But I want the above steps to be continued till the total itemID are finished.
class ExampleSpider(scrapy.Spider):
name = "example"
allowed_domains = ["example.com"]
itemIDs = [11111,22222,33333]
current_item_num = 0
def __init__(self, itemids=None, *args, **kwargs):
super(ExampleSpider, self).__init__(*args, **kwargs)
dispatcher.connect(self.item_scraped, signals.item_scraped)
dispatcher.connect(self.spider_closed, signals.spider_closed)
def spider_closed(self, spider):
self.driver.quit()
def start_requests(self):
request = self.make_requests_from_url('http://example.com/itemview')
yield request
def parse(self,response):
self.driver = webdriver.PhantomJS()
self.driver.get(response.url)
first_data = self.driver.find_element_by_xpath('//div[#id="itemview"]').text.strip()
yield Request(response.url,meta={'first_data':first_data},callback=self.processDetails,dont_filter=True)
def processDetails(self,response):
itemID = self.itemIDs[self.current_item_num]
..form submission with the current itemID goes here...
...the content of the page is updated with the given itemID...
yield Request(response.url,meta={'first_data':response.meta['first_data']},callback=self.processData,dont_filter=True)
def processData(self,response):
...some more scraping goes here...
item = ExamplecrawlerItem()
item['first_data'] = response.meta['first_data']
yield item
def item_scraped(self,item,response,spider):
self.current_item_num += 1
#i need to call the processDetails function here for the next itemID
#and the process needs to contine till the itemID finishes
self.parse(response)
My piepline:
class ExampleDBPipeline(object):
def process_item(self, item, spider):
MYCOLLECTION.insert(dict(item))
return
I wish I had an elegant solution to this. But instead it's a hackish way of calling the underlying classes.
self.crawler.engine.slot.scheduler.enqueue_request(scrapy.Request(url,self.yourCallBack))
However, you can yield a request after you yield the item and have it callback to self.processDetails. Simply add this to your processData function:
yield item
self.counter += 1
yield scrapy.Request(response.url,callback=self.processDetails,dont_filter=True, meta = {"your":"Dictionary"}
Also, PhantomJS can be nice and make your life easy, but it is slower than regular connections. If possible, find the request for json data or whatever makes the page unparseable without JS. To do so, open up chrome, right click, click inspect, go to the network tab, then enter the ID into the form, then look at the XHR or JS tabs for a JSON that has the data or next url you want. Most of the time, there will be some url made by adding the ID, if you can find it, you can just concatenate your urls and call that directly without having the cost of JS rendering. Sometimes it is randomized, or not there, but I've had fair success with it. You can then also use that to yield many requests at the same time without having to worry about phantomJS trying to do two things at once or having to initialize many instances of it. You could use tabs, but that is a pain.
Also, I would use a Queue of your IDs to ensure thread safety. Otherwise, you could have processDetails called twice on the same ID, though in the logic of your program everything seems to go linearly, which means you aren't using the concurrency capabilities of Scrapy and your program will go more slowly. To use Queue add:
import Queue
#go inside class definition and add
itemIDQueue = Queue.Queue()
#within __init__ add
[self.itemIDQueue.put(ID) for ID in self.itemID]
#within processDetails replace itemID = self.itemIDs[self.current_item_num] with
itemID = self.itemIDQueue.get()
And then there is no need to increment the counter and your program is thread safe.

Frustrating python syntax error

I am writing a script to automate HvZ games at my college and have run into this strange frustrating syntax error:
File "HvZGameMaster.py", line 53
class players(object):
^
SyntaxError: invalid syntax
Here is the offending code
class mailMan(object):
"""mailMan manages player interactions such as tags reported via text messages or emails"""
def __init__(self, playerManager):
super(mailMan, self).__init__()
self.mail = imaplib.IMAP4_SSL('imap.gmail.com')
self.mail.login(args.username,args.password)
self.mail.list()
# Out: list of "folders" aka labels in gmail.
self.mail.select("inbox") #connect to inbox.
def getBody(self, emailMessage):
maintype = emailMessage.get_content_maintype()
if maintype == 'multipart':
for part in emailMessage.get_payload():
if part.get_content_maintype() == 'text':
return part.get_payload()
elif maintype == 'text':
return emailMessage.get_payload()
def getUnread(self):
self.mail.select("inbox") # Select inbox or default namespace
(retcode, messages) = self.mail.search(None, '(UNSEEN)')
if retcode == 'OK':
retlist = []
for num in messages[0].split(' '):
print 'Processing :', messages
typ, data = self.mail.fetch(num,'(RFC822)')
msg = email.message_from_string(data[0][1])
typ, data = self.mail.store(num,'-FLAGS','\\Seen')
if retcode == 'OK':
for item in str(msg).split('\n'):
#finds who sent the message
if re.match("From: *",item):
print (item[6:], self.getBody(msg))
retlist.append((item[6:], self.getBody(msg).rstrip())
#print (item, self.getBody(msg).rstrip())
class players(object): #<-the problem happens here
"""manages the player"""
def __init__(self, pDict):
super(players, self).__init__()
self.pDict = pDict
#makes a partucular player a zombie
def makeZombie(self, pID):
self.pDict[pID].zombie = True
#makes a partucular player a zombie
def makeHuman(self, pID):
self.pDict[pID].zombie = False
As far as I can tell what I have written is correct and I have checked to make sure it is all tabs and not spaces I have made sure i don't have any erroneous \r's or \n's floating around (all \n's are where the should be at the end of the line and I'm not using any \r's)
You can find all my code for this project here if you would like to try running it yourself
There is an unbalanced (missing) parenthesis on the line above the line raising the error:
retlist.append((item[6:], self.getBody(msg).rstrip())
Note that some editors have matching parenthesis highlighting, and key combinations for moving back and forth across matched parentheses. Using an editor with these features can help cut down on these errors.

how to use django object in session

I was wondering how i can check if a object is in session and depending on that, do something.
def login_character(request, character_name):
request.session['character'] = Character.objects.get(name=character_name)
return HttpResponseRedirect(reverse('index'))
some other function:
if request.session['character']:
print request.session['character'].name
else:
print "nothing to see here"
How i try it, i kepe getting back to a KeyError
session uses the standard Python dictionary interface, so you want either:
if 'character' in request.session:
print request.session['character'].name
else:
print "nothing to see here"
Or, in some cases, request.session.get('character') if you just want to have a default value if the key is not present.