from ..extensions import db, mail from ..tools.encryption import decrypt, encrypt from ..tools.logs import write from .test import Test from flask_login import current_user from flask_mail import Message from sqlalchemy_json import MutableJson from datetime import datetime, timedelta from uuid import uuid4 class Entry(db.Model): id = db.Column(db.String(36), primary_key=True) first_name = db.Column(db.String(128), nullable=False) surname = db.Column(db.String(128), nullable=False) email = db.Column(db.String(128), nullable=False) club = db.Column(db.String(128), nullable=True) test_id = db.Column(db.String(36), db.ForeignKey('test.id')) user_code = db.Column(db.String(6), nullable=True) start_time = db.Column(db.DateTime, nullable=True) end_time = db.Column(db.DateTime, nullable=True) status = db.Column(db.String(16), nullable=True) valid = db.Column(db.Boolean, default=True, nullable=True) answers = db.Column(MutableJson, nullable=True) result = db.Column(MutableJson, nullable=True) def __repr__(self): return f' was added with .' @property def generate_id(self): raise AttributeError('generate_id is not a readable attribute.') generate_id.setter def generate_id(self): self.id = uuid4().hex @property def set_first_name(self): raise AttributeError('set_first_name is not a readable attribute.') set_first_name.setter def set_first_name(self, name:str): self.first_name = encrypt(name) def get_first_name(self): return decrypt(self.first_name) @property def set_surname(self): raise AttributeError('set_first_name is not a readable attribute.') set_surname.setter def set_surname(self, name:str): self.surname = encrypt(name) def get_surname(self): return decrypt(self.surname) @property def set_email(self): raise AttributeError('set_email is not a readable attribute.') set_email.setter def set_email(self, email:str): self.email = encrypt(email) def get_email(self): return decrypt(self.email) @property def set_club(self): raise AttributeError('set_club is not a readable attribute.') set_club.setter def set_club(self, club:str): self.club = encrypt(club) def get_club(self): return decrypt(self.club) def ready(self): self.generate_id() try: db.session.add(self) db.session.commit() except Exception as exception: db.session.rollback() write('system.log', f'Database error when preparing new entry for {self.get_surname()}, {self.get_first_name()}: {exception}') return False, f'Database error: {exception}' write('tests.log', f'New test ready for {self.get_surname()}, {self.get_first_name()} with id {self.id}.') return True, f'Test ready.' def start(self): self.start_time = datetime.now() self.status = 'started' try: db.session.commit() except Exception as exception: db.session.rollback() write('system.log', f'Database error when starting test for {self.get_surname()}, {self.get_first_name()}: {exception}') return False, f'Database error: {exception}' write('tests.log', f'Test started by {self.get_surname()}, {self.get_first_name()} with id {self.id}.') return True, f'New test started with id {self.id}.' def complete(self, answers:dict=None, result:dict=None): self.end_time = datetime.now() self.answers = answers self.result = result delta = timedelta(minutes=int(0 if self.test.time_limit is None else self.test.time_limit)+1) if not self.test.time_limit or self.end_time <= self.start_time + delta: self.status = 'completed' self.valid = True else: self.status = 'late' self.valid = False try: db.session.commit() except Exception as exception: db.session.rollback() write('system.log', f'Database error when submitting entry for {self.get_surname()}, {self.get_first_name()}: {exception}') return False, f'Database error: {exception}' write('tests.log', f'Test completed by {self.get_surname()}, {self.get_first_name()} with id {self.id}.') return True, f'Test entry completed for id {self.id}.' def validate(self): if self.valid: return False, f'The entry is already valid.' if self.status == 'started': return False, 'The entry is still pending.' self.valid = True self.status = 'completed' try: db.session.commit() except Exception as exception: db.session.rollback() write('system.log', f'Database error when validating entry {self.id}: {exception}') return False, f'Database error: {exception}' write('system.log', f'The entry {self.id} has been validated by {current_user.get_username()}.') return True, f'The entry {self.id} has been validated.' def delete(self): id = self.id name = f'{self.get_first_name()} {self.get_surname()}' try: db.session.delete(self) db.session.commit() except Exception as exception: db.session.rollback() write('system.log', f'Database error when deleting entry {id}: {exception}') return False, f'Database error: {exception}' write('system.log', f'The entry {id} by {name} has been deleted by {current_user.get_username()}.') return True, 'Entry deleted.' def notify_result(self): score = round(100*self.result['score']/self.result['max']) tags_low = { tag: tag_result['max'] - tag_result['scored'] for tag, tag_result in self.result['tags'].items() } sorted_low_tags = sorted(tags_low.items(), key=lambda x: x[1], reverse=True) tag_output = [ tag[0] for tag in sorted_low_tags[0:3] if tag[1] > 3] revision_plain = '' revision_html = '' if self.result['grade'] == 'pass': flavour_text = """Well done on successfully completing the refereeing theory exam. We really appreciate members of our community taking the time to get qualified to referee. """ elif self.result['grade'] == 'merit': flavour_text = """Congratulations on achieving a merit in the refereeing exam. We are delighted that members of our community work so hard with refereeing. """ elif self.result['grade'] == 'fail': flavour_text = """Unfortunately, you were not successful in passing the theory exam in this attempt. We hope that this does not dissuade you, and that you try again in the future. """ revision_plain = f"""Based on your answers, we would also suggest you brush up on the following topics for your next attempt:\n\n {','.join(tag_output)}\n\n """ revision_html = f"""

Based on your answers, we would also suggest you brush up on the following topics for your next attempt:

""" email = Message( subject='RefTest | SKA Refereeing Theory Exam Results', recipients=[self.get_email()], body=f""" SKA Refereeing Theory Exam Candidate Results Dear {self.get_first_name()}, This email is to confirm that you have taken the SKA Refereeing Theory Exam. Your submission has been evaluated and your results are as follows: {self.get_surname()}, {self.get_first_name()} Email Address: {self.get_email()} {f'Club: {self.get_club()}' if self.club else ''} Date of Exam: {self.end_time.strftime('%d %b %Y')} Score: {score}% Grade: {self.result['grade']} {flavour_text} {revision_plain} Thank you for taking the time to become a qualified referee. Best wishes, SKA Refereeing """, html=f"""

SKA Refereeing Theory Exam

Candidate Results

Dear {self.get_first_name()},

This email is to confirm that you have taken the SKA Refereeing Theory Exam. Your submission has been evaluated and your results are as follows:

{self.get_surname()}, {self.get_first_name()}

Email Address: {self.get_email()}

{f'

Club: {self.get_club()}

' if self.club else ''}

{score}%

{self.result['grade']}

{flavour_text}

{revision_html}

Thank you for taking the time to become a qualified referee.

Have a nice day!

Best wishes,
SKA Refereeing

""" ) try: mail.send(email) except Exception as exception: write('system.log', f'SMTP Error when trying to notify results to {self.get_surname()}, {self.get_first_name()} with error: {exception}')