2022-06-19 13:22:24 +01:00
from . . extensions import db , mail
2022-06-12 21:03:51 +01:00
from . . tools . encryption import decrypt , encrypt
from . . tools . logs import write
from . test import Test
from flask_login import current_user
2022-06-16 12:46:03 +01:00
from flask_mail import Message
2022-08-19 12:07:38 +01:00
from smtplib import SMTPException
2022-08-19 13:25:20 +01:00
from sqlalchemy . exc import SQLAlchemyError
2022-08-20 13:01:32 +01:00
from sqlalchemy_json import MutableJson
2022-06-12 21:03:51 +01:00
from datetime import datetime , timedelta
2022-06-14 22:55:11 +01:00
from uuid import uuid4
2022-06-11 18:26:39 +01:00
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 )
2022-06-16 10:44:48 +01:00
start_time = db . Column ( db . DateTime , nullable = True )
2022-06-11 18:26:39 +01:00
end_time = db . Column ( db . DateTime , nullable = True )
status = db . Column ( db . String ( 16 ) , nullable = True )
2022-06-12 21:03:51 +01:00
valid = db . Column ( db . Boolean , default = True , nullable = True )
2022-08-20 13:01:32 +01:00
answers = db . Column ( MutableJson , nullable = True )
result = db . Column ( MutableJson , nullable = True )
2022-06-11 18:26:39 +01:00
2022-06-14 22:55:11 +01:00
def __repr__ ( self ) :
return f ' <New entry by { self . first_name } { self . surname } > was added with <id { self . id } >. '
@property
def generate_id ( self ) : raise AttributeError ( ' generate_id is not a readable attribute. ' )
generate_id . setter
2022-06-15 23:54:44 +01:00
def generate_id ( self ) : self . id = uuid4 ( ) . hex
2022-06-14 22:55:11 +01:00
2022-06-11 18:26:39 +01:00
@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. ' )
2022-06-12 21:03:51 +01:00
set_email . setter
2022-06-11 18:26:39 +01:00
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. ' )
2022-06-12 21:03:51 +01:00
set_club . setter
2022-06-11 18:26:39 +01:00
def set_club ( self , club : str ) : self . club = encrypt ( club )
2022-06-12 21:03:51 +01:00
def get_club ( self ) : return decrypt ( self . club )
2022-06-16 10:44:48 +01:00
def ready ( self ) :
2022-06-16 01:03:06 +01:00
self . generate_id ( )
2022-08-19 13:25:20 +01:00
try :
db . session . add ( self )
db . session . commit ( )
2022-08-20 14:47:46 +01:00
except Exception as exception :
2022-08-19 13:25:20 +01:00
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 } . ' )
2022-06-16 10:44:48 +01:00
return True , f ' Test ready. '
def start ( self ) :
2022-06-12 21:03:51 +01:00
self . start_time = datetime . now ( )
self . status = ' started '
2022-08-20 10:56:43 +01:00
try : db . session . commit ( )
2022-08-20 14:47:46 +01:00
except Exception as exception :
2022-08-19 13:25:20 +01:00
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 } . ' )
2022-06-16 01:03:06 +01:00
return True , f ' New test started with id { self . id } . '
2022-06-12 21:03:51 +01:00
2022-06-14 22:55:11 +01:00
def complete ( self , answers : dict = None , result : dict = None ) :
2022-06-12 21:03:51 +01:00
self . end_time = datetime . now ( )
2022-06-14 22:55:11 +01:00
self . answers = answers
2022-06-16 10:44:48 +01:00
self . result = result
2022-06-16 14:15:18 +01:00
delta = timedelta ( minutes = int ( 0 if self . test . time_limit is None else self . test . time_limit ) + 1 )
2022-06-12 21:20:09 +01:00
if not self . test . time_limit or self . end_time < = self . start_time + delta :
2022-06-15 11:23:38 +01:00
self . status = ' completed '
2022-06-12 21:03:51 +01:00
self . valid = True
else :
self . status = ' late '
self . valid = False
2022-08-20 10:56:43 +01:00
try : db . session . commit ( )
2022-08-20 14:47:46 +01:00
except Exception as exception :
2022-08-19 13:25:20 +01:00
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 } . ' )
2022-06-16 01:03:06 +01:00
return True , f ' Test entry completed for id { self . id } . '
2022-06-12 21:03:51 +01:00
def validate ( self ) :
2022-06-15 11:23:38 +01:00
if self . valid : return False , f ' The entry is already valid. '
if self . status == ' started ' : return False , ' The entry is still pending. '
2022-06-12 21:03:51 +01:00
self . valid = True
self . status = ' completed '
2022-08-20 10:56:43 +01:00
try : db . session . commit ( )
2022-08-20 14:47:46 +01:00
except Exception as exception :
2022-08-19 13:25:20 +01:00
db . session . rollback ( )
write ( ' system.log ' , f ' Database error when validating entry { self . id } : { exception } ' )
return False , f ' Database error: { exception } '
2022-06-15 11:23:38 +01:00
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 ( ) } '
2022-08-19 13:25:20 +01:00
try :
db . session . delete ( self )
db . session . commit ( )
2022-08-20 14:47:46 +01:00
except Exception as exception :
2022-08-19 13:25:20 +01:00
db . session . rollback ( )
write ( ' system.log ' , f ' Database error when deleting entry { id } : { exception } ' )
return False , f ' Database error: { exception } '
2022-06-15 11:23:38 +01:00
write ( ' system.log ' , f ' The entry { id } by { name } has been deleted by { current_user . get_username ( ) } . ' )
2022-06-16 12:46:03 +01:00
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 """ <p>Based on your answers, we would also suggest you brush up on the following topics for your next attempt:</p>
< ul >
< li > { ' </li><li> ' . join ( tag_output ) } < / li >
< / ul >
"""
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 """
< h1 > SKA Refereeing Theory Exam < / h1 >
< h2 > Candidate Results < / h2 >
< p > Dear { self . get_first_name ( ) } , < / p >
< p > 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 : < / p >
< h3 > { self . get_surname ( ) } , { self . get_first_name ( ) } < / h3 >
< p > < strong > Email Address < / strong > : { self . get_email ( ) } < / p >
{ f ' <p><strong>Club</strong>: { self . get_club ( ) } </p> ' if self . club else ' ' }
< h1 > { score } % < / h1 >
< h2 > { self . result [ ' grade ' ] } < / h2 >
< p > { flavour_text } < / p >
{ revision_html }
< p > Thank you for taking the time to become a qualified referee . < / p >
< p > Have a nice day ! < / p >
< p > Best wishes , < br / > SKA Refereeing < / p >
"""
)
2022-08-20 10:56:43 +01:00
try : mail . send ( email )
2022-08-20 14:47:46 +01:00
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 } ' )