bottle.py render static file - python

I'm building a bottle.py app that grabs some data from MongoDB and renders it into a web page using pygal.
The code produces a Error: 500 Internal Server Error in my browser.
On the server, I see: Exception: TypeError('serve_static() takes exactly 1 argument (0 given)',).
My question: how do I correct the code to render the .svg file?
The code:
import sys
import bottle
from bottle import get, post, request, route, run, static_file
import pymongo
import json
import pygal
connection = pymongo.MongoClient("mongodb://localhost", safe=True)
#get('/chart')
def serve_static(chart):
db = connection.control
chart = db.chart
cursor = chart.find({}, {"num":1, "x":1, "_id":0})
data = []
for doc in cursor:
data.append(doc)
list = [int(i.get('x')) for i in data]
line = pygal.Line()
line.title = 'widget quality'
line.x_labels = map(str, range(1, 20))
line.add('quality measure', list)
line.render_to_file('chart.svg')
try:
return static_file(chart.svg, root='/home/johnk/Desktop/chart/',mimetype='image/svg+xml')
except:
return "<p>Yikes! Somethin' wrong!</p>"
bottle.debug(True)
bottle.run(host='localhost', port=8080)

You didn't give a parameter to the route, so the function doesn't get any.
What you probably want to do, is either:
#get('/<chart>')
def serve_static(chart):
...
If you want /myfile.svg to work, or:
#get('/chart/<chart>')
def serve_static(chart):
...
If you want /chart/myfile.svg to work.
If you just want to show the same SVG file every time, you can just leave off the parameter:
#get('/chart')
def serve_static():
...

Related

How do you create a new line in f-strings?

I'm trying to create new lines using python for the variable "profile" using the 'new_line' variable, but I haven't been successful.
The code below does not produce errors. It gives me all 3 strings in one line and I'd like to get 3 lines.
I would like the output to look like this with a new line for each string.
response: response
Lat/Lon: 1293.2312,123123.432
City: New York"
from flask import Flask
from flask import jsonify
import requests
import json
import os
app = Flask(__name__)
#app.route('/change/')
def he():
API_KEY = "API_KEY"
CITY_NAME = "oakland"
url = f"http://api.openweathermap.org/data/2.5/weather?q={CITY_NAME}&appid={API_KEY}"
response = requests.get(url).json()
new_line='\n'
profile = (
f"response: {response} ============== {new_line}"
f"Lat/Lon: {response['coord']} ========={new_line}"
f"City: {CITY_NAME}========{new_line}"
)
return profile
Try new_line='</br>'.
If you're viewing it in browser, it may interpret the page as badly formatted HTML and ignore line breaks and whitespaces, therefore you will need to use tags for that.

How Do I Return Many Values Back To Flask Page?

Hello can anyone help? I am quite new to practicing FLASK, and my python experience has been mainly around print statements than return values, and I am not sure why return is not acting as print would.
This is my draft of a flask app, e.g. app.py
import my_algorithms
from flask import Flask, request, render_template
app = Flask(__name__)
#app.route('/')
def my_form():
return render_template('my-form.html')
#app.route('/', methods=['POST'])
def my_form_post():
text = request.form['text']
return text + str(my_algorithms.receive_text_from_form(text))
I receive the variable text from a web page form.
If I do the following code my_algorithms.py as a print command and run in command line prompt or IDLE I get the results that I want, i.e. multiple news headlines
def receive_text_from_form(text):
news_keyword = text
# https://newsapi.org/docs/client-libraries/python
newsapi = NewsApiClient(api_key='0fb13acc3bc8480eafedb87afa941f7e')
# /v2/everything
data = newsapi.get_everything(q=news_keyword)
jdict = data.get('articles')
for row in jdict:
print(row['title'])
However if I do the code above as a return, I only get one result returned back to my flask app.py
def receive_text_from_form(text):
news_keyword = text
# https://newsapi.org/docs/client-libraries/python
newsapi = NewsApiClient(api_key='0fb13acc3bc8480eafedb87afa941f7e')
# /v2/everything
data = newsapi.get_everything(q=news_keyword)
jdict = data.get('articles')
for row in jdict:
return(row['title'])
Any ideas what I am doing wrong?
I have found the solution that I need here
How to use a return statement in a for loop?
def show_todo():
my_list = []
for key, value in cal.items():
my_list.append((value[0], key))
return my_list

Calling method from Flask Blueprint

I am trying to call a get method from my main.py from another python file and I am using flask and blueprints.
I have 2 files: main.py and product.py.
Based on the documentation, I thought after we have done an import, we can then call the method.
In my product.py
import os
from flask import Blueprint, Flask, render_template, request, jsonify
import stripe
import json
import ast
product = Blueprint('product',__name__)
#product.route('/getallproducts', methods=['GET'])
def get_products ():
myList = ["price_1GqLlRFswqvdSoNHi27H2wLV","price_1GqLiAFswqvdSoNHjv4R6scY","price_1GqY8eFswqvdSoNHVSRzEQdn","price_1GqYAcFswqvdSoNH1uReU4kN"]
result =[]
for i in myList:
priceobj = stripe.Price.retrieve(i)
product= stripe.Product.retrieve(priceobj.product)
data = product
data["price"] = priceobj.unit_amount/100
data["priceid"] =i
result.append(data)
return result
In my main.py i have
import os
from flask import Blueprint, Flask, render_template, request, jsonify
import stripe
import json
import ast
from product import product
stripe.api_key = stripe_keys['secret_key']
app=Flask(__name__,template_folder='../client', static_url_path='', static_folder='../client')
app.register_blueprint(product)
#app.route('/', methods=['GET'])
def main():
result =product.get_products()
return render_template('index.html', data=result)
I tried to do product.get_products() but it complained that no such methods exist.
Is there something which I am missing out on as I thought this was the way we can use blueprints?
You're likely getting the invalid method because you're not importing the function get_products() but a variable from your file that uses it. Try changing the import line in your main.py to from product import product, get_products.
Ideally get_products should be a separate method somewhere instead of calling the Flask route method, in this case, you could start off with product.py itself.
def get_all_products():
myList = ["price_1GqLlRFswqvdSoNHi27H2wLV","price_1GqLiAFswqvdSoNHjv4R6scY","price_1GqY8eFswqvdSoNHVSRzEQdn","price_1GqYAcFswqvdSoNH1uReU4kN"]
result =[]
for i in myList:
priceobj = stripe.Price.retrieve(i)
product= stripe.Product.retrieve(priceobj.product)
data = product
data["price"] = priceobj.unit_amount/100
data["priceid"] =i
result.append(data)
and you could simply refer to this method from wherever you need to get all the products.
P.S: Leandro's answer works perfectly if you don't want to follow this method

Stuck implementing elasticsearch in a flask app

I'm currently working through the Flask Mega-Tutorial (Part XVI) and have gotten stuck implementing elasticsearch. Specifically, I get this error when running the following from my flask shell command line:
from app.search import add_to_index, remove_from_index, query_index
>>> for post in Post.query.all():
... add_to_index('posts', post)
AttributeError: module 'flask.app' has no attribute 'elasticsearch'
I should mention that I did not implement the app restructuring from the previous lesson to use blueprints. Here's what my files look like:
__init__.py:
#
from elasticsearch import Elasticsearch
app.elasticsearch = Elasticsearch([app.config['ELASTICSEARCH_URL']]) \
if app.config['ELASTICSEARCH_URL'] else None
config.py:
class Config(object):
#
ELASTICSEARCH_URL = 'http://localhost:9200'
search.py:
from flask import app
def add_to_index(index, model):
if not app.elasticsearch:
return
payload = {}
for field in model.__searchable__:
payload[field] = getattr(model, field)
app.elasticsearch.index(index=index, id=model.id, body=payload)
def remove_from_index(index, model):
if not app.elasticsearch:
return
app.elasticsearch.delete(index=index, id=model.id)
def query_index(index, query, page, per_page):
if not app.elasticsearch:
return [], 0
search = app.elasticsearch.search(
index=index,
body={'query': {'multi_match': {'query': query, 'fields': ['*']}},
'from': (page - 1) * per_page, 'size': per_page})
ids = [int(hit['_id']) for hit in search['hits']['hits']]
return ids, search['hits']['total']['value']
I think I'm not importing elasticsearch correctly into search.py but I'm not sure how to represent it given that I didn't do the restructuring in the last lesson. Any ideas?
The correct way to write it in the search.py file should be from flask import current_app
Not sure if you got this working, but the way I implemented it was by still using app.elasticsearch but instead within search.py do:
from app import app

How to initialize session data in automated test? (python 2.7, webpy, nosetests)

I have a web application that uses session data to determine what to do next.
The application is running in the browser and does what it shall do.
I'd like to write an automated test to have that knowledge for future projects.
The last hours I failed miserable to initialize (and hand over) session data within my test. Also I couldn't find the answer for such a basic case on the web.
However, here is the app code:
import web
from project import code
urls = (
"/page", "/Page",
"/", "Index"
)
app = web.application(urls, globals())
# Little hack so that debug mode works with sessions.
if web.config.get('_session') is None:
store = web.session.DiskStore('sessions')
session = web.session.Session(app, store, initializer={'data':None})
web.config._session = session
else:
session = web.config._session
render = web.template.render('templates/', base="layout")
class Index():
def GET(self):
# This is used to "setup" the session with starting values.
# code.START contains a dict "whatnext"
# that assigns keywords to information what to do next.
# It also contains a attribute "name"
session.data = code.START
web.seeother("/page")
class Page(object):
def GET(self):
if session.data:
return render.page(data=session.data)
else:
# This is here in case someone puts "/page" directly as URL.
# wrong_page just has a link to "/" and everything will be fine
# from there on.
return render.wrong_page()
def POST(self):
form = web.input(action=None)
if form.action in session.data.whatnext:
# The method .go returns what to do next according to the
# keywords.
session.data = session.data.go(form.action)
web.seeother("/page")
if __name__ == "__main__":
app.run()
The code itself is not the scope of the question, but I can provide it, if necessary.
However, page.html looks like this
$def with (data)
Something something dependend on the data.
$if data.name == "finished":
<p> Again? </p>
$else:
<p>
<form action="/page" method="POST">
<input type="text" name="action"> <input type="SUBMIT">
</form>
</p>
In the test the following is used:
from nose.tools import *
import re
def assert_response(resp, contains=None, status="200"):
assert status in resp.status, "Excpected response %r not in %r" % (status,
resp.status)
if status == "200":
assert resp.data, "Response data is empty."
if contains:
assert contains in resp.data, "Response does not contain %r" % contains
and here is the actual test:
from nose.tools import *
from bin.app import app # the code above
from tests.tools import assert_response
def test_redirect():
# Check that we get a 303 on the / URL
resp = app.request("/")
assert_response(resp, status="303")
def test_wrong_page():
# test the first GET request to /game
resp = app.request("/page")
assert_response(resp, contains="Go here instead")
def test_page
# test that we get expected values
what_to_do = {'action' : 'foo'}
resp = app.request("/page", method="POST", data=what_to_do)
assert_response(resp, contains="something specific according to foo")
The first two tests work as expected.
The third test is not working and I think it is because /page requires session.data to function.
I am getting as ouptut:
Traceback (most recent call last):
File "/.../nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/.../tests/app_tests.py", line 19, in test_page
assert_response(resp, contains="something specific according to foo")
File "/.../tests/tools.py", line 17, in assert_response
resp.status)
AssertionError: Excpected response '200' not in '500 Internal Server Error'
Since I don't know how to initialize the session/session data in the test, my question is: how can I do that, that the automated test can run with the give session information.
You don't need to initialise session in your test, since when you make app.request() call, your app will auto init session for you. The issue here is you don't maintain session id in your test ( your test is like a client as any browser ).
The solution is that when you make first app.request(), record the session id in the response headers. Then supply with the session id when you make subsequent app.request()
Here is my code:
First I make a helper function in tests/tools.py to extract the session id from response header:
def get_session_id(resp):
cookies_str = resp.headers['Set-Cookie']
if cookies_str:
for kv in cookies_str.split(';'):
if 'webpy_session_id=' in kv:
return kv
then write test as:
def test_session():
resp = app.request('/')
session_id = get_session_id(resp)
resp1 = app.request('/game', headers={'Cookie':session_id})
assert_response(resp1, status='200', contains='Central Corridor')

Categories