Coverage for e2xgrader/exchange/collect.py: 23%
60 statements
« prev ^ index » next coverage.py v7.4.2, created at 2024-03-14 13:22 +0100
« prev ^ index » next coverage.py v7.4.2, created at 2024-03-14 13:22 +0100
1import glob
2import os
3from collections import defaultdict
5from nbgrader.api import Gradebook, MissingEntry
6from nbgrader.exchange.default.collect import ExchangeCollect, groupby
7from nbgrader.utils import check_mode
8from traitlets import Bool
10from .exchange import E2xExchange
13class E2xExchangeCollect(E2xExchange, ExchangeCollect):
14 update = Bool(
15 False, help="Update existing submissions with ones that have newer timestamps."
16 ).tag(config=True)
18 before_duedate = Bool(
19 False,
20 help=(
21 "Collect the last submission before due date or the last submission\n"
22 "if no submission before due date."
23 ),
24 ).tag(config=True)
26 check_owner = Bool(
27 default_value=True,
28 help=(
29 "Whether to cross-check the student_id with the UNIX-owner of the submitted directory."
30 ),
31 ).tag(config=True)
33 def init_submissions(self):
34 if self.personalized_inbound:
35 self.log.info("Collecting from restricted submit dirs")
36 submit_dirs = [
37 username
38 for username in os.listdir(self.inbound_path)
39 if "+" not in username
40 and os.path.isdir(os.path.join(self.inbound_path, username))
41 ]
42 self.log.info(f"Submission dirs: {submit_dirs}")
44 usergroups = defaultdict(list)
45 records = []
47 for user in submit_dirs:
48 submit_path = os.path.join(self.inbound_path, user)
49 self.log.info(f"Assignment id: {self.coursedir.assignment_id}")
50 pattern = os.path.join(
51 submit_path, f"{user}+{self.coursedir.assignment_id}+*"
52 )
53 user_records = [self._path_to_record(f) for f in glob.glob(pattern)]
54 self.log.info(f"{user} has {len(user_records)} submissions.")
55 for i, record in enumerate(user_records):
56 user_records[i]["filename"] = os.path.join(user, record["filename"])
58 usergroups.update(groupby(user_records, lambda item: item["username"]))
59 records.append(user_records)
61 else:
62 student_id = self.coursedir.student_id if self.coursedir.student_id else "*"
63 pattern = os.path.join(
64 self.inbound_path,
65 "{}+{}+*".format(student_id, self.coursedir.assignment_id),
66 )
67 records = [self._path_to_record(f) for f in glob.glob(pattern)]
68 usergroups = groupby(records, lambda item: item["username"])
70 return records, usergroups
72 def init_src(self):
73 if self.coursedir.course_id == "":
74 self.fail("No course id specified. Re-run with --course flag.")
76 self.course_path = os.path.join(self.root, self.coursedir.course_id)
77 self.inbound_path = os.path.join(self.course_path, self.inbound_directory)
78 if not os.path.isdir(self.inbound_path):
79 self.fail("Course not found: {}".format(self.inbound_path))
80 if not check_mode(self.inbound_path, read=True, execute=True):
81 self.fail(
82 "You don't have read permissions for the directory: {}".format(
83 self.inbound_path
84 )
85 )
87 records, usergroups = self.init_submissions()
89 with Gradebook(self.coursedir.db_url, self.coursedir.course_id) as gb:
90 try:
91 assignment = gb.find_assignment(self.coursedir.assignment_id)
92 self.duedate = assignment.duedate
93 except MissingEntry:
94 self.duedate = None
95 if self.duedate is None or not self.before_duedate:
96 self.src_records = [
97 self._sort_by_timestamp(v)[0] for v in usergroups.values()
98 ]
99 else:
100 self.log.info(
101 "Duedate is enabled. Collecting assignments submitted before duedate..."
102 )
103 self.src_records = []
104 for v in usergroups.values():
105 records = self._sort_by_timestamp(v)
106 records_before_duedate = [
107 record for record in records if record["timestamp"] <= self.duedate
108 ]
109 if records_before_duedate:
110 self.src_records.append(records_before_duedate[0])
111 else:
112 self.src_records.append(records[0])