2022-06-20 01:08:57 +00:00
from flask import flash , redirect , request , url_for
2022-02-23 18:37:09 +00:00
from flask_admin import expose
2022-02-23 19:36:29 +00:00
from flask_security import admin_change_password , current_user
2022-06-20 01:08:57 +00:00
from flask_wtf import FlaskForm
2022-06-17 20:20:42 +00:00
from wtforms . fields import TextAreaField
from wtforms . validators import Optional
2021-07-30 00:50:49 +00:00
2022-05-08 19:26:25 +00:00
from . import assistantSpace , db
2022-06-01 21:57:28 +00:00
from . advlabdb_independent_funs import (
deep_getattr ,
flashRandomPassword ,
missing_formatter ,
str_formatter ,
str_without_semester_formatter ,
)
2022-05-08 19:26:25 +00:00
from . customClasses import SecureAssistantBaseView , SecureAssistantModelView
from . exceptions import DataBaseException , ModelViewException
2022-06-20 01:08:57 +00:00
from . forms import assistant_group_experiment_form_factory
2022-06-17 18:56:14 +00:00
from . model_dependent_funs import (
generate_new_password_field ,
initActiveSemesterMenuLinks ,
mark_field ,
2022-06-26 22:20:30 +00:00
parse_selection_mark_field ,
2022-06-17 18:56:14 +00:00
user_info_fields ,
)
2022-06-20 01:08:57 +00:00
from . model_independent_funs import randomPassword , reportBadAttempt
2022-05-08 19:26:25 +00:00
from . models import (
2022-04-11 23:44:53 +00:00
MAX_MARK ,
MIN_MARK ,
2021-07-30 00:50:49 +00:00
Appointment ,
Assistant ,
ExperimentMark ,
GroupExperiment ,
2021-07-30 12:20:54 +00:00
SemesterExperiment ,
2021-07-30 00:50:49 +00:00
User ,
)
2022-06-17 20:20:42 +00:00
class AssistantGroupExperimentView ( SecureAssistantModelView ) :
2022-06-20 01:08:57 +00:00
class EditForm ( FlaskForm ) :
2022-06-17 20:20:42 +00:00
note = TextAreaField (
" Note " ,
validators = [ Optional ( ) ] ,
description = " This note is optional and can be seen and edited by admins and assistants that are responsible for this semester experiment. This note is for information related to the experiment and group. Examples: Protocol received, submission until ..., etc. " ,
)
can_edit = True
column_display_actions = True
column_list = [
" semester_experiment.experiment " ,
" group.number " ,
2022-06-27 20:12:52 +00:00
" group.part_students " ,
" appointments " ,
# TODO: "final_experiment_marks_set",
2022-06-17 20:20:42 +00:00
" note " ,
]
column_labels = {
" semester_experiment.experiment " : " Experiment " ,
" group.number " : " Group number " ,
2022-06-27 20:12:52 +00:00
" group.part_students " : " Students " ,
2022-06-17 20:20:42 +00:00
}
2022-06-27 20:12:52 +00:00
def part_students_formatter ( view , context , model , name ) :
part_students = deep_getattr ( model , name )
if part_students is not None :
return " , " . join ( [ str ( part_student . student ) for part_student in part_students ] )
return attr
def appointments_formatter ( view , context , model , name ) :
appointments = deep_getattr ( model , name )
if appointments is not None :
return " , " . join ( [ str ( appointment . date ) for appointment in appointments ] )
return attr
2022-06-17 20:20:42 +00:00
column_formatters = {
" semester_experiment.experiment " : str_formatter ,
2022-06-27 20:12:52 +00:00
" group.part_students " : part_students_formatter ,
" appointments " : appointments_formatter ,
2022-06-17 20:20:42 +00:00
}
def query_modifier ( self , query ) :
return (
query . join ( SemesterExperiment )
. where ( SemesterExperiment . semester == current_user . active_semester )
. join ( SemesterExperiment . assistants )
. where ( Assistant . user == current_user )
)
2022-06-20 01:08:57 +00:00
@expose ( " /form/ " , methods = ( " GET " , " POST " ) )
def form_view ( self ) :
group_experiment_id_str = request . args . get ( " id " )
try :
group_experiment = db . session . get ( GroupExperiment , int ( group_experiment_id_str ) )
except :
red = url_for ( " index " ) + " assistant/group_experiment "
flash ( " No valid group experiment id " )
return redirect ( red )
if group_experiment not in self . get_query ( ) :
reportBadAttempt ( " Assistant {current_user} tried to edit {group_experiment} " )
raise ModelViewException ( " Unauthorized action! " )
form , appointments , experiment_marks = assistant_group_experiment_form_factory ( current_user , group_experiment )
num_appointments = len ( appointments )
appointment_fields = [
getattr ( form , f " appointment_ { appointment_num } " ) for appointment_num in range ( 1 , num_appointments + 1 )
]
num_experiment_marks = len ( experiment_marks )
experiment_mark_students = [ experiment_mark . part_student . student for experiment_mark in experiment_marks ]
oral_experiment_mark_fields = [
getattr ( form , f " oral_experiment_mark_ { experiment_mark_num } " )
for experiment_mark_num in range ( 1 , num_experiment_marks + 1 )
]
protocol_experiment_mark_fields = [
getattr ( form , f " protocol_experiment_mark_ { experiment_mark_num } " )
for experiment_mark_num in range ( 1 , num_experiment_marks + 1 )
]
if form . validate_on_submit ( ) :
2022-06-26 22:20:30 +00:00
try :
for ind , appointment in enumerate ( appointments ) :
appointment . date = appointment_fields [ ind ] . data
2022-06-20 01:08:57 +00:00
2022-06-26 22:20:30 +00:00
for ind , experiment_mark in enumerate ( experiment_marks ) :
experiment_mark . oral_mark = parse_selection_mark_field ( oral_experiment_mark_fields [ ind ] )
experiment_mark . protocol_mark = parse_selection_mark_field ( protocol_experiment_mark_fields [ ind ] )
2022-06-20 01:08:57 +00:00
2022-06-26 22:20:30 +00:00
group_experiment . note = form . note . data
db . session . commit ( )
red = url_for ( " index " ) + " assistant/group_experiment "
return redirect ( red )
except Exception as ex :
flash ( str ( ex ) , " error " )
db . session . rollback ( )
2022-06-20 01:08:57 +00:00
2022-06-27 20:12:52 +00:00
final_experiment_marks = [ experiment_mark . final_experiment_mark for experiment_mark in experiment_marks ]
2022-06-20 01:08:57 +00:00
return self . render (
" assistant_group_experiment_form.html " ,
form = form ,
experiment_label = group_experiment . semester_experiment . experiment . str ( ) ,
group_number = group_experiment . group . number ,
appointment_fields = appointment_fields ,
experiment_mark_zip = zip (
2022-06-27 20:12:52 +00:00
experiment_mark_students ,
oral_experiment_mark_fields ,
protocol_experiment_mark_fields ,
final_experiment_marks ,
2022-06-20 01:08:57 +00:00
) ,
)
2022-06-17 20:20:42 +00:00
2021-07-30 21:50:10 +00:00
class AssistantAppointmentView ( SecureAssistantModelView ) :
2022-02-24 00:28:09 +00:00
can_edit = True
2021-07-31 22:41:21 +00:00
column_list = [
" date " ,
" special " ,
" group_experiment.semester_experiment.experiment " ,
2022-06-01 21:15:22 +00:00
" group_experiment.group.number " ,
2021-07-31 22:41:21 +00:00
" group_experiment.group.part_students " ,
]
column_labels = {
" group_experiment.semester_experiment.experiment " : " Experiment " ,
2022-06-01 21:15:22 +00:00
" group_experiment.group.number " : " Group number " ,
2021-07-31 22:41:21 +00:00
" group_experiment.group.part_students " : " Students " ,
}
2021-11-30 00:47:37 +00:00
column_editable_list = [
" date " ,
]
2022-02-24 00:28:09 +00:00
form_columns = column_editable_list
2022-05-30 02:18:19 +00:00
column_descriptions = {
" special " : " A special appointment should take place in the semester break " ,
}
2021-07-30 21:50:10 +00:00
2022-06-01 21:57:28 +00:00
def part_students_formatter ( view , context , model , name ) :
part_students = deep_getattr ( model , name )
if part_students is not None :
return " , " . join ( [ str ( part_student . student ) for part_student in part_students ] )
return attr
column_formatters = {
" group_experiment.semester_experiment.experiment " : str_formatter ,
" group_experiment.group.part_students " : part_students_formatter ,
}
2022-05-16 22:42:49 +00:00
def query_modifier ( self , query ) :
return (
query . join ( GroupExperiment )
. join ( SemesterExperiment )
. where (
SemesterExperiment . semester == current_user . active_semester ,
Appointment . assistant == current_user . assistant ,
)
2021-07-30 21:50:10 +00:00
)
class AssistantExperimentMarkView ( SecureAssistantModelView ) :
2022-06-20 01:08:57 +00:00
class EditForm ( FlaskForm ) :
2022-06-17 18:56:14 +00:00
oral_mark = mark_field ( " Oral " )
protocol_mark = mark_field ( " Protocol " )
2022-06-01 21:03:41 +00:00
2022-02-24 00:28:09 +00:00
can_edit = True
2022-06-01 21:03:41 +00:00
column_display_actions = True
2022-02-24 00:28:09 +00:00
2021-07-31 22:41:21 +00:00
column_list = [
" oral_mark " ,
" protocol_mark " ,
2021-08-29 16:15:14 +00:00
" final_experiment_mark " ,
2022-06-01 21:57:28 +00:00
" group_experiment.semester_experiment.experiment.number " ,
2022-06-01 21:15:22 +00:00
" part_student.student " ,
" part_student.group.number " ,
2021-07-31 22:41:21 +00:00
" part_student.student.uni_email " ,
" part_student.student.contact_email " ,
" part_student.part " ,
" assistant " ,
2022-02-27 18:30:28 +00:00
" admin " ,
2021-07-31 22:41:21 +00:00
]
column_labels = {
2022-06-01 21:57:28 +00:00
" group_experiment.semester_experiment.experiment.number " : " Experiment number " ,
2022-06-01 21:15:22 +00:00
" part_student.student " : " Student " ,
" part_student.group.number " : " Group number " ,
2021-07-31 22:41:21 +00:00
" part_student.student.uni_email " : " Uni Email " ,
" part_student.student.contact_email " : " Contact Email " ,
" part_student.part " : " Part " ,
}
column_descriptions = {
2022-04-11 23:44:53 +00:00
" oral_mark " : f " Between { MIN_MARK } and { MAX_MARK } " ,
" protocol_mark " : f " Between { MIN_MARK } and { MAX_MARK } " ,
2021-08-29 16:15:14 +00:00
" final_experiment_mark " : " Calculated automatically with oral and protocol marks and weightings " ,
2021-07-31 22:41:21 +00:00
" part_student.student.contact_email " : " The preferred contact email address if entered by the student " ,
2022-02-27 18:30:28 +00:00
" assistant " : " The last assistant who edited the mark " ,
" admin " : " The last admin who edited the mark " ,
2021-07-31 22:41:21 +00:00
}
2022-05-30 02:29:02 +00:00
column_searchable_list = [
" part_student.student.first_name " ,
" part_student.student.last_name " ,
" part_student.student.uni_email " ,
" part_student.student.contact_email " ,
]
2022-06-01 21:57:28 +00:00
column_formatters = {
" oral_mark " : missing_formatter ,
" protocol_mark " : missing_formatter ,
" part_student.part " : str_without_semester_formatter ,
}
2021-07-30 00:50:49 +00:00
2021-07-30 21:50:10 +00:00
column_default_sort = [ ( " oral_mark " , False ) , ( " protocol_mark " , False ) ]
2021-07-30 00:50:49 +00:00
2022-05-21 16:30:23 +00:00
def query_modifier ( self , query ) :
return (
query . join ( GroupExperiment )
. join ( SemesterExperiment )
. where ( SemesterExperiment . semester == current_user . active_semester )
. join ( SemesterExperiment . assistants )
. where ( Assistant . user == current_user )
2021-07-30 00:50:49 +00:00
)
2021-08-21 21:08:32 +00:00
def update_model ( self , form , model ) :
2022-05-15 20:25:19 +00:00
if ( form . oral_mark is not None and form . oral_mark . data != model . oral_mark ) or (
form . protocol_mark is not None and form . protocol_mark . data != model . protocol_mark
2021-08-21 21:08:32 +00:00
) :
model . assistant = current_user . assistant
2021-07-30 00:50:49 +00:00
2022-05-15 20:25:19 +00:00
updateSuccessful = super ( ) . update_model ( form , model )
2021-07-30 00:50:49 +00:00
2021-08-21 21:08:32 +00:00
model . part_student . checkThenSetFinalPartMark ( )
2022-05-15 20:25:19 +00:00
return updateSuccessful
2021-08-21 21:08:32 +00:00
else :
# Nothing changed
return True
2021-07-30 00:50:49 +00:00
2021-07-30 21:50:10 +00:00
2021-11-30 00:47:37 +00:00
class AssistantUserView ( SecureAssistantModelView ) :
2022-06-20 01:08:57 +00:00
class EditForm ( FlaskForm ) :
2022-06-17 18:56:14 +00:00
phone_number , mobile_phone_number , building , room = user_info_fields ( )
2022-02-23 19:36:29 +00:00
2022-06-17 18:56:14 +00:00
generate_new_password = generate_new_password_field ( )
2022-02-23 19:36:29 +00:00
can_edit = True
2022-05-30 13:38:20 +00:00
can_view_details = True
2022-02-23 19:36:29 +00:00
column_display_actions = True
2021-11-30 00:47:37 +00:00
column_sortable_list = [ ]
column_list = [
" email " ,
" phone_number " ,
" mobile_phone_number " ,
" building " ,
2022-02-23 19:36:29 +00:00
" room " ,
2021-11-30 00:47:37 +00:00
" assistant.semester_experiments " ,
]
column_labels = {
" assistant.semester_experiments " : " Semester Experiments " ,
}
2022-05-21 16:30:23 +00:00
def query_modifier ( self , query ) :
return query . where ( User . id == current_user . id )
2021-11-30 00:47:37 +00:00
2022-02-23 19:36:29 +00:00
def on_model_change ( self , form , model , is_created ) :
if form . generate_new_password . data :
password = randomPassword ( )
flashRandomPassword ( password )
admin_change_password ( model , password , notify = False ) # Password is automatically hashed with this function
2021-11-30 00:47:37 +00:00
2022-02-23 18:37:09 +00:00
class AssistantDocsView ( SecureAssistantBaseView ) :
2022-03-04 02:49:02 +00:00
@expose ( " / " )
2022-02-23 18:37:09 +00:00
def index ( self ) :
2022-05-30 15:02:00 +00:00
return self . render ( " docs/docs.html " , role = " assistant " )
2022-02-23 18:37:09 +00:00
2022-06-17 20:20:42 +00:00
assistantSpace . add_view (
AssistantGroupExperimentView (
GroupExperiment , db . session , endpoint = " assistant_groupexperiment " , url = " group_experiment "
)
)
2021-07-30 21:50:10 +00:00
assistantSpace . add_view (
AssistantAppointmentView ( Appointment , db . session , endpoint = " assistant_appointment " , url = " appointment " )
)
2021-07-30 00:50:49 +00:00
assistantSpace . add_view (
2022-05-30 15:02:00 +00:00
AssistantExperimentMarkView ( ExperimentMark , db . session , endpoint = " assistant_experimentmark " , url = " experiment_mark " )
2021-07-30 00:50:49 +00:00
)
2021-11-30 00:47:37 +00:00
assistantSpace . add_view ( AssistantUserView ( User , db . session , endpoint = " assistant_user " , url = " user " ) )
2022-02-23 18:37:09 +00:00
assistantSpace . add_view ( AssistantDocsView ( name = " Docs " , endpoint = " assistant_docs " , url = " docs " ) )
2021-07-30 22:12:37 +00:00
initActiveSemesterMenuLinks ( assistantSpace )