简体   繁体   中英

Python/ Django- how to update what information is displayed on a generated PDF

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 (ie '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 (ie 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?

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')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM