Coverage for e2xgrader/exchange/release_feedback.py: 20%
54 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
3import re
4import shutil
5from stat import (
6 S_IRGRP,
7 S_IROTH,
8 S_IRUSR,
9 S_ISGID,
10 S_IWGRP,
11 S_IWOTH,
12 S_IWUSR,
13 S_IXGRP,
14 S_IXOTH,
15 S_IXUSR,
16)
18from nbgrader.exchange.default import ExchangeReleaseFeedback
19from nbgrader.utils import make_unique_key, notebook_hash
21from .exchange import E2xExchange
24class E2xExchangeReleaseFeedback(E2xExchange, ExchangeReleaseFeedback):
25 def init_dest(self):
26 """
27 Create exchange feedback destination
28 """
30 if self.coursedir.course_id == "":
31 self.fail("No course id specified. Re-run with --course flag.")
33 self.course_path = os.path.join(self.root, self.coursedir.course_id)
34 self.dest_path = os.path.join(self.course_path, self.feedback_directory)
36 if self.personalized_feedback:
37 # u+rwx, g+wx, o+wx
38 self.ensure_directory(
39 self.dest_path,
40 (
41 S_IRUSR
42 | S_IWUSR
43 | S_IXUSR
44 | S_IWGRP
45 | S_IXGRP
46 | S_IWOTH
47 | S_IXOTH
48 | (
49 (S_IRGRP | S_IWGRP | S_ISGID)
50 if self.coursedir.groupshared
51 else 0
52 )
53 ),
54 )
55 else:
56 # 0755
57 self.ensure_directory(
58 self.dest_path,
59 (
60 S_IRUSR
61 | S_IWUSR
62 | S_IXUSR
63 | S_IXGRP
64 | S_IXOTH
65 | (
66 (S_IRGRP | S_IWGRP | S_ISGID)
67 if self.coursedir.groupshared
68 else 0
69 )
70 ),
71 )
73 def copy_files(self):
74 """
75 Overried copy_files and add personalized-feedback generation
76 """
77 if self.coursedir.student_id_exclude:
78 exclude_students = set(self.coursedir.student_id_exclude.split(","))
79 else:
80 exclude_students = set()
82 html_files = glob.glob(os.path.join(self.src_path, "*.html"))
83 for html_file in html_files:
84 if "hashcode" in html_file:
85 self.log.debug("Skipping hashcode info")
86 continue
87 regexp = re.escape(os.path.sep).join(
88 [
89 self.coursedir.format_path(
90 self.coursedir.feedback_directory,
91 "(?P<student_id>.*)",
92 self.coursedir.assignment_id,
93 escape=True,
94 ),
95 "(?P<notebook_id>.*).html",
96 ]
97 )
99 m = re.match(regexp, html_file)
100 if m is None:
101 msg = "Could not match '%s' with regexp '%s'" % (html_file, regexp)
102 self.log.error(msg)
103 continue
105 gd = m.groupdict()
106 student_id = gd["student_id"]
107 notebook_id = gd["notebook_id"]
108 if student_id in exclude_students:
109 self.log.debug("Skipping student '{}'".format(student_id))
110 continue
112 feedback_dir = os.path.split(html_file)[0]
113 submission_dir = self.coursedir.format_path(
114 self.coursedir.submitted_directory,
115 student_id,
116 self.coursedir.assignment_id,
117 )
119 if self.personalized_feedback:
120 dest = os.path.join(
121 self.dest_path, student_id, self.coursedir.assignment_id
122 )
123 # u+rwx, g+wx, o+wx
124 self.ensure_directory(
125 dest,
126 (
127 S_IRUSR
128 | S_IWUSR
129 | S_IXUSR
130 | S_IRGRP
131 | S_IXGRP
132 | S_IXOTH
133 | S_IROTH
134 | (
135 (S_IRGRP | S_IWGRP | S_ISGID)
136 if self.coursedir.groupshared
137 else 0
138 )
139 ),
140 )
142 dest = os.path.join(dest, notebook_id + ".html")
144 self.log.info(
145 "Releasing feedback for student '{}' on assignment '{}/{}/{}' ".format(
146 student_id,
147 self.coursedir.course_id,
148 self.coursedir.assignment_id,
149 notebook_id,
150 )
151 )
152 else:
153 timestamp = open(os.path.join(feedback_dir, "timestamp.txt")).read()
154 nbfile = os.path.join(submission_dir, "{}.ipynb".format(notebook_id))
155 unique_key = make_unique_key(
156 self.coursedir.course_id,
157 self.coursedir.assignment_id,
158 notebook_id,
159 student_id,
160 timestamp,
161 )
163 self.log.debug("Unique key is: {}".format(unique_key))
164 checksum = notebook_hash(nbfile, unique_key)
165 dest = os.path.join(self.dest_path, "{}.html".format(checksum))
167 self.log.info(
168 "Releasing feedback for student '{}' on assignment '{}/{}/{}' ({})".format(
169 student_id,
170 self.coursedir.course_id,
171 self.coursedir.assignment_id,
172 notebook_id,
173 timestamp,
174 )
175 )
177 shutil.copy(html_file, dest)
178 self.log.info("Feedback released to: {}".format(dest))