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

1import glob 

2import os 

3from collections import defaultdict 

4 

5from nbgrader.api import Gradebook, MissingEntry 

6from nbgrader.exchange.default.collect import ExchangeCollect, groupby 

7from nbgrader.utils import check_mode 

8from traitlets import Bool 

9 

10from .exchange import E2xExchange 

11 

12 

13class E2xExchangeCollect(E2xExchange, ExchangeCollect): 

14 update = Bool( 

15 False, help="Update existing submissions with ones that have newer timestamps." 

16 ).tag(config=True) 

17 

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) 

25 

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) 

32 

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}") 

43 

44 usergroups = defaultdict(list) 

45 records = [] 

46 

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"]) 

57 

58 usergroups.update(groupby(user_records, lambda item: item["username"])) 

59 records.append(user_records) 

60 

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"]) 

69 

70 return records, usergroups 

71 

72 def init_src(self): 

73 if self.coursedir.course_id == "": 

74 self.fail("No course id specified. Re-run with --course flag.") 

75 

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 ) 

86 

87 records, usergroups = self.init_submissions() 

88 

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])