513
513
frozenset(['date', 'text']),
514
514
frozenset(['problemid', 'loginid']))
516
def _get_problem_status(self, login, exercisename, dry=False):
517
"""Given a login name and exercise name, returns information about the
518
user's performance on that problem.
520
- A boolean, whether they have successfully passed this exercise.
521
- An int, the number of attempts they have made up to and
522
including the first successful attempt (or the total number of
523
attempts, if not yet successful).
524
Note: exercisename may be an int, in which case it will be directly
525
used as the problemid.
527
if isinstance(exercisename, int):
528
problemid = exercisename
530
problemid = self.get_problem_problemid(exercisename)
531
loginid = self.get_user_loginid(login) # May raise a DBException
533
# ASSUME that it is completed, get the total number of attempts up to
534
# and including the first successful attempt.
535
# (Get the date of the first successful attempt. Then count the number
536
# of attempts made <= that date).
537
# Will return an empty table if the problem has never been
538
# successfully completed.
539
query = """SELECT COUNT(*) FROM problem_attempt
540
WHERE loginid = %d AND problemid = %d AND active AND date <=
541
(SELECT date FROM problem_attempt
542
WHERE loginid = %d AND problemid = %d AND complete AND active
544
LIMIT 1);""" % (loginid, problemid, loginid, problemid)
546
result = self.db.query(query)
547
count = int(result.getresult()[0][0])
549
# The user has made at least 1 successful attempt.
550
# Return True for success, and the number of attempts up to and
551
# including the successful one.
554
# Returned 0 rows - this indicates that the problem has not been
556
# Return the total number of attempts, and False for success.
557
query = """SELECT COUNT(*) FROM problem_attempt
558
WHERE loginid = %d AND problemid = %d AND active;""" % (loginid, problemid)
559
result = self.db.query(query)
560
count = int(result.getresult()[0][0])
561
return (False, count)
563
# WORKSHEET/PROBLEM ASSOCIATION AND MARKS CALCULATION
565
def calculate_score_worksheet(self, login, subject, worksheet):
567
Calculates the score for a user on a given worksheet.
568
Returns a 4-tuple of ints, consisting of:
569
(No. mandatory exercises completed,
570
Total no. mandatory exercises,
571
No. optional exercises completed,
572
Total no. optional exercises)
574
self.start_transaction()
580
# Get a list of problems and optionality for all problems in the
582
query = ("""SELECT problemid, optional FROM worksheet_problem
583
WHERE worksheetid = (SELECT worksheetid FROM worksheet
584
WHERE subject = %s and identifier = %s);"""
585
% (_escape(subject), _escape(worksheet)))
586
result = self.db.query(query)
587
# Now get the student's pass/fail for each problem in this worksheet
588
for problemid, optional in result.getresult():
589
done, _ = self._get_problem_status(login, problemid)
590
# done is a bool, whether this student has completed that
592
if _parse_boolean(optional):
594
if done: opt_done += 1
597
if done: mand_done += 1
602
return mand_done, mand_total, opt_done, opt_total
604
516
# ENROLMENT INFORMATION
606
518
def add_enrolment(self, login, subj_code, semester, year=None, dry=False):