598
629
count = int(result.getresult()[0][0])
599
630
return (False, count)
632
# WORKSHEET/PROBLEM ASSOCIATION AND MARKS CALCULATION
634
def get_worksheet_mtime(self, subject, worksheet, dry=False):
636
For a given subject/worksheet name, gets the time the worksheet was
637
last updated in the DB, if any.
638
This can be used to check if there is a newer version on disk.
639
Returns the timestamp as a time.struct_time, or None if the worksheet
640
is not found or has no stored mtime.
644
{"subject": subject, "identifier": worksheet},
645
"worksheet", ["mtime"], ["subject", "identifier"],
648
# Assume the worksheet is not in the DB
652
if r["mtime"] is None:
654
return time.strptime(r["mtime"], TIMESTAMP_FORMAT)
656
def create_worksheet(self, subject, worksheet, problems,
659
Inserts or updates rows in the worksheet and worksheet_problems
660
tables, to create a worksheet in the database.
661
This atomically performs all operations. If the worksheet is already
662
in the DB, removes it and all its associated problems and rebuilds.
663
Sets the timestamp to the current time.
665
problems is a collection of pairs. The first element of the pair is
666
the problem identifier ("identifier" column of the problem table). The
667
second element is an optional boolean, "optional". This can be omitted
668
(so it's a 1-tuple), and then it will default to False.
670
Note: As with get_problem_problemid, if a problem name is not in the
671
DB, it will be added to the problem table.
673
self.start_transaction()
675
# Use the current time as the "mtime" field
676
mtime = time.localtime()
678
# Get the worksheetid
680
{"subject": subject, "identifier": worksheet},
681
"worksheet", ["worksheetid"], ["subject", "identifier"])
682
worksheetid = r["worksheetid"]
684
# Delete any problems which might exist
685
query = ("DELETE FROM worksheet_problem "
686
"WHERE worksheetid = %d;" % worksheetid)
688
# Update the row with the new details
689
query = ("UPDATE worksheet "
690
"SET assessable = %s, mtime = %s "
691
"WHERE worksheetid = %d;"
692
% (_escape(assessable), _escape(mtime), worksheetid))
695
# Assume the worksheet is not in the DB
696
# Create the worksheet row
697
query = ("INSERT INTO worksheet "
698
"(subject, identifier, assessable, mtime) "
699
"VALUES (%s, %s, %s, %s);"""
700
% (_escape(subject), _escape(worksheet),
701
_escape(assessable), _escape(mtime)))
703
# Now get the worksheetid again - should succeed
705
{"subject": subject, "identifier": worksheet},
706
"worksheet", ["worksheetid"], ["subject", "identifier"])
707
worksheetid = r["worksheetid"]
709
# Now insert each problem into the worksheet_problem table
710
for problem in problems:
711
if isinstance(problem, tuple):
712
prob_identifier = problem[0]
714
optional = problem[1]
718
prob_identifier = problem
720
problemid = self.get_problem_problemid(prob_identifier)
721
query = ("INSERT INTO worksheet_problem "
722
"(worksheetid, problemid, optional) "
723
"VALUES (%d, %d, %s);"
724
% (worksheetid, problemid, _escape(optional)))
732
def worksheet_is_assessable(self, subject, worksheet, dry=False):
734
{"subject": subject, "identifier": worksheet},
735
"worksheet", ["assessable"], ["subject", "identifier"], dry=dry)
736
return _parse_boolean(r["assessable"])
738
def calculate_score_worksheet(self, login, subject, worksheet):
740
Calculates the score for a user on a given worksheet.
741
Returns a 4-tuple of ints, consisting of:
742
(No. mandatory exercises completed,
743
Total no. mandatory exercises,
744
No. optional exercises completed,
745
Total no. optional exercises)
747
self.start_transaction()
753
# Get a list of problems and optionality for all problems in the
755
query = ("""SELECT problemid, optional FROM worksheet_problem
756
WHERE worksheetid = (SELECT worksheetid FROM worksheet
757
WHERE subject = %s and identifier = %s);"""
758
% (_escape(subject), _escape(worksheet)))
759
result = self.db.query(query)
760
# Now get the student's pass/fail for each problem in this worksheet
761
for problemid, optional in result.getresult():
762
done, _ = self.get_problem_status(login, problemid)
763
# done is a bool, whether this student has completed that
765
if _parse_boolean(optional):
767
if done: opt_done += 1
770
if done: mand_done += 1
775
return mand_done, mand_total, opt_done, opt_total
602
778
"""Close the DB connection. Do not call any other functions after
603
779
this. (The behaviour of doing so is undefined).