Skip to content

Commit 6331cfa

Browse files
committed
Move get_username, submission_open, and others to meta.py
1 parent 26d2333 commit 6331cfa

File tree

3 files changed

+107
-104
lines changed

3 files changed

+107
-104
lines changed

examples/5_import_funcs/test_clock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from jmu_pytest_utils.common import assert_pep8, assert_docs
2-
from jmu_pytest_utils.common import submission_open, postpone_tests
32
from jmu_pytest_utils.decorators import weight
3+
from jmu_pytest_utils.meta import submission_open, postpone_tests
44

55
from clock import time_str, add_time
66

jmu_pytest_utils/common.py

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"""Common test functions used in many autograders."""
22

3-
from datetime import datetime, timedelta, timezone
43
import inspect
5-
import json
64
import os
75
import pytest
86
import subprocess
@@ -22,23 +20,6 @@ def chdir_test():
2220
break
2321

2422

25-
def get_username(default="username"):
26-
"""Get the student's username from the submission metadata.
27-
28-
Args:
29-
default (str): Value to return if metadata not found.
30-
31-
Returns:
32-
str: The student's email address up to the @ symbol.
33-
"""
34-
try:
35-
with open("/autograder/submission_metadata.json") as file:
36-
metadata = json.load(file)
37-
return metadata["users"][0]["email"].split("@")[0]
38-
except FileNotFoundError:
39-
return default
40-
41-
4223
def _get_cfg(filename):
4324
"""Get the path of an optional configuration file.
4425
@@ -133,87 +114,3 @@ def run_module(filename, input=None):
133114
str: Captured output from the child process.
134115
"""
135116
return run_command(["python", filename], input)
136-
137-
138-
def submission_open(before=5, after=5):
139-
"""Check if the current time is within the user's submission window.
140-
141-
The window starts at the release date and ends at the due date (or late
142-
due date, if set). This function applies a tolerance by including times:
143-
* `before` minutes prior to the release date
144-
* `after` minutes beyond the due/late date
145-
146-
Args:
147-
before (int): Minutes to extend the window before the release date.
148-
after (int): Minutes to extend the window after the due/late date.
149-
150-
Returns:
151-
bool: True if the current time is within the submission window.
152-
"""
153-
154-
# Get the submission metadata
155-
try:
156-
with open("/autograder/submission_metadata.json") as file:
157-
metadata = json.load(file)
158-
except FileNotFoundError:
159-
return False
160-
161-
# Get the assignment's dates specific to the user
162-
assignment = metadata["users"][0]["assignment"]
163-
beg = datetime.fromisoformat(assignment["release_date"])
164-
end = datetime.fromisoformat(assignment["due_date"])
165-
late = assignment.get("late_due_date")
166-
if late is not None:
167-
end = datetime.fromisoformat(late)
168-
169-
# Extend the submission window
170-
beg -= timedelta(minutes=before)
171-
end += timedelta(minutes=after)
172-
173-
# Compare with current time
174-
now = datetime.now(timezone.utc)
175-
return beg <= now <= end
176-
177-
178-
def submission_closed():
179-
"""Check if the current time is outside the user's submission window.
180-
181-
See submission_open() for more details. This function returns the opposite.
182-
"""
183-
return not submission_open()
184-
185-
186-
def postpone_tests(
187-
title="Ready to grade",
188-
message="Your submission has been received and will be graded manually.",
189-
):
190-
"""Replace all tests in the calling test module with a single placeholder.
191-
192-
This function is used to hide autograder feedback during an assignment or
193-
exam. It deletes all existing test functions and defines a new function
194-
named `test_postpone` that carries the given title and message.
195-
196-
Args:
197-
title (str): Docstring for the new test (rendered as the test's name).
198-
message (str): Message assigned to the new test's `output` attribute.
199-
"""
200-
201-
# Get the test module
202-
for frameinfo in inspect.stack():
203-
module = inspect.getmodule(frameinfo.frame)
204-
if module and module.__name__.startswith("test_"):
205-
test_module = module
206-
break
207-
208-
# Delete all test functions
209-
g = test_module.__dict__
210-
for name in list(g.keys()):
211-
if name.startswith("test_"):
212-
del g[name]
213-
214-
# Define the fallback test
215-
def test_postpone():
216-
test_postpone.__doc__ = title
217-
test_postpone.output = message
218-
219-
g["test_postpone"] = test_postpone

jmu_pytest_utils/meta.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""Functions that use the submission metadata."""
2+
3+
from datetime import datetime, timedelta, timezone
4+
import inspect
5+
import json
6+
7+
8+
def get_username(default="username"):
9+
"""Get the student's username from the submission metadata.
10+
11+
Args:
12+
default (str): Value to return if metadata not found.
13+
14+
Returns:
15+
str: The student's email address up to the @ symbol.
16+
"""
17+
try:
18+
with open("/autograder/submission_metadata.json") as file:
19+
metadata = json.load(file)
20+
return metadata["users"][0]["email"].split("@")[0]
21+
except FileNotFoundError:
22+
return default
23+
24+
25+
def submission_open(before=5, after=5):
26+
"""Check if the current time is within the user's submission window.
27+
28+
The window starts at the release date and ends at the due date (or late
29+
due date, if set). This function applies a tolerance by including times:
30+
* `before` minutes prior to the release date
31+
* `after` minutes beyond the due/late date
32+
33+
Args:
34+
before (int): Minutes to extend the window before the release date.
35+
after (int): Minutes to extend the window after the due/late date.
36+
37+
Returns:
38+
bool: True if the current time is within the submission window.
39+
"""
40+
41+
# Get the submission metadata
42+
try:
43+
with open("/autograder/submission_metadata.json") as file:
44+
metadata = json.load(file)
45+
except FileNotFoundError:
46+
return False
47+
48+
# Get the assignment's dates specific to the user
49+
assignment = metadata["users"][0]["assignment"]
50+
beg = datetime.fromisoformat(assignment["release_date"])
51+
end = datetime.fromisoformat(assignment["due_date"])
52+
late = assignment.get("late_due_date")
53+
if late is not None:
54+
end = datetime.fromisoformat(late)
55+
56+
# Extend the submission window
57+
beg -= timedelta(minutes=before)
58+
end += timedelta(minutes=after)
59+
60+
# Compare with current time
61+
now = datetime.now(timezone.utc)
62+
return beg <= now <= end
63+
64+
65+
def submission_closed():
66+
"""Check if the current time is outside the user's submission window.
67+
68+
See submission_open() for more details. This function returns the opposite.
69+
"""
70+
return not submission_open()
71+
72+
73+
def postpone_tests(
74+
title="Ready to grade",
75+
message="Your submission has been received and will be graded manually.",
76+
):
77+
"""Replace all tests in the calling test module with a single placeholder.
78+
79+
This function is used to hide autograder feedback during an assignment or
80+
exam. It deletes all existing test functions and defines a new function
81+
named `test_postpone` that carries the given title and message.
82+
83+
Args:
84+
title (str): Docstring for the new test (rendered as the test's name).
85+
message (str): Message assigned to the new test's `output` attribute.
86+
"""
87+
88+
# Get the test module
89+
for frameinfo in inspect.stack():
90+
module = inspect.getmodule(frameinfo.frame)
91+
if module and module.__name__.startswith("test_"):
92+
test_module = module
93+
break
94+
95+
# Delete all test functions
96+
g = test_module.__dict__
97+
for name in list(g.keys()):
98+
if name.startswith("test_"):
99+
del g[name]
100+
101+
# Define the fallback test
102+
def test_postpone():
103+
test_postpone.__doc__ = title
104+
test_postpone.output = message
105+
106+
g["test_postpone"] = test_postpone

0 commit comments

Comments
 (0)