Skip to content

Commit e3e3848

Browse files
authored
Feature-9059: Refactor download PDF request due to unable to receieve on frontend (#9060)
* fix issue user request check in not admin * feature-9059: Refactor download PDF request due to unable to receieve on frontend * update code * fix pipeline * fix pipeline * update newest branch * fix pipeline * fix pipeline * fix pipeline * fix code review * fix code review * fix code review * fix code review * fix code review * fix code review
1 parent da33c6e commit e3e3848

File tree

8 files changed

+270
-103
lines changed

8 files changed

+270
-103
lines changed

app/api/custom/badge_forms.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
from flask import Blueprint, request
2-
from flask.helpers import send_from_directory
1+
from flask import Blueprint, jsonify, request
2+
from flask.helpers import send_from_directory, url_for
33
from flask_jwt_extended import jwt_required
44

5-
from app.api.helpers.badge_forms import create_preivew_badge_pdf, create_print_badge_pdf
5+
from app.api.helpers.badge_forms import create_preivew_badge_pdf
66
from app.api.helpers.errors import ForbiddenError, NotFoundError
7+
from app.api.helpers.export_helpers import (
8+
comma_separated_params_to_list,
9+
create_export_badge_job,
10+
)
711
from app.api.helpers.permission_manager import has_access
812
from app.models.badge_form import BadgeForms
913
from app.models.ticket_holder import TicketHolder
@@ -27,26 +31,40 @@ def preivew_badge_pdf():
2731
return send_from_directory('../', file_path, as_attachment=True)
2832

2933

30-
@badge_forms_routes.route('/print-badge-pdf', methods=['POST'])
34+
@badge_forms_routes.route('/print-badge-pdf', methods=['GET'])
3135
@jwt_required
3236
def print_badge_pdf():
3337
"""Print Badge Template PDF"""
34-
attendee_id = request.json.get('attendee_id')
35-
list_field_show = request.json.get('list_field_show')
36-
ticketHolders = TicketHolder.query.filter_by(id=attendee_id).first()
37-
if ticketHolders is None:
38+
from ..helpers.tasks import create_print_badge_pdf
39+
40+
if not request.args.get('attendee_id', False):
41+
raise NotFoundError(
42+
{'parameter': 'attendee_id'}, "attendee_id is missing from your request."
43+
)
44+
if not request.args.get('list_field_show', False):
45+
raise NotFoundError(
46+
{'parameter': 'list_field_show'},
47+
"list_field_show is missing from your request.",
48+
)
49+
attendee_id = request.args.get('attendee_id')
50+
list_field_show = comma_separated_params_to_list(request.args.get('list_field_show'))
51+
52+
ticket_holder = TicketHolder.query.filter_by(id=attendee_id).first()
53+
if ticket_holder is None:
3854
raise NotFoundError(
3955
{'source': ''}, 'This ticket holder is not associated with any ticket'
4056
)
41-
badgeForms = BadgeForms.query.filter_by(
42-
badge_id=ticketHolders.ticket.badge_id
57+
badge_form = BadgeForms.query.filter_by(
58+
badge_id=ticket_holder.ticket.badge_id
4359
).first()
44-
if badgeForms is None:
60+
if badge_form is None:
4561
raise NotFoundError(
4662
{'source': ''}, 'This badge form is not associated with any ticket'
4763
)
48-
if not has_access('is_coorganizer', event_id=badgeForms.event_id):
64+
if not has_access('is_coorganizer', event_id=ticket_holder.event_id):
4965
raise ForbiddenError({'source': ''}, 'Unauthorized Access')
5066

51-
file_path = create_print_badge_pdf(badgeForms, ticketHolders, list_field_show)
52-
return send_from_directory('../', file_path, as_attachment=True)
67+
task = create_print_badge_pdf.delay(attendee_id, list_field_show)
68+
create_export_badge_job(task.id, ticket_holder.event_id, attendee_id)
69+
70+
return jsonify(task_url=url_for('tasks.celery_task', task_id=task.id))

app/api/helpers/badge_forms.py

Lines changed: 26 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import base64
22
import io
3-
from datetime import datetime
43

54
import qrcode
65
from flask import render_template
7-
from sqlalchemy import asc
86

9-
from app.api.helpers.db import save_to_db
107
from app.api.helpers.files import create_save_pdf
118
from app.api.helpers.storage import UPLOAD_PATHS, generate_hash
129
from app.api.helpers.utilities import to_snake_case
@@ -33,8 +30,8 @@ def create_preivew_badge_pdf(badgeForms):
3330
font_weight = []
3431
font_style = []
3532
text_decoration = []
36-
if badge_field['font_weight']:
37-
for item in badge_field['font_weight']:
33+
if badge_field.get('font_weight'):
34+
for item in badge_field.get('font_weight'):
3835
if item.get('font_weight'):
3936
font_weight.append(item.get('font_weight'))
4037
if item.get('font_style'):
@@ -80,25 +77,32 @@ def get_value_from_qr_filed(field: BadgeFieldForms, ticket_holder: TicketHolder)
8077
"""Get the value of a QR code field."""
8178
qr_value = {}
8279
custom_fields = []
83-
for field_identifier in field.qr_custom_field:
84-
value_ = ""
85-
try:
86-
snake_case_field_identifier = to_snake_case(field_identifier)
87-
value_ = getattr(ticket_holder, snake_case_field_identifier)
88-
except AttributeError:
80+
if field.qr_custom_field is not None:
81+
for field_identifier in field.qr_custom_field:
82+
value_ = ""
8983
try:
90-
value_ = ticket_holder.complex_field_values[field_identifier]
91-
# Get the field description then Capitalize first letter and remove space.
92-
custom_form = CustomForms.query.filter_by(
93-
field_identifier=field_identifier,
94-
form_id=ticket_holder.ticket.form_id,
95-
).first()
96-
field_description = custom_form.description.title().replace(' ', '')
97-
custom_fields.append({field_description: value_})
84+
snake_case_field_identifier = to_snake_case(field_identifier)
85+
value_ = getattr(ticket_holder, snake_case_field_identifier)
9886
except AttributeError:
99-
print(field_identifier)
100-
101-
qr_value.update({field_identifier: str(value_)})
87+
try:
88+
if ticket_holder.complex_field_values is not None:
89+
value_ = ticket_holder.complex_field_values[field_identifier]
90+
# Get the field description then
91+
# Capitalize first letter and remove space.
92+
custom_form = CustomForms.query.filter_by(
93+
field_identifier=field_identifier,
94+
form_id=ticket_holder.ticket.form_id,
95+
).first()
96+
field_description = custom_form.description.title().replace(
97+
' ', ''
98+
)
99+
custom_fields.append({field_description: value_})
100+
except AttributeError:
101+
print(field_identifier)
102+
except Exception:
103+
print(field_identifier)
104+
105+
qr_value.update({field_identifier: str(value_)})
102106
qr_value.update({'custom_fields': custom_fields, 'ticket_id': ticket_holder.id})
103107
return qr_value
104108

@@ -118,67 +122,3 @@ def create_base64_img_qr(qr_code_data: str) -> str:
118122
img.save(io_buffer)
119123
qr_img_str = base64.b64encode(io_buffer.getvalue()).decode()
120124
return qr_img_str
121-
122-
123-
def create_print_badge_pdf(badge_form, ticket_holder, list_field_show):
124-
"""
125-
Create tickets and invoices for the holders of an order.
126-
:param badgeForms: The order for which to create tickets for.
127-
"""
128-
badgeFieldForms = (
129-
BadgeFieldForms.query.filter_by(badge_form_id=badge_form.id)
130-
.filter_by(badge_id=badge_form.badge_id)
131-
.order_by(asc("id"))
132-
.all()
133-
)
134-
for field in badgeFieldForms:
135-
if field.custom_field.lower() == 'qr':
136-
qr_code_data = get_value_from_qr_filed(field, ticket_holder)
137-
qr_rendered = render_template('cvf/badge_qr_template.cvf', **qr_code_data)
138-
139-
field.sample_text = create_base64_img_qr(qr_rendered)
140-
continue
141-
if list_field_show is None or field.field_identifier not in list_field_show:
142-
field.sample_text = ' '
143-
continue
144-
145-
get_value_from_field_indentifier(field, ticket_holder)
146-
147-
for badge_field in badgeFieldForms:
148-
font_weight = []
149-
font_style = []
150-
text_decoration = []
151-
badge_field.font_weight_tmp = badge_field.font_weight
152-
if badge_field.font_weight:
153-
for item in badge_field.font_weight:
154-
if item.get('font_weight'):
155-
font_weight.append(item.get('font_weight'))
156-
if item.get('font_style'):
157-
font_style.append(item.get('font_style'))
158-
if item.get('text_decoration'):
159-
text_decoration.append(item.get('text_decoration'))
160-
if not font_weight:
161-
badge_field.font_weight = 'none'
162-
else:
163-
badge_field.font_weight = ','.join(font_weight)
164-
if not font_style:
165-
badge_field.font_style = 'none'
166-
else:
167-
badge_field.font_style = ','.join(font_style)
168-
if not text_decoration:
169-
badge_field.text_decoration = 'none'
170-
else:
171-
badge_field.text_decoration = ','.join(text_decoration)
172-
create_save_pdf(
173-
render_template(
174-
'pdf/badge_forms.html', badgeForms=badge_form, badgeFieldForms=badgeFieldForms
175-
),
176-
UPLOAD_PATHS['pdf']['badge_forms_pdf'].format(identifier=badge_form.badge_id),
177-
identifier=badge_form.badge_id,
178-
)
179-
ticket_holder.is_badge_printed = True
180-
ticket_holder.badge_printed_at = datetime.now()
181-
for badge_field in badgeFieldForms:
182-
badge_field.font_weight = badge_field.font_weight_tmp
183-
save_to_db(ticket_holder, 'Ticket Holder saved')
184-
return file_pdf_path(badge_form)

app/api/helpers/export_helpers.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,37 @@ def handle_unserializable_data(obj):
315315
"""
316316
if isinstance(obj, datetime):
317317
return obj.__str__()
318+
319+
320+
def create_export_badge_job(task_id, event_id, attendee_id):
321+
"""Create export job for an export that is going to start"""
322+
export_job = ExportJob.query.filter_by(
323+
event_id=event_id, attendee_id=attendee_id
324+
).first()
325+
task_url = url_for('tasks.celery_task', task_id=task_id)
326+
logged_user = get_current_user()
327+
328+
if export_job:
329+
330+
export_job.task = task_url
331+
export_job.user_email = logged_user.email
332+
export_job.attendee_id = attendee_id
333+
export_job.event = Event.query.get(event_id)
334+
export_job.starts_at = datetime.now(pytz.utc)
335+
else:
336+
export_job = ExportJob(
337+
task=task_url,
338+
user_email=logged_user.email,
339+
attendee_id=attendee_id,
340+
event=Event.query.get(event_id),
341+
)
342+
save_to_db(export_job, 'ExportJob saved')
343+
344+
345+
def comma_separated_params_to_list(param):
346+
"""
347+
convert string to list separated by comma
348+
@param param: string to be separates
349+
@return: array string
350+
"""
351+
return list(filter(lambda x: x and x is not None, param.split(',')))

app/api/helpers/tasks.py

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,21 @@
5555
from app.models.user_follow_group import UserFollowGroup
5656
from app.settings import get_settings
5757

58+
from ...models.badge_field_form import BadgeFieldForms
59+
from ...models.badge_form import BadgeForms
60+
from .badge_forms import (
61+
create_base64_img_qr,
62+
get_value_from_field_indentifier,
63+
get_value_from_qr_filed,
64+
)
65+
from .errors import NotFoundError
5866
from .import_helpers import update_import_job
5967

6068
"""
6169
Define all API v2 celery tasks here
6270
This is done to resolve circular imports
6371
"""
6472

65-
6673
logger = logging.getLogger(__name__)
6774

6875

@@ -727,7 +734,6 @@ def export_admin_sales_csv_task(self, status='all'):
727734

728735
@celery.task(base=RequestContextTask, name='export.speakers.csv', bind=True)
729736
def export_speakers_csv_task(self, event_id, status='all'):
730-
731737
if status not in [
732738
'all',
733739
'pending',
@@ -833,7 +839,6 @@ def rename_chat_room(event_id):
833839

834840
@celery.task(base=RequestContextTask, name='export.group.followers.csv', bind=True)
835841
def export_group_followers_csv_task(self, group_id):
836-
837842
followers = UserFollowGroup.query.filter_by(group_id=group_id).all()
838843

839844
try:
@@ -861,3 +866,97 @@ def export_group_followers_csv_task(self, group_id):
861866
logging.exception('Error in exporting group followers as CSV')
862867

863868
return result
869+
870+
871+
@celery.task(base=RequestContextTask, name='export.badge.pdf', bind=True)
872+
def create_print_badge_pdf(self, attendee_id, list_field_show):
873+
"""
874+
Create tickets and invoices for the holders of an order.
875+
@param self: create_print_badge_pdf
876+
@param attendee_id: attendee
877+
@param list_field_show: field will be included in badge pdf
878+
@return: create pdf file and return download link
879+
"""
880+
try:
881+
ticket_holder, badge_form, badge_field_forms = validate_badge_print(attendee_id)
882+
for field in badge_field_forms:
883+
field.sample_text_tmp = field.sample_text
884+
if field.custom_field is not None and field.custom_field.lower() == 'qr':
885+
qr_code_data = get_value_from_qr_filed(field, ticket_holder)
886+
qr_rendered = render_template('cvf/badge_qr_template.cvf', **qr_code_data)
887+
field.sample_text = create_base64_img_qr(qr_rendered)
888+
continue
889+
if list_field_show is None or field.field_identifier not in list_field_show:
890+
field.sample_text = ' '
891+
continue
892+
get_value_from_field_indentifier(field, ticket_holder)
893+
# Font style set up
894+
for field in badge_field_forms:
895+
font_weight = []
896+
font_style = []
897+
text_decoration = []
898+
field.font_weight_tmp = field.font_weight
899+
if field.font_weight is not None:
900+
for item in field.font_weight:
901+
if item.get('font_weight', False):
902+
font_weight.append(item.get('font_weight'))
903+
if item.get('font_style', False):
904+
font_style.append(item.get('font_style'))
905+
if item.get('text_decoration', False):
906+
text_decoration.append(item.get('text_decoration'))
907+
field.font_weight = 'none' if not font_weight else ','.join(font_weight)
908+
field.font_style = 'none' if not font_style else ','.join(font_style)
909+
field.text_decoration = (
910+
'none' if not text_decoration else ','.join(text_decoration)
911+
)
912+
badge_url = create_save_pdf(
913+
render_template(
914+
'pdf/badge_forms.html',
915+
badgeForms=badge_form,
916+
badgeFieldForms=badge_field_forms,
917+
),
918+
UPLOAD_PATHS['pdf']['badge_forms_pdf'].format(identifier=badge_form.badge_id),
919+
identifier=badge_form.badge_id,
920+
)
921+
result = {'download_url': badge_url}
922+
ticket_holder.is_badge_printed = True
923+
ticket_holder.badge_printed_at = datetime.now()
924+
for badge_field in badge_field_forms:
925+
badge_field.font_weight = badge_field.font_weight_tmp
926+
badge_field.sample_text = badge_field.sample_text_tmp
927+
save_to_db(ticket_holder, 'Ticket Holder saved')
928+
except AttributeError as e:
929+
result = {'__error': True, 'result': str(e)}
930+
except Exception:
931+
logging.exception(
932+
'%s: Error in exporting Badge as PDF', self.request.id.__str__()
933+
)
934+
result = {
935+
'__error': True,
936+
'result': 'Unexpected error when trying to print badge, please try again.',
937+
}
938+
return result
939+
940+
941+
def validate_badge_print(attendee_id):
942+
"""
943+
Validate and get attendee, badge form and badge field
944+
@param attendee_id: attendee
945+
@return: ticket_holder, badge_form, badge_field_forms
946+
"""
947+
ticket_holder = TicketHolder.query.filter_by(id=attendee_id).first()
948+
if ticket_holder is None:
949+
raise NotFoundError('This ticket holder is not associated with any ticket')
950+
badge_form = BadgeForms.query.filter_by(
951+
badge_id=ticket_holder.ticket.badge_id
952+
).first()
953+
if badge_form is None:
954+
raise NotFoundError('This badge form is not associated with any ticket')
955+
badge_field_forms = (
956+
BadgeFieldForms.query.filter_by(badge_form_id=badge_form.id)
957+
.filter_by(badge_id=badge_form.badge_id)
958+
.filter_by(is_deleted=False)
959+
.order_by(asc("id"))
960+
.all()
961+
)
962+
return ticket_holder, badge_form, badge_field_forms

app/models/export_job.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class ExportJob(db.Model):
1717

1818
event_id = db.Column(db.Integer, db.ForeignKey('events.id', ondelete='CASCADE'))
1919
event = db.relationship('Event', backref=backref('export_jobs'))
20+
attendee_id = db.Column(db.Integer, nullable=True)
2021

2122
def __repr__(self):
2223
return '<ExportJob %d for event %d>' % (self.id, self.event.id)

0 commit comments

Comments
 (0)