SyntaxError while trying to run python code (Array) - python

I'm getting an error:
File "/home/ofw/playlister/app.py", line 7
playlists = [
^
SyntaxError: invalid syntax
This is my app.py code:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
playlists = [
{ 'title': 'Cat Videos', 'description': 'Cats acting weird' },
{ 'title': '80\'s Music', 'description': 'Don\'t stop believing!' }
]
#app.route('/)
def playlists_index():
"""Show all playlists."""
return render_template('playlists_index.html', playlists=playlists)
I'm guessing it's related to the playlist array. Any suggestions? In advance, thanks.

You are using a decorator on line 5 but there is nothing to decorate below. There must be a function/method.
The one on line 11 is correct.

the positioning of your playlist variable is wrong, because it's directly under a decorator "#app.route" What SHOULD come after a decorator is a function. try editing the code this way:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/)
def playlists_index():
"""Show all playlists."""
playlists = [
{ 'title': 'Cat Videos', 'description': 'Cats acting weird' },
{ 'title': '80\'s Music', 'description': 'Don\'t stop believing!' }
]
return render_template('playlists_index.html', playlists=playlists)
or creating the playlist variable globally, this way:
playlists = [
{ 'title': 'Cat Videos', 'description': 'Cats acting weird' },
{ 'title': '80\'s Music', 'description': 'Don\'t stop believing!' }
]
#app.route('/')
def playlists_index():
"""Show all playlists."""
return render_template('playlists_index.html', playlists=playlists)
don't forget to correct your route string on line 11 to:
#app.route('/')

Related

Internal Server Error Dash app serving a Flask Server: A name collision occurred between blueprints

I'm working with a Dash App inside a Flask Server.
This is how I instantiate the Flask Server:
from flask import Flask, redirect
server = Flask(__name__, template_folder="../frontend/templates",
instance_relative_config=True)
This is the create_dash_app function:
import dash
def create_dash_app(server):
dash_app = dash.Dash(
__name__,
server=server,
url_base_pathname='/dash/',
external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css']
)
dash_app.config['suppress_callback_exceptions'] = True
dash_app.layout = html.H3("Lorem ipsum... ")
return dash_app.server
At one of my server routes I have the following:
#server.route('/filter')
def filter_stuff():
<some code>
create_dash_app()
redirect("/dash")
The point is that for my use case a user may want to see the dash app, go back to the filter route, apply some filters and check the changings in the dash app.
Unfortunately, when I do that I get the following:
> AssertionError: A name collision occurred between blueprints
> <flask.blueprints.Blueprint object at 0x11905bcd0> and
> <flask.blueprints.Blueprint object at 0x118acbad0>. Both share the
> same name "_dash_dash_assets". Blueprints that are created on the fly
> need unique names.
Given my little experience with Flask and Dash I guess it's something trivial that I'm missing. Please tell me if there is additional info you may need.
EDIT: I've found a workaround which I'm ashamed of
2nd EDIT: included a callback example
from flask import Flask, redirect
from multiprocessing import Value
counter = Value('i', 0)
server = Flask(__name__, template_folder="../frontend/templates",
instance_relative_config=True)
#server.route('/filter')
def filter_stuff():
with counter.get_lock():
counter.value += 1
out = counter.value
<some code>
dash_address = "/dash"+str(out)+"/"
create_dash_app(server, dash_address, nodes_cyto, edges_cyto)
redirect(dash_address)
import dash
from dash_core_components import *
from dash.dependencies import Input, Output
def create_dash_app(server, dash_address, nodes_cyto, edges_cyto):
dash_app = dash.Dash(
__name__,
server=server,
url_base_pathname=dash_address,
external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css']
)
dash_app.config['suppress_callback_exceptions'] = True
dash_app.layout = html.Div([
cyto.Cytoscape(
id='cytoscape_net',
elements=nodes_cyto + edges_cyto,
zoomingEnabled=True,
zoom=0.8,
layout={'name': 'klay'},
style={'width': '80%', 'height': '700px', 'float': 'left'},
stylesheet=my_stylesheet
),
dcc.Tabs([
dcc.Tab(label='Dettaglio Interazioni', style=tab_style, selected_style=tab_selected_style, children=[
dash_table.DataTable(
id='edge-table',
columns=[],
style_header={
'backgroundColor': 'rgb(230, 230, 230)',
'fontWeight': 'bold'
},
fixed_rows={'headers': True},
style_data = {'whiteSpace': 'normal',
'height': 'auto',
'lineHeight': '15px'},
style_data_conditional=[
{
'if': {'row_index': 'odd'},
'backgroundColor': 'rgb(248, 248, 248)'
},
{
'if': {'column_id': 'Info'},
'textAlign': 'right'
}
],
style_cell={
'height': 'auto',
'minWidth': '180px', 'width': '180px', 'maxWidth': '180px',
'whiteSpace': 'normal',
'fontSize':14
},
style_table={'width': '50%',
'overflowY': 'auto',
'overflowX': 'auto'},
data=[])
])
init_callbacks(dash_app)
return dash_app.server
def init_callbacks(app):
#app.callback([Output('edge-table', 'data'),
Output('edge-table', 'columns')],
[Input('cytoscape_net', 'tapEdgeData')])
def populateEdgeTable(data):
json_data = json.loads(json.dumps(data, indent=2))
if data is None:
return [], []
dict_data = dict(json_data)
dict_data_1 = {your_key.replace("_1", ""): dict_data[your_key] for your_key in
[j for j in dict_data.keys() if "_1" in j] + ["tipo_interazione"]}
dict_data_2 = {your_key.replace("_2", ""): dict_data[your_key] for your_key in
[j for j in dict_data.keys() if "_2" in j] + ["tipo_interazione"]}
columns = [{'name': 'Info', 'id': 'Info'},
{'name': 'Source', 'id': 'Source'},
{'name': 'Target', 'id': 'Target'}]
return pd.DataFrame({'Info': list(dict_data_1.keys()),
'Source': list(dict_data_1.values()),
'Target': list(dict_data_2.values())}).to_dict(orient='records'), columns
As you can see I'm using a counter to redirect to a new page each time. I wonder if some people might die reading this.
Thank you
While Dash apps can be instantiated dynamically, in my experience this is a design pattern that should be avoided. Regarding the specific use case that is being targeted in this question, i would consider two main pathways,
Integrate the filters with the Dash app
The most simplistic and coherent solution would be to include the filters as part of the Dash app itself. With this approach, it would be possible to apply the filters to other page(s) by passing the filter values as a State argument to the page rendering callback(s). They could either be passed directly (if there are not too many filters) or aggregated via e.g. a Store component. Here is a code snippet to illustrate the concept,
#app.callback(Output("store", "data"), [Input("filter{}".format(i), "value") for i in range(100)])
def aggregate_filters(*args):
return list(args)
#app.callback(Output("page", "children"), [Input(...)], [State("store", "data")])
def render_page(*args, filters):
...
Pass filter selections to the Dash app
If it is not possible to port the filter page itself to Dash, you could serialize the filter selections and pass them to the Dash app. It could be e.g. via the URL (if there are not too many selections) or a session variable. Here is a sketch of the latter,
from flask import session, redirect
...
#server.route('/filter')
def filter_stuff():
# This assignment might happen from JavaScript depending on you app structure
session["filters"] = ...
redirect(dash_address)
...
#app.callback(Output("page", "children"), [Input(...)])
def render_page(*args, filters):
filters = session["filters"]
...
Other variations of this approach include saving the selections server side (e.g. in a file or a memory cache such a Redis) identified by a key, e.g. a uuid, which is then passed via the URL to the Dash app.
Regarding the hack the you are currently utilizing, in additional to being not-too-beautiful, i fear that it will deplete the server resources since you are effectively creating a new Dash app every time the filter selections change.

Response class in Flask-RESTplus

What is the proper way to handle response classes in Flask-RESTplus?
I am experimenting with a simple GET request seen below:
i_throughput = api.model('Throughput', {
'date': fields.String,
'value': fields.String
})
i_server = api.model('Server', {
'sessionId': fields.String,
'throughput': fields.Nested(i_throughput)
})
#api.route('/servers')
class Server(Resource):
#api.marshal_with(i_server)
def get(self):
servers = mongo.db.servers.find()
data = []
for x in servers:
data.append(x)
return data
I want to return my data in as part of a response object that looks like this:
{
status: // some boolean value
message: // some custom response message
error: // if there is an error store it here
trace: // if there is some stack trace dump throw it in here
data: // what was retrieved from DB
}
I am new to Python in general and new to Flask/Flask-RESTplus. There is a lot of tutorials out there and information. One of my biggest problems is that I'm not sure what to exactly search for to get the information I need. Also how does this work with marshalling? If anyone can post good documentation or examples of excellent API's, it would be greatly appreciated.
https://blog.miguelgrinberg.com/post/customizing-the-flask-response-class
from flask import Flask, Response, jsonify
app = Flask(__name__)
class CustomResponse(Response):
#classmethod
def force_type(cls, rv, environ=None):
if isinstance(rv, dict):
rv = jsonify(rv)
return super(MyResponse, cls).force_type(rv, environ)
app.response_class = CustomResponse
#app.route('/hello', methods=['GET', 'POST'])
def hello():
return {'status': 200, 'message': 'custom_message',
'error': 'error_message', 'trace': 'trace_message',
'data': 'input_data'}
result
import requests
response = requests.get('http://localhost:5000/hello')
print(response.text)
{
"data": "input_data",
"error": "error_message",
"message": "custom_message",
"status": 200,
"trace": "trace_message"
}

Unittest Django: Mock external API, what is proper way?

I am having a problem understanding how mock works and how to write unittests with mock objects. I wanted to mock an external api call every time when my model calls save() method.
My code:
models.py
from . import utils
class Book(Titleable, Isactiveable, Timestampable, IsVoidable, models.Model):
title
orig_author
orig_title
isbn
def save(self, *args, **kwargs):
if self.isbn:
google_data = utils.get_original_title_and_name(self.isbn)
if google_data:
self.original_author = google_data['author']
self.original_title = google_data['title']
super().save(*args, **kwargs)
utils.py
def get_original_title_and_name(isbn, **kawargs):
isbn_search_string = 'isbn:{}'.format(isbn)
payload = {
'key': GOOGLE_API_KEY,
'q': isbn_search_string,
'printType': 'books',
}
r = requests.get(GOOGLE_API_URL, params=payload)
response = r.json()
if 'items' in response.keys():
title = response['items'][THE_FIRST_INDEX]['volumeInfo']['title']
author = response['items'][THE_FIRST_INDEX]['volumeInfo']['authors'][THE_FIRST_INDEX]
return {
'title': title,
'author': author
}
else:
return None
I began read docs and write test:
test.py:
from unittest import mock
from django.test import TestCase
from rest_framework import status
from .constants import THE_FIRST_INDEX, GOOGLE_API_URL, GOOGLE_API_KEY
class BookModelTestCase(TestCase):
#mock.patch('requests.get')
def test_get_original_title_and_name_from_google_api(self, mock_get):
# Define new Mock object
mock_response = mock.Mock()
# Define response data from Google API
expected_dict = {
'kind': 'books#volumes',
'totalItems': 1,
'items': [
{
'kind': 'books#volume',
'id': 'IHxXBAAAQBAJ',
'etag': 'B3N9X8vAMWg',
'selfLink': 'https://www.googleapis.com/books/v1/volumes/IHxXBAAAQBAJ',
'volumeInfo': {
'title': "Alice's Adventures in Wonderland",
'authors': [
'Lewis Carroll'
]
}
}
]
}
# Define response data for my Mock object
mock_response.json.return_value = expected_dict
mock_response.status_code = 200
# Define response for the fake API
mock_get.return_value = mock_response
The first of all, I can't write target for the #mock.patch correct. If a define target as utuls.get_original_title_and_name.requests.get, I get ModuleNotFoundError. Also I can't understand how to make fake-call to external API and verify recieved data (whether necessarly its, if I've already define mock_response.json.return_value = expected_dict?) and verify that my save() method work well?
How do I write test for this cases? Could anyone explain me this case?
You should mock the direct collaborators of the code under test. For Book that would be utils. For utils that would be requests.
So for the BookModelTestCase:
class BookModelTestCase(TestCase):
#mock.patch('app.models.utils')
def test_save_book_calls_google_api(self, mock_utils):
mock_utils.get_original_title_and_name.return_value = {
'title': 'Google title',
'author': 'Google author'
}
book = Book(
title='Some title',
isbn='12345'
)
book.save()
self.assertEqual(book.title, 'Google title')
self.assertEqual(book.author, 'Google author')
mock_utils.get_original_title_and_name.assert_called_once_with('12345')
And then you can create a separate test case to test get_original_title_and_name:
class GetOriginalTitleAndNameTestCase(TestCase):
#mock.patch('app.utils.requests.get')
def test_get_original_title_and_name_from_google_api(self, mock_get):
mock_response = mock.Mock()
# Define response data from Google API
expected_dict = {
'kind': 'books#volumes',
'totalItems': 1,
'items': [
{
'kind': 'books#volume',
'id': 'IHxXBAAAQBAJ',
'etag': 'B3N9X8vAMWg',
'selfLink': 'https://www.googleapis.com/books/v1/volumes/IHxXBAAAQBAJ',
'volumeInfo': {
'title': "Alice's Adventures in Wonderland",
'authors': [
'Lewis Carroll'
]
}
}
]
}
# Define response data for my Mock object
mock_response.json.return_value = expected_dict
mock_response.status_code = 200
# Define response for the fake API
mock_get.return_value = mock_response
# Call the function
result = get_original_title_and_name(12345)
self.assertEqual(result, {
'title': "Alice's Adventures in Wonderland",
'author': 'Lewis Carroll'
})
mock_get.assert_called_once_with(GOOGLE_API_URL, params={
'key': GOOGLE_API_KEY,
'q': 'isbn:12345',
'printType': 'books',
})

Telegram bot API - Inline bot getting Error 400 while trying to answer inline query

I have a problem coding a bot in Python that works with the new inline mode.
The bot gets the query, and while trying to answer, it receives error 400.
Here is a sample of data sent by the bot at this time:
{
'inline_query_id': '287878416582808857',
'results': [
{
'type': 'article',
'title': 'Convertion',
'parse_mode': 'Markdown',
'id': '287878416582808857/0',
'message_text': 'blah blah'
}
]
}
I use requests library in to make requests, and here is the line that does it in the code:
requests.post(url = "https://api.telegram.org/bot%s%s" % (telegram_bot_token, "/answerInlineQuery"), data = myData)
With myData holding the data described in the sample.
Can you help me solve this, please?
I suspect it is because you haven't JSON-serialized the results parameter.
import json
results = [{'type': 'article',
'title': 'Convertion',
'parse_mode': 'Markdown',
'id': '287878416582808857/0',
'message_text': 'blah blah'}]
my_data = {
'inline_query_id': '287878416582808857',
'results': json.dumps(results),
}
requests.post(url="https://api.telegram.org/bot%s%s" % (telegram_bot_token, "/answerInlineQuery"),
params=my_data)
Note that I use params to supply the data.
I am getting the correct response after doing some POC. I am using java com.github.pengrad.
Below the code.
GetUpdatesResponse updatesResponse = bot.execute(new GetUpdates());
List updates = updatesResponse.updates();
for(Update update:updates){
InlineQuery inlineQuery = update.inlineQuery();
System.out.println(update);
System.out.println(inlineQuery);
System.out.println("----------------");
if(inlineQuery!=null) {
InlineQueryResult r1 = new InlineQueryResultPhoto("AgADBQADrqcxG5q8tQ0EKSz5JaZjzDWgvzIABL0Neit4ar9MsXYBAAEC", "https://api.telegram.org/file/bot230014106:AAGtWr8xUCqUy8HjSgSFrY3aCs4IZs00Omg/photo/file_1.jpg", "https://api.telegram.org/file/bot230014106:AAGtWr8xUCqUy8HjSgSFrY3aCs4IZs00Omg/photo/file_1.jpg");
BaseResponse baseResponse = bot.execute(new AnswerInlineQuery(inlineQuery.id(), r1)
.cacheTime(6000)
.isPersonal(true)
.nextOffset("offset")
.switchPmParameter("pmParam")
.switchPmText("pmText"));
System.out.println(baseResponse.isOk());
System.out.println(baseResponse.toString());
System.out.println(baseResponse.description());
}
}
Below the console output:
Update{update_id=465103212, message=null, edited_message=null, inline_query=InlineQuery{id='995145139265927135', from=User{id=231700283, first_name='Manabendra', last_name='Maji', username='null'}, location=null, query='hi', offset=''}, chosen_inline_result=null, callback_query=null}
InlineQuery{id='995145139265927135', from=User{id=231700283, first_name='Manabendra', last_name='Maji', username='null'}, location=null, query='hi', offset=''}
true
BaseResponse{ok=true, error_code=0, description='null'}
null
And I am getting proper response in my mobile telegram app also.

Convert test client data to JSON

I'm building an app and I want to make some tests. I need to convert the response data from the test client to JSON.
The app:
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
app = Flask(__name__, static_url_path="")
#app.route('/myapp/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': [task for task in tasks]})
if __name__ == '__main__':
app.run(debug=True)
The tests:
class MyTestCase(unittest.TestCase):
def setUp(self):
myapp.app.config['TESTING'] = True
self.app = myapp.app.test_client()
def test_empty_url(self):
response = self.app.get('/myapp/api/v1.0/tasks')
resp = json.loads(response.data)
print(resp)
if __name__ == '__main__':
unittest.main()
When I try to convert response.data to JSON, I get the following error:
TypeError: the JSON object must be str, not 'bytes'
How can I fix this error and get the JSON data?
Flask 1.0 adds the get_json method to the response object, similar to the request object. It handles parsing the response data as JSON, or raises an error if it can't.
data = response.get_json()
Prior to that, and prior to Python 3.6, json.loads expects text, but data is bytes. The response object provides the method get_data, with the as_text parameter to control this.
data = json.loads(response.get_data(as_text=True))

Categories

Resources