pyPDF merging and displaying as httpresponse through django - django

I'm having trouble incorporating pyPDF logic to merge two pdf files into my django site. I have written code that works to merge files when run in a python file on the local server(but I need to explicitly identify which files to merge:
from pyPdf import PdfFileReader, PdfFileWriter
output = PdfFileWriter()
input1 = PdfFileReader(file("abc_form0.pdf", "rb"))
input2 = PdfFileReader(file("abc_form1.pdf", "rb"))
total_pages = input1.getNumPages()
total_pages1 = input2.getNumPages()
for page in xrange(total_pages):
output.addPage(input1.getPage(page))
for page in xrange(total_pages1):
output.addPage(input2.getPage(page))
outputStream = file("output.pdf", "wb")
output.write(outputStream)
outputStream.close()
This code (from my django site) works to display a single PDF. However, when more than one PDF is selected, only the final PDF selected displays, hence the need to integrate pyPDF to create one file containing all requested files.
class ABCAdmin(admin.ModelAdmin):
actions = ['print_selected_pdf']
def create_pdf(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
for obj in queryset:
response.write(obj.form)
ABC.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
return response
def print_selected_pdf(self, request, queryset):
# prints the pdfs for those that are selected,
# regardless if the pdf_printed field is true or false
qs = queryset.filter(pdf_printed__exact=0)
return self.create_pdf(request, qs)
I'm struggling with finding a way to combine these two methods. What I have tried is the following, but get an IO error [Errno 2] No such file or directory: 'obj.form'. So it is not reading in obj.form - I need to find a way for it to read in these objects correctly. Anyway, this is what I've tried.
def create_pdf(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
for obj in queryset:
output = PdfFileWriter()
input = PdfFileReader(file("obj.form","rb"))
total_pages = input.getNumPages()
for page in xrange(total_pages):
output.addPage(input.GetPage(page))
outputStream = file("output.pdf", "wb")
response.write(outputStream)
outputStream.close()
ABC.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
return response
def print_selected_pdf(self, request, queryset):
# prints the pdfs for those that are selected,
# regardless if the pdf_printed field is true or false
qs = queryset.filter(pdf_printed__exact=0)
return self.create_pdf(request, qs)
As always, thanks for any help or tips you may be able to provide!

I used stringIO in the output stream to get around this. I also had to define each page of the PDF form to be added to the output. This works to iterate for any number of forms that I need to be requested.
def create_form(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
output = PdfFileWriter()
for obj in queryset:
input = PdfFileReader(cStringIO.StringIO(obj.form))
output.addPage(input.getPage(0))
output.addPage(input.getPage(1))
output.addPage(input.getPage(2))
output.addPage(input.getPage(3))
ABC_Self.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
outputStream = cStringIO.StringIO()
output.write(outputStream)
response.write(outputStream.getvalue())
return response

All you need to do is provide the full absolute path to your PDF files, e.g.
/home/joseph/form.pdf
or
c:/home/joseph/form.pdf
etc.

Related

I can't write Georgian shrift, when I generate pdf , using django . My code writes black squares besides Georgian letters

I generate pdf files about each models object, but in pdf, I can't write with Georgian letters, it returns black squares.
Here is some code, I am using from xhtml2pdf import pisa
in my urls.py :path('pdf/', views.customer_render_pdf_view, name='customer-pdf-view')
def customer_render_pdf_view(request, *args, **kwargs):
pk = kwargs.get('pk')
customer = get_object_or_404(id, pk=pk)
template_path = 'test.html'
context = {'customer': customer}
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'filename= "report.pdf"'
template = get_template(template_path)
html = template.render(context)
pisa_status = pisa.CreatePDF(
html,
dest=response,
encoding='utf-8'
)
# if error then show some funny view
if pisa_status.err:
return HttpResponse('We had some errors <pre>' + html + '</pre>')
return response
You are just using a font that doesn't contain your letters. You need to use a font that does have them.
https://xhtml2pdf.readthedocs.io/en/latest/reference.html?highlight=Font#using-custom-fonts

django - Generate multiple PDF files in a loop condition using reportlab

I am in a django project and trying to generate multiple PDF files in a loop condition using reportlab.
view.py
def pdftest(request, *args, **kwargs):
if request.method == 'POST':
for x in range(0, 3):
response = HttpResponse(content_type='application/pdf')
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=portrait(letter))
elements = []
ptext = 'Hellow World! --- %s' %x
styles=getSampleStyleSheet()
elements.append(Paragraph(ptext, styles['Normal']))
doc.build(elements)
response['Content-Disposition'] = 'attachment; filename="{}"'.format('test.pdf')
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response
context = {}
context['title'] = 'PDF TEST'
return render(request, 'companies/pdftest.html', context)
But my program produce only one PDF. Do you have any idea about this problem?
You’re calling return response in the first pass of your loop so obviously your program never reaches the second or third pass.

Django Rest Framework function view post not working

So I am trying to serve a static file through a simple Django Rest framework function view. It gives me 200 code but doesn't download the file.
Here is the code :
#api_view(['POST'])
def download_file(request):
if request.method == 'POST':
serializer = MySerializer(data=request.data)
filename = 'file.xlsx'
file_full_path = "src/{0}".format(filename)
with open(file_full_path, 'rb') as f:
file = f.read()
response = HttpResponse(file, content_type="application/xls")
response['Content-Disposition'] = "attachment; filename={0}".format(filename)
response['Content-Length'] = os.path.getsize(file_full_path)
return response
return Response(status=status.HTTP_400_BAD_REQUEST)
What am I doing wrong here?
You are trying to download file with a HTTP POST method, I don't think it's a nice way. So try HTTP GET for downloading. If you wish to provide extra arguments (payload in POST method), you could do it using Query Parameter as /api/end/point/?param=value1&param2=value2.
So, try the following snippet,
#api_view(['GET'])
def download_file(request):
if request.method == 'GET':
filename = 'file.xlsx'
file_full_path = "src/{0}".format(filename)
with open(file_full_path, 'rb') as f:
file = f.read()
response = HttpResponse(file, content_type="application/xls")
response['Content-Disposition'] = "attachment; filename={0}".format(filename)
response['Content-Length'] = os.path.getsize(file_full_path)
return response
return Response(status=status.HTTP_400_BAD_REQUEST)

Django : How to upload csv file in unit test case using APIClient

I would like to write a unit test for a view on a Django REST Framework application. The test should upload a CSV file using the POST.
#staticmethod
def _file_upload(client, string, args, file_name):
base_path = os.path.dirname(os.path.realpath(__file__))
with open(base_path + file_name, 'rb') as data:
data = {
'file': data
}
response = client.post(reverse(string, args=[args]), data, format = "multipart")
return response.status_code, response.data
The above code I used which obviously doesn't work it shows the following error
Missing filename. Request should include a Content-Disposition header with a filename parameter.
The following code is the one that I want to test via unit testing.
class ChartOfAccounts(views.APIView):
parser_classes = (JSONParser, FileUploadParser)
def post(self, request, pk, *args, **kwargs):
request.FILES['file'].seek(0)
csv_data = CSVUtils.format_request_csv(request.FILES['file'])
try:
coa_data = CSVUtils.process_chart_of_accounts_csv(company, csv_data)
serializer = CoASerializer(coa_data, many=True)
if len(serializer.data) > 0:
return Utils.dispatch_success(request, serializer.data)
except Exception as e:
error = ["%s" % e]
return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error)
Any help regarding this is welcome. Thanks in advance
I have fixed my issue using the different approach with HTTP headers HTTP_CONTENT_DISPOSITION, HTTP_CONTENT_TYPE by this reference
And here is my code
#staticmethod
def _file_upload_csv( string, args, file_name):
base_path = os.path.dirname(os.path.realpath(__file__))
data = open(base_path + file_name, 'rb')
data = SimpleUploadedFile(content = data.read(),name = data.name,content_type='multipart/form-data')
factory = RequestFactory()
user = User.objects.get(username=UserConstant.ADMIN_USERNAME)
view = ChartOfAccounts.as_view()
content_type = 'multipart/form-data'
headers= {
'HTTP_CONTENT_TYPE': content_type,
'HTTP_CONTENT_DISPOSITION': 'attachment; filename='+file_name}
request = factory.post(reverse(string, args=[args]),{'file': data},
**headers)
force_authenticate(request, user=user)
response = view(request, args)
return response.status_code, response.data
**headers done the trick...
Here's what i did
#patch("pandas.read_csv")
#patch("pandas.DataFrame.to_sql")
def test_upload_csv_success(self, mock_read_csv, mock_to_sql) -> None:
"""Test uploading a csv file"""
file_name = "test.csv"
# Open file in write mode (Arrange)
with open(file_name, "w") as file:
writer = csv.writer(file)
# Add some rows in csv file
writer.writerow(["name", "area", "country_code2", "country_code3"])
writer.writerow(
["Albania", 28748, "AL", "ALB"],
)
writer.writerow(
["Algeria", 2381741, "DZ", "DZA"],
)
writer.writerow(
["Andorra", 468, "AD", "AND"],
)
# open file in read mode
data = open(file_name, "rb")
# Create a simple uploaded file
data = SimpleUploadedFile(
content=data.read(), name=data.name, content_type="multipart/form-data"
)
# Perform put request (Act)
res = self.client.put(CSV_URL, {"file_name": data}, format="multipart")
# Mock read_csv() and to_sql() functions provided by pandas module
mock_read_csv.return_value = True
mock_to_sql.return_value = True
# Assert
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
self.assertEqual(res.data, "Data set uploaded")
# Delete the test csv file
os.remove(file_name)

Django - serving an ical file

I'm server an icalender file through django_ical. Problem is that the file is named download.ics. I'm trying to change this to MyCalender.ics. If found this old snippet. I would prefer using django_ical, because it ingerates nicely with django syndication.
cal = vobject.iCalendar()
cal.add('method').value = 'PUBLISH' # IE/Outlook needs this
for event in event_list:
vevent = cal.add('vevent')
icalstream = cal.serialize()
response = HttpResponse(icalstream, mimetype='text/calendar')
response['Filename'] = 'filename.ics' # IE needs this
response['Content-Disposition'] = 'attachment; filename=filename.ics'
In django_ical the ICalFeed is inherited from django.contrib.syndication.views.Feed
In your app you inherit from ICalFeed to provide items, item_title and other methods that generate data for ics file.
You can override the __call__ method. The call to super will return you HttpResponse and you will add custom headers to it.
The code will be something like:
class EventFeed(ICalFeed):
"""
A simple event calender
"""
product_id = '-//example.com//Example//EN'
timezone = 'UTC'
def items(self):
return Event.objects.all().order_by('-start_datetime')
# your other fields
def __call__(self, request, *args, **kwargs):
response = super(EventFeed, self).__call__(request, *args, **kwargs)
if response.mimetype == 'text/calendar':
response['Filename'] = 'filename.ics' # IE needs this
response['Content-Disposition'] = 'attachment; filename=filename.ics'
return response
This code is not tested, so there might be some typos. Also you need to catch if there were errors in call to super. I do it by response.mimetype == 'text/calendar' but maybe there is a better way to do it