Reputation: 3172
I am working on a project that has been written in Python/ Django, and in particular, want to make a small change to one of its features.
On one of the webpages, there is a button, which, when clicked will generate a PDF document with information about a project in the database.
I simply want to add a 'date' to the PDF, so that whenever the button is clicked, and a PDF is generated, it will be generated with today's date displayed in the header, to keep track of when which PDF has been created (as they might show different information each time the user generates them).
The URL on which page the button is displayed is: www.xyz.com/costing/5915/
The numbers in the URL '/5915/' are the ID of the project that the user is currently working with. In the urls.py file of the costing
app, there is the following line:
url(r'^(?P<project_id>[0-9]+)/$', views.costing_home, name='home'),
and this view
is defined in views.py with:
def costing_home(request, project_id):
""" Current costing home page in FileMaker with budget reports, CCIs, variations etc """
project = Project.objects.get(id=project_id)
context = {'project': project, 'mini_form':CostingMiniForm(instance=project)}
if project.budget_overview.deposit_budget_saved:
curr_budget = Budget.objects.get_or_create(project=project, version_number=None, current_marker=False)[0]
context['current_budget'] = curr_budget
return render(request, 'costing/costing_post_deposit.html', context)
else:
curr_budget = get_current_budget(project_id) or Budget.objects.get_or_create(project=project, version_number=None, current_marker=False)[0]
context['current_budget'] = curr_budget
versions = project.budget_versions.annotate(item_count=Count('budget_items')).filter(version_number__gte=0,)
saved_versions = versions.filter(item_count__gt=0)
presentations = versions.filter(item_count=0)
if saved_versions: context['version_formset'] = VersionFormSet(queryset=saved_versions)
if presentations: context['presentations'] = presentations
print curr_budget
return render(request, 'costing/costing_pre_deposit.html', context)
I know that it is the 'post_deposit' projects that I want to add the date to (i.e. '5915' is the ID of a 'post_deposit' project)
If I go to the costing/costing_post_deposit.html file in Sublime, I can see that it has the following structure:
{% block costing %}
...
{% endblock costing %}
{% block reports %}
<div class="app-wrap clear">
...
<div class="app sm {{app.color}}">
<a class="filler" id="pdf2_package" data-view-url="{% url 'costing:pdf2_master' project.id %}"></ a>
<strong>Save pdf payment package</strong>
<a id="new_payment_pdf" class="hidden" href="" target="_blank"></a>
</div>
</div>
{% endblock reports %}
...
The class with the text "Save pdf payment package" is the button that I'm pressing to generate the PDF (clicking the button also opens up a 'new email', with the generated PDF attached, in order to send it on to the client)
As I understand, the view passed to data-view-url
in this class (i.e. costing:pdf2_master
) is the view that is being used to generate the PDF. This view is defined with:
def pdf2_master(request, project_id):
project = Project.objects.select_related('budget_overview').prefetch_related('budget_versions', 'payments').get(id=project_id)
""" Save to the current budget (no version number), as versions not used once deposit is received """
budget = get_current_budget(project_id)
# Create a date variable to displays today's date on the PDF when it's generated
date_to_display = datetime.now()
if not budget:
Budget.objects.create(project=project, current_marker=1)
project.payments.first().recalc_balance_payment()
""" Payments """
future = datetime.now()+timedelta(days=9999)
payments = list(project.payments.exclude(is_balance_payment=True).annotate(date_null=Coalesce('date_due', Value(future))).order_by('date_null', 'date_due', 'number'))
try: payments.append(project.payments.get(is_balance_payment=True)) #Ensure balance payment is the last in the list
except ObjectDoesNotExist: pass
def get_payment_details(payment):
payment_amount_exc = payment.amount_exc_vat
payment_amount_inc = inc_vat(payment.amount_exc_vat, project.vat_status)
vat = payment.amount_inc_vat - payment.amount_exc_vat
if payment.requested and not payment.date_paid:
status = 'due'
elif payment.requested and payment.date_paid:
status = 'paid'
else:
status = 'hidden'
return [
['Payment', payment.name],
['Date', payment.date_due],
['Scheduled payment (£)', payment_amount_exc],
['VAT (£)', vat],
['Gross payment (£)', payment_amount_inc, status],
['Date paid', payment.date_paid],
]
payments_details = [get_payment_details(p) for p in payments]
budget_overview = project.budget_overview
payment_made = budget_overview.total_paid_exc
budget_exc_vat = budget_overview.updated_exc_vat
outstanding = budget_overview.outstanding_exc
outstanding_inc = budget_overview.outstanding_inc
net_payment_due = budget_overview.net_payment_due
gross_payment_due = inc_vat(net_payment_due, project.vat_status)
""" Post deposit budget overview styled for pdf """
try:
construction_variations = project.variations.total_construction
client_variations = project.variations.total_client
except ObjectDoesNotExist:
construction_variations = 0
client_variations = 0
grouped_totals = [
['Construction budget', budget.item_total_exc_vat() or 0],
['Construction variations', construction_variations],
['Client variations', client_variations],
['Client choice items', budget.cci_total_exc_vat_final],
]
total_exc = project.budget_overview.updated_exc_vat
total_inc = project.budget_overview.updated_inc_vat
""" CCIs """
cci_grouped_items = budget.cci_items.all().order_by('project_room', 'room', 'name')
""" Agreed client & construction variations """
try:
variations = project.variations.addomit_set.filter(date_agreed__isnull=False).order_by('item__project_room', 'item__room', '-date_agreed').annotate(sca=Case(When(item__notes__icontains='standard cost assumption', then=Value(1)), output_field=IntegerField()) )
# variations.filter(item__notes__icontains='standard cost assumption')
agreed_total = sum([i.item.total_inc_profit for i in variations])
client_variations = variations.filter(variation_type=1).order_by('-date_agreed')
client_agreed_total = sum([i.item.total_inc_profit for i in client_variations])
construction_variations = variations.filter(variation_type=1).order_by('-date_agreed')
construction_agreed_total = sum([i.item.total_inc_profit for i in construction_variations])
unagreed_variations = project.variations.addomit_set.filter(date_agreed__isnull=True, item_reported__isnull=False).order_by('item__project_room', 'item__room', '-date_agreed')
except ObjectDoesNotExist:
variations = ''
agreed_total = ''
client_variations = ''
client_agreed_total = ''
construction_variations = ''
construction_agreed_total = ''
unagreed_variations = ''
pdf_context = {
'project': project,
'date_to_display': date_to_display,
'budget': budget,
'grouped_totals': grouped_totals,
'total_exc': total_exc,
'total_inc': total_inc,
'cci_grouped_items': cci_grouped_items,
'cci_total_exc': budget.cci_total_exc_vat_final,
'variations': variations,
'agreed_total_exc': agreed_total,
'client_variations': client_variations,
'client_agreed_total_exc': client_agreed_total,
'construction_variations': construction_variations,
'construction_agreed_total_exc': construction_agreed_total,
# 'grouped_unagreed_variations': grouped_unagreed_variations,
'unagreed_variations': unagreed_variations,
'payments_details': payments_details,
'latest_total_exc': budget_exc_vat,
'total_paid_exc': payment_made,
'outstanding_exc': outstanding,
'outstanding_inc': outstanding_inc,
'net_payment_due': net_payment_due,
'gross_payment_due': gross_payment_due,
}
template = get_template('pdf2_base.html')
html = template.render(pdf_context)
filename='temp.pdf'
result = open(filename, 'w+b')
pdf = pisa.pisaDocument(src=StringIO.StringIO(
html.encode("UTF-8")), link_callback=fetch_resources, dest=result)
file_to_be_saved = result
if request.GET.get('quick-pdf'):
temp_pdf_obj = Spreadsheet.objects.get_or_create(id=2)[0]
temp_pdf_obj.file.save('temp.pdf', File(file_to_be_saved))
result.close()
file_path = temp_pdf_obj.file.file.name
return open_pdf(file_path)
# Change to save on payment without PDF
payment = project.payments.filter(date_paid__isnull=True, requested=True).order_by('number').last()
if not payment:
payment = project.payments.get(is_balance_payment=True)
# print 'Saving to payment ', payment.number
payment_pdf = payment.pdf_package
payment_pdf.save('{}/Client Payments/{} {} Payment.pdf'.format(CCD, FDS, project.project_code.upper()), File(file_to_be_saved))
result.close()
""" Presentation meeting report. Variables key: Subject: {0: Project name, 1: date DD.MM.YY} Body: {0: Email template,} """
template = EmailTemplate.objects.get(code=10)
email_to = template.get_email_addresses(project_id)
# Make variables for subject/body
date_str = datetime.now().strftime('%d.%m.%Y')
subject = template.subject.format(project.project_name, date_str)
body = template.body([payment.email_content])
file = payment_pdf.file
filename = file.name.split('/')[-1]
mailto = 'mailto:{}?subject={}&body={}&cc={}'.format(email_to, subject, '%0D%0A'.join(body.split('\n')), accounts_email)
pdf_url = reverse('costing:pdf_open', args=[payment.id])+"?pdf=payments"
return JsonResponse({'success':'Made pdf version', 'mailto': mailto, 'pdf_url': pdf_url})
I tried adding a variable date_to_display
to the view, and setting it to datetime.now()
, to get the current date, and then added that variable to the pdf_context
with the line:
'date_to_display': date_to_display,
However, when I click the 'Save PDF Payment Package' button at that URL, although the PDF is generated, when I open it, I can't see the date that I've added to it displayed anywhere... why is this? How can I add today's date to the PDF when creating it?
Edit
Actually, I think the issue may be that if a PDF has already been generated for today, then the new one is not saved- as when I look at the PDF that has been generated, it says it was last modified a couple of hours ago, even though I pressed the 'Save PDF Payment Package' button only 5 minutes ago.
How can I check whether the PDF that I am generating already exists, and if it does, then rename the one that I am generating, to include the current date/time, so that it will have a unique filename?
Also, it seems that today's date is added to the PDF- in the page header for every page except the first page... why isn't it displayed on the first page, and how can I get it to display there?
Upvotes: 0
Views: 329
Reputation: 76997
Instead of passing in the datetime
object, try pass a string representation of it:
date_to_display = str(datetime.now())
Ideally, the better way is to have the filename timestamped (rather than the content), since that way any one who downloads can also check whether the file he received is latest or not, and this can be done without opening the file.
For doing that, simply append the timestamp to the filename using:
filename='temp_%s.pdf' % datetime.now().strftime("%Y%m%d_%H%M%S")
result = open(filename, 'w+b')
Upvotes: 0