Skip to content

Commit

Permalink
Merge pull request #11 from SPARCS-UP-Mindanao/stage
Browse files Browse the repository at this point in the history
Update for Bitwise
  • Loading branch information
ArJSarmiento authored Jan 20, 2024
2 parents 575f43e + 626f2a6 commit e9a7788
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 121 deletions.
3 changes: 2 additions & 1 deletion handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ def generate_certificate_handler(event, context):
logger.info(record)
message_body = json.loads(record['body'])
event_id = message_body['eventId']
certificate_usecase.generate_certficates(event_id=event_id)
registration_id = message_body['registrationId']
certificate_usecase.generate_certficates(event_id=event_id, registration_id=registration_id)
SQS.delete_message(QueueUrl=CERTIFICATE_QUEUE, ReceiptHandle=record['receiptHandle'])
45 changes: 38 additions & 7 deletions model/events/event.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
import os
from datetime import datetime
from typing import Optional

from model.events.events_constants import EventStatus
from pydantic import BaseModel, EmailStr, Extra, Field
from pynamodb.attributes import BooleanAttribute, NumberAttribute, UnicodeAttribute
from pynamodb.indexes import AllProjection, LocalSecondaryIndex
from pynamodb.models import Model


class EventIdIndex(LocalSecondaryIndex):
class Meta:
index_name = 'eventId-index'
projection = AllProjection()
read_capacity_units = 1
write_capacity_units = 1

hashKey = UnicodeAttribute(hash_key=True)
eventId = UnicodeAttribute(range_key=True)

from model.entities import Entities
from model.events.events_constants import EventStatus

class Event(Model):
# hk: v<version_number>
# rk: <adminId>#<eventId>
class Meta:
table_name = os.getenv('EVENTS_TABLE')
region = os.getenv('REGION')
billing_mode = 'PAY_PER_REQUEST'

hashKey = UnicodeAttribute(hash_key=True)
rangeKey = UnicodeAttribute(range_key=True)

latestVersion = NumberAttribute(null=False)
entryStatus = UnicodeAttribute(null=False)
eventId = UnicodeAttribute(null=False)

createDate = UnicodeAttribute(null=True)
updateDate = UnicodeAttribute(null=True)
createdBy = UnicodeAttribute(null=True)
updatedBy = UnicodeAttribute(null=True)

class Event(Entities, discriminator='Event'):
# hk: Event
# rk: v<version_number>#<entry_id>
name = UnicodeAttribute(null=True)
description = UnicodeAttribute(null=True)
status = UnicodeAttribute(null=True)
Expand All @@ -25,6 +54,8 @@ class Event(Entities, discriminator='Event'):
price = NumberAttribute(null=True)
certificateTemplate = UnicodeAttribute(null=True)

eventIdIndex = EventIdIndex()


class EventIn(BaseModel):
class Config:
Expand All @@ -49,11 +80,11 @@ class EventOut(EventIn):
class Config:
extra = Extra.ignore

entryId: str = Field(..., title="ID")
eventId: str = Field(..., title="ID")
createDate: datetime = Field(..., title="Created At")
updateDate: datetime = Field(..., title="Updated At")
createdBy: str = Field(..., title="Created By")
updatedBy: str = Field(..., title="Updated By")
updatedBy: str = Field(None, title="Updated By")
bannerUrl: Optional[str] = Field(None, title="Banner Pre-signed URL")
logoUrl: Optional[str] = Field(None, title="Logo Pre-signed URL")
certificateTemplateUrl: Optional[str] = Field(None, title="Certificate Template Pre-signed URL")
112 changes: 5 additions & 107 deletions repository/events_repository.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import logging
import os
from copy import deepcopy
from datetime import datetime
from http import HTTPStatus
from typing import List, Tuple

from constants.common_constants import EntryStatus
from model.events.event import Event
from pynamodb.connection import Connection
from pynamodb.exceptions import (
PutError,
PynamoDBConnectionError,
QueryError,
TableDoesNotExist,
TransactWriteError,
)
from pynamodb.transactions import TransactWrite

from constants.common_constants import EntryStatus
from model.events.event import Event, EventIn
from repository.repository_utils import RepositoryUtils


class EventsRepository:
Expand All @@ -29,16 +23,10 @@ def __init__(self) -> None:

def query_events(self, event_id: str = None) -> Tuple[HTTPStatus, List[Event], str]:
try:
if event_id:
range_key_prefix = f'v{self.latest_version}#{event_id}'
range_key_condition = Event.rangeKey.__eq__(range_key_prefix)
else:
range_key_prefix = f'v{self.latest_version}#'
range_key_condition = Event.rangeKey.startswith(range_key_prefix)

range_key_condition = Event.eventId == event_id if event_id else None
event_entries = list(
Event.query(
hash_key=self.core_obj,
Event.eventIdIndex.query(
hash_key=f'v{self.latest_version}',
range_key_condition=range_key_condition,
filter_condition=Event.entryStatus == EntryStatus.ACTIVE.value,
)
Expand Down Expand Up @@ -73,93 +61,3 @@ def query_events(self, event_id: str = None) -> Tuple[HTTPStatus, List[Event], s

logging.info(f'[{self.core_obj}={event_id}] Fetch Event data successful')
return HTTPStatus.OK, event_entries, None

def update_event(self, event_entry: Event, event_in: EventIn) -> Tuple[HTTPStatus, Event, str]:
current_version = event_entry.latestVersion
new_version = current_version + 1

data = RepositoryUtils.load_data(pydantic_schema_in=event_in, exclude_unset=True)
has_update, updated_data = RepositoryUtils.get_update(
old_data=RepositoryUtils.db_model_to_dict(event_entry), new_data=data
)
if not has_update:
return HTTPStatus.OK, event_entry, 'no update'
try:
with TransactWrite(connection=self.conn) as transaction:
# Update Entry -----------------------------------------------------------------------------
# check if there's update or none
updated_data.update(
updateDate=self.current_date,
updatedBy=os.getenv('CURRENT_USER'),
latestVersion=new_version,
)
actions = [getattr(Event, k).set(v) for k, v in updated_data.items()]
transaction.update(event_entry, actions=actions)

# Store Old Entry --------------------------------------------------------------------------
old_event_entry = deepcopy(event_entry)
old_event_entry.rangeKey = event_entry.rangeKey.replace('v0#', f'v{new_version}#')
old_event_entry.latestVersion = current_version
old_event_entry.updatedBy = old_event_entry.updatedBy or os.getenv('CURRENT_USER')
transaction.save(old_event_entry)

event_entry.refresh()
logging.info(f'[{event_entry.rangeKey}] ' f'Update event data successful')
return HTTPStatus.OK, event_entry, ''

except TransactWriteError as e:
message = f'Failed to update event data: {str(e)}'
logging.error(f'[{event_entry.rangeKey}] {message}')

return HTTPStatus.INTERNAL_SERVER_ERROR, None, message

def delete_event(self, event_entry: Event) -> Tuple[HTTPStatus, str]:
try:
# create new entry with old data
current_version = event_entry.latestVersion
new_version = current_version + 1
old_event_entry = deepcopy(event_entry)
old_event_entry.rangeKey = event_entry.rangeKey.replace('v0#', f'v{new_version}#')
old_event_entry.updatedBy = old_event_entry.updatedBy or os.getenv('CURRENT_USER')
old_event_entry.save()

# set entry status to deleted
event_entry.updateDate = self.current_date
event_entry.updatedBy = os.getenv('CURRENT_USER')
event_entry.latestVersion = new_version
event_entry.entryStatus = EntryStatus.DELETED.value
event_entry.save()

logging.info(f'[{event_entry.rangeKey}] ' f'Delete event data successful')
return HTTPStatus.OK, None
except PutError as e:
message = f'Failed to delete event data: {str(e)}'
logging.error(f'[{event_entry.rangeKey}] {message}')
return HTTPStatus.INTERNAL_SERVER_ERROR, message

def update_event_after_s3_upload(self, event_entry: Event, event_in: EventIn) -> Tuple[HTTPStatus, Event, str]:
"""
This method is almost the same as the update_event() method,
but excludes the metadata e.g updatedBy, updateDate etc.
This is needed so that the lambda handler that triggers when a file
is uploaded on S3 works properly.
"""
data = RepositoryUtils.load_data(pydantic_schema_in=event_in, exclude_unset=True)
_, updated_data = RepositoryUtils.get_update(
old_data=RepositoryUtils.db_model_to_dict(event_entry), new_data=data
)

try:
with TransactWrite(connection=self.conn) as transaction:
actions = [getattr(Event, k).set(v) for k, v in updated_data.items()]
transaction.update(event_entry, actions=actions)

event_entry.refresh()
logging.info(f'[{event_entry.rangeKey}] ' f'Update event data successful')
return HTTPStatus.OK, event_entry, ''

except TransactWriteError as e:
message = f'Failed to update event data: {str(e)}'
logging.error(f'[{event_entry.rangeKey}] {message}')

return HTTPStatus.INTERNAL_SERVER_ERROR, None, message
2 changes: 2 additions & 0 deletions resources/generate_certificate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ certMaker:
Resource:
- arn:aws:dynamodb:ap-southeast-1:192218445313:table/${self:custom.stage}-sparcs-events-entities
- arn:aws:dynamodb:ap-southeast-1:192218445313:table/${self:custom.stage}-sparcs-events-registrations
- arn:aws:dynamodb:ap-southeast-1:192218445313:table/${self:custom.stage}-sparcs-events
- arn:aws:dynamodb:ap-southeast-1:192218445313:table/${self:custom.stage}-sparcs-events/index/*
2 changes: 2 additions & 0 deletions serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ custom:
bucket: ${self:custom.stage}-${self:custom.projectName}-file-bucket
registrations: ${self:custom.stage}-${self:custom.projectName}-registrations
certificateQueue: ${self:custom.stage}-${self:custom.projectName}-certificate-queue.fifo
events: ${self:custom.stage}-${self:custom.projectName}
pythonRequirements:
dockerizePip: non-linux
noDeploy:
Expand All @@ -34,6 +35,7 @@ provider:
CERTIFICATE_QUEUE: ${self:custom.certificateQueue}
REGISTRATIONS_TABLE: ${self:custom.registrations}
ENTITIES_TABLE: ${self:custom.entities}
EVENTS_TABLE: ${self:custom.events}
S3_BUCKET: ${self:custom.bucket}

package: ${file(resources/package.yml)}
Expand Down
17 changes: 11 additions & 6 deletions usecase/certificate_usecase.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def generate_certificate_html(self, template_img: str, name: str):
htmlTemplate = j2.from_string(template)
return htmlTemplate.render(template_img=template_img, name=name)

def generate_certficates(self, event_id: str):
def generate_certficates(self, event_id: str, registration_id: str = None):
logger.info(f"Generating certificates for event: {event_id}")

# Get Events Data
Expand All @@ -39,7 +39,14 @@ def generate_certficates(self, event_id: str):
template_img = event.certificateTemplate

# Get Registration Data
status, registrations, message = self.__registrations_repository.query_registrations(event_id=event_id)
if registration_id:
status, registration, message = self.__registrations_repository.query_registrations(
event_id=event_id, registration_id=registration_id
)
registrations = [registration]
else:
status, registrations, message = self.__registrations_repository.query_registrations(event_id=event_id)

if status != HTTPStatus.OK:
logger.error(message)
return
Expand All @@ -49,6 +56,7 @@ def generate_certficates(self, event_id: str):
with tempfile.TemporaryDirectory() as tmpdir:
template_img_path = os.path.join(tmpdir, 'template_img.png')
self.__s3_data_store.download_file(object_name=template_img, file_name=template_img_path)
zoom = 4

for registration in registrations:
logger.info(
Expand All @@ -72,8 +80,7 @@ def generate_certficates(self, event_id: str):

# Get only the first page of the PDF----------------------------------------------------------------------------------
certificate_doc = fitz.open(certificate_path)
first_page_index = 0
first_page = certificate_doc.load_page(first_page_index)
first_page = certificate_doc.load_page(0)

# Create a new PDF to store the first page
doc_first_page = fitz.open()
Expand All @@ -87,8 +94,6 @@ def generate_certficates(self, event_id: str):
certificate_pdf_object_key = f'certificates/{event_id}/{name}/{certificate_name_pdf}'
self.__s3_data_store.upload_file(file_name=certificate_path, object_name=certificate_pdf_object_key)

# Convert to png-----------------------------------------------------------------------------------------------------
zoom = 4
mat = fitz.Matrix(zoom, zoom)
pix = first_page.get_pixmap(matrix=mat)

Expand Down

0 comments on commit e9a7788

Please sign in to comment.