Coverage for e2xgrader/server_extensions/apps/formgrader/handlers.py: 38%
130 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 os
3from e2xcore import format_url
4from nbgrader.api import MissingEntry
5from nbgrader.server_extensions.formgrader.base import (
6 BaseHandler,
7 check_notebook_dir,
8 check_xsrf,
9)
10from nbgrader.server_extensions.formgrader.handlers import (
11 SubmissionNavigationHandler as NbgraderSubmissionNavigationHandler,
12)
13from tornado import web
16class ExportGradesHandler(BaseHandler):
17 @web.authenticated
18 @check_xsrf
19 def get(self):
20 html = self.render("export_grades.tpl", base_url=self.base_url)
21 self.write(html)
24class GradebookAssignmentsHandler(BaseHandler):
25 @web.authenticated
26 @check_xsrf
27 @check_notebook_dir
28 def get(self):
29 view = self.get_argument("view", "notebook")
30 if view == "task":
31 template = os.path.join("task_view", "gradebook_assignments.tpl")
32 else:
33 template = "gradebook_assignments.tpl"
34 html = self.render(template, base_url=self.base_url)
35 self.write(html)
38class GradebookNotebooksHandler(BaseHandler):
39 @web.authenticated
40 @check_xsrf
41 @check_notebook_dir
42 def get(self, assignment_id):
43 view = self.get_argument("view", "notebook")
44 if view == "task":
45 template = os.path.join("task_view", "gradebook_notebooks.tpl")
46 else:
47 template = "gradebook_notebooks.tpl"
48 html = self.render(
49 template, assignment_id=assignment_id, base_url=self.base_url
50 )
51 self.write(html)
54class GradebookTasksHandler(BaseHandler):
55 @web.authenticated
56 @check_xsrf
57 def get(self, assignment_id, notebook_id):
58 html = self.render(
59 "task_view/gradebook_tasks.tpl",
60 assignment_id=assignment_id,
61 notebook_id=notebook_id,
62 base_url=self.base_url,
63 )
64 self.write(html)
67class GradebookNotebookSubmissionsHandler(BaseHandler):
68 @web.authenticated
69 @check_xsrf
70 @check_notebook_dir
71 def get(self, assignment_id, notebook_id):
72 view = self.get_argument("view", "notebook")
73 if view == "task":
74 template = os.path.join("task_view", "gradebook_notebook_submissions.tpl")
75 else:
76 template = "gradebook_notebook_submissions.tpl"
77 task_id = self.get_argument("filter", "")
79 html = self.render(
80 template,
81 assignment_id=assignment_id,
82 notebook_id=notebook_id,
83 base_url=self.base_url,
84 task_id=task_id,
85 )
86 self.write(html)
89class SubmissionNavigationHandler(NbgraderSubmissionNavigationHandler):
90 def _assignment_notebook_list_url(self, assignment_id, notebook_id, task_id):
91 if len(task_id) < 1:
92 return "{}/formgrader/gradebook/{}/{}".format(
93 self.base_url, assignment_id, notebook_id
94 )
95 return format_url(
96 f"{self.base_url}/formgrader/gradebook/{assignment_id}/{notebook_id}/",
97 dict(view="task", filter=task_id),
98 )
100 def _next(self, assignment_id, notebook_id, submission, task_id):
101 # find next submission
102 submission_ids = self._get_submission_ids(assignment_id, notebook_id)
103 ix = submission_ids.index(submission.id)
104 if ix == (len(submission_ids) - 1):
105 return self._assignment_notebook_list_url(
106 assignment_id, notebook_id, task_id
107 )
108 else:
109 return format_url(
110 self._submission_url(submission_ids[ix + 1]), dict(task=task_id)
111 )
113 def _prev(self, assignment_id, notebook_id, submission, task_id):
114 # find previous submission
115 submission_ids = self._get_submission_ids(assignment_id, notebook_id)
116 ix = submission_ids.index(submission.id)
117 if ix == 0:
118 return self._assignment_notebook_list_url(
119 assignment_id, notebook_id, task_id
120 )
121 else:
122 return format_url(
123 self._submission_url(submission_ids[ix - 1]), dict(task=task_id)
124 )
126 def _next_incorrect(self, assignment_id, notebook_id, submission, task_id):
127 # find next incorrect submission
128 submission_ids = self._get_incorrect_submission_ids(
129 assignment_id, notebook_id, submission
130 )
131 ix_incorrect = submission_ids.index(submission.id)
132 if ix_incorrect == (len(submission_ids) - 1):
133 return self._assignment_notebook_list_url(
134 assignment_id, notebook_id, task_id
135 )
136 else:
137 return format_url(
138 self._submission_url(submission_ids[ix_incorrect + 1]),
139 dict(task=task_id),
140 )
142 def _prev_incorrect(self, assignment_id, notebook_id, submission, task_id):
143 # find previous incorrect submission
144 submission_ids = self._get_incorrect_submission_ids(
145 assignment_id, notebook_id, submission
146 )
147 ix_incorrect = submission_ids.index(submission.id)
148 if ix_incorrect == 0:
149 return self._assignment_notebook_list_url(
150 assignment_id, notebook_id, task_id
151 )
152 else:
153 return format_url(
154 self._submission_url(submission_ids[ix_incorrect - 1]),
155 dict(task=task_id),
156 )
158 @web.authenticated
159 @check_xsrf
160 @check_notebook_dir
161 def get(self, submission_id, action):
162 try:
163 submission = self.gradebook.find_submission_notebook_by_id(submission_id)
164 assignment_id = submission.assignment.assignment.name
165 notebook_id = submission.notebook.name
166 task_id = self.get_argument("task", "")
167 except MissingEntry:
168 raise web.HTTPError(404, "Invalid submission: {}".format(submission_id))
170 handler = getattr(self, "_{}".format(action))
171 self.redirect(
172 handler(assignment_id, notebook_id, submission, task_id), permanent=False
173 )
176class SubmissionHandler(BaseHandler):
177 @web.authenticated
178 @check_xsrf
179 @check_notebook_dir
180 def get(self, submission_id):
181 try:
182 submission = self.gradebook.find_submission_notebook_by_id(submission_id)
183 assignment_id = submission.assignment.assignment.name
184 notebook_id = submission.notebook.name
185 student_id = submission.student.id
186 except MissingEntry:
187 raise web.HTTPError(404, "Invalid submission: {}".format(submission_id))
189 # redirect if there isn't a trailing slash in the uri
190 if os.path.split(self.request.path)[1] == submission_id:
191 url = self.request.path + "/"
192 if self.request.query:
193 url += "?" + self.request.query
194 return self.redirect(url, permanent=True)
196 filename = os.path.join(
197 os.path.abspath(
198 self.coursedir.format_path(
199 self.coursedir.autograded_directory, student_id, assignment_id
200 )
201 ),
202 "{}.ipynb".format(notebook_id),
203 )
204 relative_path = os.path.relpath(filename, self.coursedir.root)
205 indices = self.api.get_notebook_submission_indices(assignment_id, notebook_id)
206 ix = indices.get(submission.id, -2)
208 task_id = self.get_argument("task", "")
210 resources = {
211 "assignment_id": assignment_id,
212 "notebook_id": notebook_id,
213 "submission_id": submission.id,
214 "index": ix,
215 "total": len(indices),
216 "base_url": self.base_url,
217 "student": student_id,
218 "last_name": submission.student.last_name,
219 "first_name": submission.student.first_name,
220 "notebook_path": self.url_prefix + "/" + relative_path,
221 "keyword": task_id,
222 }
224 if not os.path.exists(filename):
225 resources["filename"] = filename
226 html = self.render("formgrade_404.tpl", resources=resources)
227 self.clear()
228 self.set_status(404)
229 self.write(html)
231 else:
232 html, _ = self.exporter.from_filename(filename, resources=resources)
233 self.write(html)
236root_path = os.path.dirname(__file__)
237template_path = os.path.join(root_path, "templates")
238static_path = os.path.join(root_path, "static")
240_navigation_regex = r"(?P<action>next_incorrect|prev_incorrect|next|prev)"
242default_handlers = [
243 (r"/formgrader/export_grades/?", ExportGradesHandler),
244 (r"/formgrader/gradebook/?", GradebookAssignmentsHandler),
245 (r"/formgrader/gradebook/([^/]+)/?", GradebookNotebooksHandler),
246 (r"/formgrader/gradebook/tasks/([^/]+)/([^/]+)/?", GradebookTasksHandler),
247 (r"/formgrader/gradebook/([^/]+)/([^/]+)/?", GradebookNotebookSubmissionsHandler),
248 (
249 r"/formgrader/submissions/(?P<submission_id>[^/]+)/%s/?" % _navigation_regex,
250 SubmissionNavigationHandler,
251 ),
252 (r"/formgrader/submissions/([^/]+)/?", SubmissionHandler),
253]