I'm making a doc to pdf converter program, and i have occured an error as below:
Im an begginer in programming and i dont know how to fix it, below i paste python code:
from flask import Flask
from flask import request, render_template, redirect, url_for, send_file
import os
from typing import Tuple
from docx2pdf import convert
#from tkinter import Tk,messagebox
#from tkinter import _tkinter
UPLOADER_FOLDER = ''
app = Flask(__name__)
app.config['UPLOADER_FOLDER'] = UPLOADER_FOLDER
#app.route('/')
#app.route('/index', methods=['GET', 'POST'])
def index():
if request.method == "POST":
def docx2pdf(input_file: str, output_file: str, pages: Tuple = None):
if pages:
pages = [int(i) for i in list(pages) if i.isnumeric()]
result = convert(docx_file=input_file, pdf_file=output_file, pages=pages)
summary = {
"File": input_file, "Pages": str(pages), "Output File": output_file
}
print("\n".join("{}:{}".format(i, j) for i, j in summary.items()))
return result
file = request.files['filename']
if file.filename!= '':
file.save(os.path.join(app.config['UPLOADER_FOLDER'], file.filename))
input_file = file.filename
output_file = r"hello.pdf"
docx2pdf(input_file, output_file)
pdf = input_file.split(".")[0]+".pdf"
print(pdf)
lis=pdf.replace(" ", "=")
return render_template("docx.html", variable=lis)
return render_template("index.html")
#app.route('/docx', methods=['GET', 'POST'])
def docx():
if request.method=="POST":
lis = request.form.get('filename', None)
lis = lis.replace("=", " ")
return send_file(lis, as_attachment=True)
return render_template("index.html")
if __name__=="__main__":
app.debug = True
app.run()
There are two templates called index.html and docx.html in Templates directory, this is how it looks in Pycharm:
Does anyone have an idea where i made an mistake? Thanks
You are passing parameters into the convert method like this:
result = convert(docx_file=input_file, pdf_file=output_file, pages=pages)
But the docx2pdf convert method is defined like this:
def convert(input_path, output_path=None, keep_active=False):
as you can see, there is no parameter called docx_file, so you get the error that this parameter was unexpected. To fix this, either get rid of the names altogether:
result = convert(input_file, output_file)
or use the correct parameter names:
result = convert(input_path=input_file, output_path=output_file)
I also see that you have passed a variable called pages. I'm not familiar with this library so I can't be 100% sure, but I didn't find a convert function with a parameter called pages anywhere.
Since you are in PyCharm, right click on the convert function and select Go to -> Declaration to see the method. You should be able to see what the function does. There is limited documentation on this library so you will have to rely on reading the code to understand.
Related
When I run the following code snippet, I get a URL not found error. Does anyone know what is wrong with it? Thanks.
from flask import *
app = Flask(__name__)
app.route('/set')
def setcookie():
res = make_response("<h2>The cookie is set</h2>")
res.set_cookie('framework', 'flask')
return res
if __name__=="__main__":
app.run(debug=True)
Add an # to your app.route like below:
#app.route('/set')
def setcookie():
res = make_response("<h2>The cookie is set</h2>")
res.set_cookie('framework', 'flask')
return res
This question already has an answer here:
Capture arbitrary path in Flask route
(1 answer)
Closed 1 year ago.
I am trying to pass a website as a parameter. It works if the website does not have a "/" in it. For example: http://192.168.1.156:2434/www.cookinglight.com scrapes cooking light for all the images on it's page; however, if I pass in http://192.168.1.156:2434/https://www.cookinglight.com/recipes/chicken-apple-butternut-squash-soup then an I get an invalid response. Here is my current code:
import json
from flask import Flask, render_template
from imagescraper import image_scraper
app = Flask(__name__)
#app.route("/", methods = ['GET'])
def home():
return render_template('index.html')
#app.route("/<site>", methods = ['GET'])
def get_image(site):
return json.dumps(image_scraper(site))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=2434, debug=True)
import requests
from bs4 import BeautifulSoup
def image_scraper(site):
"""scrapes user inputed url for all images on a website and
:param http url ex. https://www.cookinglight.com
:return dictionary key:alt text; value: source link"""
search = site.strip()
search = search.replace(' ', '+')
website = 'https://' + search
response = requests.get(website)
soup = BeautifulSoup(response.text, 'html.parser')
img_tags = soup.find_all('img')
# create dictionary to add image alt tag and source link
images = {}
for img in img_tags:
try:
name = img['alt']
link = img['src']
images[name] = link
except:
pass
return images
I tried urrllib but did not have any success. Any help would be greatly appreciated! I am a student so still learning!!
UPDATE:
I believe this is the issue as described in the stackoverflow post
Need to allow encoded slashes on Apache
Flask uses / as separate between arguments in url - so you can create route("/<arg1>/<arg2>/<arg3>") (or popular in blogs route("/<year>/<month>/<day>")) and you can get values in variables arg1, arg2, arg3 - and when you try to use your url with / then it also treat it as "/<arg1>/<arg2>/<arg3>" and it tries to find route like route("/<arg1>/<arg2>/<arg3>") and it can't find it and it gives error 404.
route("/<site>") can match only string without /. site is only variable name - it doesn't mean that it will treat it as url with /
If you want to use / as part of single argument, not as separator between arguments, then you need <path:site>.
from flask import Flask
app = Flask(__name__)
#app.route("/")
def home():
return "Hello World"
#app.route("/<path:site>")
def get_image(site):
return f"OK: {site}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=2434)#, debug=True)
See also Variable Rules
EDIT:
It has nothing to do with issue. Flask was specially created to use / as special char to separate values.
To clarify the question, using a Flask URL converter how would one parse the json at this website by entering a url such as: https://ds-med-cabinet.herokuapp.com/strainjson/Purple-Kush so that when the user visits the /Purple-Kush website only that json object is displayed? This website is for building an API for educational purposes only so I would appreciate any ideas pertaining to parsing the entire json by URL input mostly likely using Flask URL converter or any other practical method. Thank you very much for your time and consideration. Here is the code I have tried, as a mock up of the Flask URL Converter documentation:
# Directory.py
# Import
from os import path
import pandas as pd
from flask import Blueprint, render_template
from werkzeug.routing import BaseConverter
# Make Blueprint for __init__.py
Directory = Blueprint("Directory", __name__)
# Import Leafly csv
file_name = path.join(path.dirname(__file__), "Leafly.csv")
df = pd.read_csv(file_name)
strains = df['Strain']
# Custom converter
class ListConverter(BaseConverter):
def to_python(self, value):
return value.split('+')
def to_url(self, values):
return '+'.join(BaseConverter.to_url(value)
for value in values)
# Flask Url-converter
#Directory.route('/<strain>')
def strain_url(strain):
"""Show the json object for the given strain."""
strain = []
for strain in strains:
strain
return render_template('json.html', strain=strain)
# __init__.py
# Imports
from flask import Flask
from web_app.routes.Directory import Directory, ListConverter
from web_app.routes.GET_PUT_API import GET_PUT_API
# Create Flask app
def create_app():
app = Flask(__name__)
app.register_blueprint(Directory)
app.register_blueprint(GET_PUT_API)
app.url_map.converters['list'] = ListConverter
return app
if __name__ == "__main__":
my_app = create_app()
my_app.run(debug=True)
the strains in the for loop is a list of every strain from the csv version of the data, and the json.html being rendered is the html file of json objects that are being rendered at this website. This code and /whateveristypedintheurl just renders all of the data at the website shared (because the html file is already full of json objects and nothing is getting parsed). Thanks again for checking this out.
Ps. If trying to replicate this by creating a Flask App, you can find the csv here as cannabis.csv (I switched the named to Leafly.csv) and you can convert the df to json by using the following code:
# dftojson.py
# Imports
from os import path
import csv
import json
file_path = r'C:\Users\johnj\OneDrive\Documents\Lambda\BuildWeek3\data-science\cannabis.csv'
csvfile = open(file_path, encoding="utf8")
jsonfile = open('cannabis.json', 'w')
fieldnames = ("Strain", "Type", "Rating", "Effects", "Flavor"," Description")
reader = csv.DictReader(csvfile, fieldnames)
for row in reader:
json.dump(row, jsonfile)
jsonfile.write('\n')
I copied and pasted the json from cannabis.json into a new json.html file (or just change the file extension) and then added the route like so:
# Directory.py
# Strain JSON Page
#Directory.route("/strainjson")
def df():
return render_template("json.html")
This is what I came up with. I got built off this article: Medium article on using CSV with Flask. For example locally in your URL you can type '
5Th-Element' and that should display is JSON format. Adding jsonify on the return will help with API issues.
import csv
from os import path
from flask import Flask, render_template, Blueprint, jsonify, json
# Make Blueprint for __init__.py
ParseURL = Blueprint("ParseURL", __name__)
# Import Leafly csv
file_name = path.join(path.dirname(__file__), "Leafly.csv")
# route to display single dictionary list item as JSON object
#APP.route('/<strain>')
def strain_url(strain):
'''
Parameters: name of strain from database as a string.
For loops the cannabis.csv file, creating a dictionary.
Returning only the strain that was given as a parameter.
'''
with open('cannabis.csv') as csv_file:
data = csv.reader(csv_file, delimiter=',')
dict_strain = {}
for row in data:
if row[0] == strain:
dict_strain = {
"strain": row[0],
"type": row[1],
"rating": row[2],
"effects": row[3],
"flavor": row[4],
"description": row[5]
}
break
return jsonify(dict_strain)
if i understand your concern very well, there's 2 parts:
create Custom URL Converter (refer to tutorial and doc ) to get a 2 separate lists of columns and terms following those patterns:
(venv) C:\Python37\myapps\flask\cannabis>flask routes
Endpoint Methods Rule
------------------ ------- -----------------------------
api.index GET /api/
api.index GET /api/<list:cols>
api.index GET /api/<list:cols>/<list:terms>
with pandas reading a cannabis.csv file (download) you filter data depending on what you get as list of columns combined with an optional list of terms
for the moment the part 1 is SOLVED and i'm little bit stuck with pandas
i've made my best and this how i setup a working Flask app demo:
cannabis
.. cannabis
.... api
__init__.py
views.py
.... errors
__init__.py
views.py
.... __init__.py
.... cannabis.py
.... utils.py
.. tests
.. venv
.. cannabis.csv
.. .flaskenv
/.flaskenv
FLASK_APP=cannabis:create_app()
FLASK_ENV=development
FLASK_DEBUG=0
/cannabis/__init __.py
from .cannabis import create_app
/cannabis/cannabis.py
from flask import Flask
from .utils import ListConverter
def create_app():
"""Create a Flask application using the app factory pattern."""
app = Flask(__name__)
app.url_map.converters['list'] = ListConverter
"""Register blueprints."""
from .errors import bp as errors_bp
app.register_blueprint(errors_bp)
from .api import bp as api_bp
app.register_blueprint(api_bp, url_prefix='/api')
return app
/cannabis/utils.py
from werkzeug.routing import BaseConverter
class ListConverter(BaseConverter):
def to_python(self, values):
return values.split('+')
def to_url(self, values):
return '+'.join(value for value in values)
for to_url() function i think you don't need BaseConverter.to_url(value) otherwise you'll stuck with error:
[..]
File "C:\Python37\myapps\flask\cannabis\cannabis\utils.py", line 11, in <genexpr>
return '+'.join(BaseConverter.to_url(term) for term in terms)
TypeError: to_url() missing 1 required positional argument: 'value'
/cannabis/api/__init __.py
from .views import bp
/cannabis/api/views.py
from flask import Blueprint, jsonify
bp = Blueprint('api', __name__)
#bp.route('/', defaults={'cols': [], 'terms': []})
#bp.route('/<list:cols>', defaults={'terms': []})
#bp.route('/<list:cols>/<list:terms>')
def index(cols, terms):
cols_a = []
for col in cols:
if col: cols_a.append(col)
terms_a = []
for term in terms:
if term: terms_a.append(term)
# we need list of cols so we can filter data for list of terms within the list of cols
if not cols_a and terms_a:
return jsonify(message="Please choose at least one column")
kws = []
kws.append(cols_a)
kws.append(terms_a)
# form the moment just make sure we get the 2 lists/arrays
# then filter data with pandas using the combination of 2 lists then convert to json and return result
# .. WORKING ..
return jsonify(message="api index page", kws=kws)
/cannabis/errors/__init __.py
from .views import bp
/cannabis/errors/views.py
from flask import Blueprint, jsonify
from werkzeug.exceptions import HTTPException
bp = Blueprint('errors', __name__)
#bp.app_errorhandler(HTTPException)
def handle_exception(e):
return jsonify(code=e.code,
name=e.name,
description=e.description)
now you can run the Flask app and make some tests with different URLS:
no given columns and terms lists, default : return non-filtered data
http://localhost:5000/api/
{"kws":[[],[]],"message":"api index page"}
1 column is given , return data with only the give column
http://localhost:5000/api/Strain
{"kws":[["Strain"],[]],"message":"api index page"}
N columns are given , return data with only the give N columns
http://localhost:5000/api/Strain+Rating
{"kws":[["Strain","Rating"],[]],"message":"api index page"}
notice this url with extra optional '+', it works.
http://localhost:5000/api/Strain++++Rating++Flavor
{"kws":[["Strain","Rating","Flavor"],[]],"message":"api index page"}
N columns and M terms are given , return data with only the give N columns and filtered rows that contains M terms (CHECK how to use this with pandas)
http://localhost:5000/api/Strain++Rating++Flavor/Purple-Kush++5+++Blackberry+++
{"kws":[["Strain","Rating","Flavor"],["Purple-Kush","5" "Blackberry"]],"message":"api index page"}
notice this url when no column is given.
http://localhost:5000/api/+/Purple-Kush
{"message":"Please add at least one column"}
other url, return non-filtered data
http://localhost:5000/api/+++/+++
{"kws":[[],[]],"message":"api page"}
[..]
i'll update my code once i finish implementing pandas..
other resources:
flask jsonify vs python json dumps : json.dumps vs flask.jsonify
I'm writing a script to collect the emails of those users that didn't receive an email confirmation email and resend it to them. The script works obviously outside of flask app context. I would like to use url_for() but can't get it right.
def resend(self, csv_path):
self.ctx.push()
with open(csv_path) as csv_file:
csv_reader = csv.reader(csv_file)
for row in csv_reader:
email = row[0]
url_token = AccountAdmin.generate_confirmation_token(email)
confirm_url = url_for('confirm_email', token=url_token, _external=True)
...
self.ctx.pop()
The first thing I had to do was to set SERVER_NAME in config. But then I get this error message:
werkzeug.routing.BuildError: Could not build url for endpoint
'confirm_email' with values ['token']. Did you mean 'static' instead?
This is how it's defined, but I don't think it can even find this, because it's not registered when ran as script:
app.add_url_rule('/v5/confirm_email/<token>', view_func=ConfirmEmailV5.as_view('confirm_email'))
Is there a way to salvage url_for() or do I have to build my own url?
Thanks
It is much easier and proper to get the URL from the application context.
You can either import the application and manually push context with app_context
https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context
from flask import url_for
from whereyoudefineapp import application
application.config['SERVER_NAME'] = 'example.org'
with application.app_context():
url_for('yourblueprint.yourpage')
Or you can redefine your application and register the wanted blueprint.
from flask import Flask, url_for
from whereyoudefineyourblueprint import myblueprint
application = Flask(__name__)
application.config['SERVER_NAME'] = 'example.org'
application.register_blueprint(myblueprint)
with application.app_context():
url_for('myblueprint.mypage')
We can also imagine different ways to do it without the application, but I don't see any adequate / proper solution.
Despite everything, I will still suggest this dirty solution.
Let's say you have the following blueprint with the following routes inside routes.py.
from flask import Blueprint
frontend = Blueprint('frontend', __name__)
#frontend.route('/mypage')
def mypage():
return 'Hello'
#frontend.route('/some/other/page')
def someotherpage():
return 'Hi'
#frontend.route('/wow/<a>')
def wow(a):
return f'Hi {a}'
You could use the library inspect to get the source code and then parse it in order to build the URL.
import inspect
import re
BASE_URL = "https://example.org"
class FailToGetUrlException(Exception):
pass
def get_url(function, complete_url=True):
source = inspect.getsource(function)
lines = source.split("\n")
for line in lines:
r = re.match(r'^\#[a-zA-Z]+\.route\((["\'])([^\'"]+)\1', line)
if r:
if complete_url:
return BASE_URL + r.group(2)
else:
return r.group(2)
raise FailToGetUrlException
from routes import *
print(get_url(mypage))
print(get_url(someotherpage))
print(get_url(wow).replace('<a>', '456'))
Output:
https://example.org/mypage
https://example.org/some/other/page
https://example.org/wow/456
I'm pretty new to Pandas and Flask, trying to leverage it to output a summarised version of a CSV containing survey feedback that I can email to users periodically.
As a standalone function, it works so long as I give it an input file that's specified (e.g. 'users/sample.csv') and outfile but when running as part of an application and using an uploaded html file, it fails with
TypeError: csuppfb() takes at least 2 arguments (0 given)
Essentially I want to pass the uploaded file to the function, and have Pandas do its thing but it doesn't get that far. Below is the code:
import re,os
import beatbox
import pandas as pd
import numpy as np
import argparse
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML
from os.path import isfile,join
from flask import Flask, request, redirect, url_for,render_template,json as fjson,send_from_directory
from werkzeug import secure_filename
from mapping import Autotagging,Manualtagging
from defs import *
UPLOAD_FOLDER = './uploads'
PIVOT_FOLDER = './pivot'
ALLOWED_EXTENSIONS = set(['csv'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['PIVOT_FOLDER']= PIVOT_FOLDER
#app.route('/feedback',methods=['GET', 'POST'])
def feedback():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = randomword(6)+'_'+secure_filename(file.filename)
file.save(os.path.join(app.config['PIVOT_FOLDER'], filename))
return redirect(url_for('csuppfb',df=filename))
return render_template('mappingtest.html')
#app.route('/csuppfb', methods=['POST','GET'])
def csuppfb(df,infile, index_list=["Case Owner","Case Number","Support Survey - Service rating"], value_list = ["Age (Hours)"]):
"""
Creating a pivot table from the raw dataframe and returning it as a dataframe
"""
table = pd.pivot_table(df, index=index_list, values = value_list,
aggfunc=[np.sum,np.mean], fill_value=0)
return table
def get_summary_stats(df, product):
"""
Get a stats summary
"""
results.append(df[df["Support Survey - Service rating"]==product]["Closed"].mean())
results.append(df[df["Support Survey - Service rating"]==product]["Age (Hours)"].mean())
return results
def dataform(df):
"""
Take the dataframe and output it in html to output a pdf report or display on a web page
"""
df = pd.read_csv(filename)
csuppreport = pivot_table(df,filename)
agent_df = []
for agent in csuppreport.index.get_level_values(0).unique():
agent_df.append([agent, csuppreport.xs(agent, level=0).to_html()])
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template("csupp.html")
template_vars={"title": "CSUPP FB REPORT",
"Excellent": get_summary_stats(df,"Excellent"),
"Good": get_summary_stats(df,"Good"),
"csupp_pivot_table": csuppreport.to_html(),
"agent_detail": agent_df}
html_out = template.render(template_vars)
HTML(string=html_out).write_pdf(args.outfile.name,stylesheets=["style.css"])
return render_template('csupp.html')
What's the best way to have the file I've uploaded be used as the dataframe argument in
def csuppfb(df,infile...
?
Any advice would be very much appreciated. I've a feeling it's something glaringly obvious I'm missing.
you need to use the args object from request which contains all the url params
http://flask.pocoo.org/docs/0.10/quickstart/#the-request-object
See this basic example:
#app.route('/csuppfb', methods=['POST','GET'])
def csuppfb():
if request.args['df'] :
df = request.args['df']
#Do your panda stuff here. Instead of returning the filename :D
return str(df)
else :
return 'nofile'