How to allow users to download an excel file using python/Flask? - python

I have a method that takes in an excel file, read the excel file, extracts data from it and save. The issue I am having now is how to write a method that returns the excel file and allow users to download it from the frontend.
This is the main file
class UploadFileForm(FlaskForm):
file = FileField('File', validators=[InputRequired()])
submit = SubmitField('Download File')
#app.route('/', methods=['GET', 'POST'])
def home_api():
form = UploadFileForm()
if form.validate_on_submit():
file = form.file.data
file.save(os.path.join(os.path.abspath(os.path.dirname(_file_)),app.config['UPLOAD_FOLDER'],secure_filename(file.filename)))
process_xl(os.path.join(os.path.abspath(os.path.dirname(_file_)),app.config['UPLOAD_FOLDER'],secure_filename(file.filename)))
download_file(os.path.join(os.path.abspath(os.path.dirname(_file_)),app.config['UPLOAD_FOLDER'],secure_filename(file.filename)))
return 'File has been uploaded'
return render_template('index.html', form=form)
This is the service that does the extraction and little manipulation.
from flask import send_file
import requests
from openpyxl import load_workbook
def weather(city):
url = "https://community-open-weather-map.p.rapidapi.com/weather"
querystring = {"q":f"{city}","lat":"0","lon":"0","callback":"test","id":"2172797","lang":"null","units":"imperial","mode":"HTML"}
headers = {
"X-RapidAPI-Key": "0a109dce92msh070cc32fb93c003p1e4e8djsnef8cf195fed1",
"X-RapidAPI-Host": "community-open-weather-map.p.rapidapi.com"
}
response = requests.request("GET", url, headers=headers, params=querystring)
return response.text
def process_xl(filename):
wb = load_workbook(filename=filename)
sheet_1 = wb['Sheet1']
sheet_2 = wb['Sheet2']
city_1 = sheet_1['A1'].value
city_2 = sheet_2['']
Based on the requirement, I need a function that will return the excel file which I can call in the main method. I have tried returning the the excel file it was not working

You only need to create a route that sends the file to the client
here is how
import os
from flask import send_file
#app.route('/downlaod')
def downloadExcel():
root_path = "path_to_your_file"
filename = "your_file_name.xlsx"
file_path = os.path.join(root_path, filename)
return send_file(file_path)
You function should save the file.
def process_xl(filename):
wb = load_workbook(filename=filename)
sheet_1 = wb['Sheet1']
sheet_2 = wb['Sheet2']
city_1 = sheet_1['A1'].value
city_2 = sheet_2['']
wb.save(filename)

This line solved my problem
return send_from_directory('directory-path', file.filename)
This is the full code here
from flask import send_from_directory
class UploadFileForm(FlaskForm):
file = FileField('File', validators=[InputRequired()])
submit = SubmitField('Download File')
#app.route('/', methods=['GET', 'POST'])
def home_api():
form = UploadFileForm()
if form.validate_on_submit():
file = form.file.data
file.save(os.path.join(os.path.abspath(os.path.dirname(_file_)),app.config['UPLOAD_FOLDER'],secure_filename(file.filename)))
process_xl(os.path.join(os.path.abspath(os.path.dirname(_file_)),app.config['UPLOAD_FOLDER'],secure_filename(file.filename)))
download_file(os.path.join(os.path.abspath(os.path.dirname(_file_)),app.config['UPLOAD_FOLDER'],secure_filename(file.filename)))
return send_from_directory('directory-path', file.filename)
return render_template('index.html', form=form)

Related

When Trying to access the excel file in post request, getting this error "MultiValueDictKeyError(key)"

CODE
import openpyxl
class FileView(APIView):
parser_classes = (MultiPartParser, FormParser)
permission_classes = [IsAdminUser]
def post(self, request, *args, **kwargs):
'''
API user to import hospital's adimission data in bulk, using xls file
'''
# getting the excel file
# try:
# excel_file = request.FILES["excel_file"]
# except:
# return Response({'err_msg': 'bad request'}, status=status.HTTP_400_BAD_REQUEST)
excel_file = request.FILES["excel_file"]
wb = openpyxl.load_workbook(excel_file)
# getting a particular sheet by name out of many sheets
worksheet = wb['Sheet1']
# excel_data = []
print(worksheet)
Error
File "G:\office\medicover\medicover_bloodbank_django\file_app\views.py", line 27, in post
excel_file = request.FILES["excel_file"]
File "G:\office\medicover\medicover_bloodbank_django\venv\lib\site-packages\django\utils\datastructures.py", line 78, in getitem
raise MultiValueDictKeyError(key)
django.utils.datastructures.MultiValueDictKeyError: 'excel_file'
[07/Jul/2022 06:50:29] "POST /file/upload/ HTTP/1.1" 500 103875
The error was a result of this line of code:-
excel_file = request.FILES["excel_file"]
Because I was passing "excel_file". However, this should be a "file" like this:-
excel_file = request.FILES["file"]
instead of providing a "key", I was providing the value "excel_file".
So, by just changing "excel_file" to "file" I got the result.

How does one write Python code that merges 2 files created using wkhtmltopdf into 1 pdf file using pypdf2

I have an application with the back-end written in Python that converts html files to pdf files. To do this it implements wkhtmltopdf (https://wkhtmltopdf.org/). It currently works perfectly for creating a single PDF file from an html file and outputs that to the user.
However, I need to be able to create multiple separate PDF files and then merge the files together into a single PDF.
I have been trying to do this using Pypdf2 with the PdfFileMerger() function (https://pythonhosted.org/PyPDF2/PdfFileMerger.html) and haven't been able to do it. I keep getting 'bytes' object has no attribute 'seek'
Here is my current code:
def multi_test_sheet(request, equipment_id):
if not request.user.is_authenticated:
return render(request, "jobs/login.html", {"message": None})
from io import BytesIO
from PyPDF2 import PdfFileReader, PdfFileMerger
if not request.user.is_authenticated:
return render(request, "jobs/login.html", {"message": None})
equipment = Equipment.objects.filter(pk=equipment_id).first()
if not job:
raise Http404("test sheet error. Error code: get job failed")
pdf_write = PdfFileWriter()
user_properties=UserProperties.objects.get(user=request.user)
context = {
"equipment": equipment,
"job": equipment.equipments,
"test_sheet": equipment.sheet_eq,
"user_properties": user_properties,
"now": datetime.now().strftime("%b-%d-%Y %H:%M"),
"now_date": datetime.now().date()
}
html_sheet = render_to_string('jobs/test_sheet_gear1.html', context)
html_sheet2 = render_to_string('jobs/test_sheet_gear2.html', context)
pdf_content1 = pdfkit.from_string(html_sheet, None)
pdf_content2 = pdfkit.from_string(html_sheet2, None)
pdfadder = PdfFileMerger(strict=False)
pdfadder.append(pdf_content1)
pdfadder.append(pdf_content2)
pdf_adder.write("combined_sheets.pdf")
response = HttpResponse(pdf_adder, content_type="application/pdf")
response["Content-Disposition"] = f"filename={equipment.site_id}.pdf"
return response
I resolved this by hiring someone. The problem was that the objects being passed into the PyPDF2 function called PdfFileMerger() were not being recognized as pdf objects.
To resolve that, save the files (I place them in a folder called interim) using the second argument from the pdfkit.from_string() function, then assign the newly created files to independent variables using open() function, and finally proceed with the merging function by merging those variables.
def multi_test_sheet(request, equipment_id):
if not request.user.is_authenticated:
return render(request, "jobs/login.html", {"message": None})
from io import BytesIO
from PyPDF2 import PdfFileReader, PdfFileMerger
if not request.user.is_authenticated:
return render(request, "jobs/login.html", {"message": None})
equipment = Equipment.objects.filter(pk=equipment_id).first()
if not job:
raise Http404("test sheet error. Error code: get job failed")
page_quantity = 2 #temporary value for a property that will be added to either equipment or test sheet model
pdf_file_object = BytesIO()
stream = BytesIO()
pdf_write = PdfFileWriter()
user_properties=UserProperties.objects.get(user=request.user)
today = datetime.now()
now=today.strftime("%b-%d-%Y %H:%M")
now_date = today.date()
context = {
"equipment": equipment,
"job": equipment.equipments,
"test_sheet": equipment.sheet_eq,
"user_properties": user_properties,
"now": now,
"now_date": now_date
}
html_sheet = render_to_string('jobs/test_sheet_gear1.html', context)
html_sheet2 = render_to_string('jobs/test_sheet_gear2.html', context)
pdf_content1 = pdfkit.from_string(html_sheet, 'interm/test_sheet_gear1.pdf')
pdf_content2 = pdfkit.from_string(html_sheet2, 'interm/test_sheet_gear2.pdf')
pdfadder = PdfFileMerger(strict=False)
pdf1_v=PdfFileReader(open('interm/test_sheet_gear1.pdf', 'rb'))
pdf2_v=PdfFileReader(open('interm/test_sheet_gear2.pdf', 'rb'))
pdfadder.append(pdf1_v, import_bookmarks=False)
pdfadder.append(pdf2_v, import_bookmarks=False)
pdfadder.write('interm/'+str(user_properties.pk)+'combined_sheets.pdf')
output_file = open('interm/combined_sheets.pdf', 'rb')
response = HttpResponse(output_file, content_type="application/pdf")
response["Content-Disposition"] = f"filename={equipment.site_id}.pdf"
return response

How to send a file to post method that accepts form data in python flask, and also how to retrieve file and other fields in the called method?

I'm trying something like this.
Caller code -
files = {'file': open(file_name, 'rb').read()}
response = requests.post(url, headers=headers, data=metadata, files=files)
Called method code-
metadata = {}
for key, value in request.form.items():
metadata[key] = value
print(metadata)
print(type(metadata))
print("Request data")
print(request.data)
print("Files")
print(request.files)
print(request.files is None)
print('file' not in request.files)
The output in called method is empty
{}
<class 'dict'>
Request data
b''
Files
ImmutableMultiDict([])
False
True
Can someone tell me how to fix this?
files = {'upload_file': open('file.txt','rb')}
values = {'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'}
r = requests.post(url, files=files, data=values)
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/', methods=['POST'])
def upload_file():
uploaded_file = request.files['file']
if uploaded_file.filename != '':
uploaded_file.save(uploaded_file.filename)
return redirect(url_for('index'))

DRF How to test file uploads?

I have a simple model, a serializer and a view. I want to upload a file over the view but no method I found worked.
Here's my code:
def test_api_post(self):
lesson = self.Create_lesson()
file = SimpleUploadedFile(
"file.txt",
"".join(random.choices(string.ascii_letters + string.digits, k=1024 * 5)).encode(),
"text/plain"
)
response = self.client.post(
"/api/submission/",
{
"lesson": lesson.id,
"file": file
},
format="multipart"
)
self.assertStatusOk(response.status_code) # Error
I tried it using with open() as file and I also tried using path.read_bytes(). Nothing worked.
How can I test binary file uploading with django-rest-framework's test client? doesn't work, https://gist.github.com/guillaumepiot/817a70706587da3bd862835c59ef584e doesn't work and how to unit test file upload in django also doesn't work.
I have fixed the problem with that:
import io
from django.test import TestCase
class test(TestCase):
def test_upload_file(self):
with open('/path/to/file.txt', 'rb') as fp :
fio = io.FileIO(fp.fileno())
fio.name = 'file.txt'
r = self.client.post('/url/', {'filename': fio, 'extraparameter': 5})
self.assertEqual(r.headers['Content-Type'], 'application/json')
url.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'/url/$', views.serverside_method, name="serverside_method")
]
Example on the server-side (view.py)
def serverside_method(request):
if 'filename' in request.FILES:
file_request = request.FILES['filename']
file_size = file_request.size
file_request_name = file_request.name
return JsonResponse({'Success': True})
else:
return JsonResponse({'Success': False})
source: https://gist.github.com/nghiaht/682c2d8d40272c52dbf7adf214f1c0f1
work for me
from rest_framework.test import APIRequestFactory
from apps.Some.SomeViewSet import SomeViewSet
c = APIRequestFactory()
url = 'someurl/'
view = SomeViewSet.as_view()
file = 'path/to/file'
q = {'filename': file}
request = c.post(url, q)
response = view(request)

Python Flask - Does not return output if the file extension is xls

I have a small Flask project that gets some inputs from the user and extracts some data from the database based on the input back to the user and returns an output file.
The code works just fine if the file format is csv. However when the file format is xls, I see the output being generated but the flask app does not return the file.
Edited:
Given below is the code for views.py
#app.route('/data', methods=['GET','POST'])
def data():
form = DataForm()
if form.validate_on_submit():
name = form.name.data
start_date = form.start_date_field.data
end_date = form.end_date_field.data
file_extension = form.file_extension_field.data
rep_func(name=name, start_date=start_date, end_date=end_date, exten=file_extension)
current_directory = path.abspath(path.join(__file__, ".."))
base = os.path.join(current_directory, 'files')
if file_extension == 'csv':
data = pd.read_csv(base + f'/final_output/{name}_{end_date}.{file_extension}', sep=r',(?!\s|\Z)', engine='python')
resp = make_response(data.to_csv(index=False))
resp.headers["Content-Disposition"] = f'attachment; filename={name}_{end_date}.{file_extension}'
resp.headers["Content-Type"] = "text/csv"
elif file_extension == 'xls':
data = pd.read_excel(base + f'/final_output/{name}_{end_date}.{file_extension}')
resp = make_response(data.to_excel(index=False))
resp.headers["Content-Disposition"] = f'attachment; filename={name}_{end_date}.{file_extension}'
resp.headers["Content-Type"] = "application/vnd.ms-excel"
return resp
return render_template('file.html', form=form)
Could anyone advise on where am I going wrong with this. Thanks

Categories

Resources