Skip to content

Commit 49f27a2

Browse files
author
Donny Wong
committed
Merge branch 'master' into release_2.9.0
2 parents 6b9e495 + cf88709 commit 49f27a2

File tree

19 files changed

+165
-87
lines changed

19 files changed

+165
-87
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ __pycache__
99
venv
1010
venv2
1111
build
12+
docker-compose.override.yml
13+
/workspace

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
2-
- repo: https://github.com/psf/black
3-
rev: 25.1.0
2+
- repo: https://github.com/psf/black-pre-commit-mirror
3+
rev: 25.11.0
44
hooks:
55
- id: black
66
- repo: https://github.com/pycqa/flake8

Changelog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# CHANGELOG
22
All notable changes to this project will be documented here.
33

4+
## [v2.9.0]
5+
- Install stack with GHCup (#626)
6+
- Fixed AI tester to report error when the specified `submission` file is not found (#663)
7+
- Updated docker image to use Ubuntu 24.04 (#668)
8+
- Fixed stack installation in Docker environment (#668)
9+
- Removed `click` from server requirements.txt file (#679)
10+
- Fixed bug in R tester setup that always triggered reinstallation of R dependencies (#680)
11+
412
## [v2.8.3]
513
- Add troubleshooting section talking about Docker Content Trust (DCT) (#653)
614
- Fixed Python tester to display fully qualified name when running pytest (#656)
@@ -22,6 +30,7 @@ All notable changes to this project will be documented here.
2230
- Add `ai_tester` module to support AI-based autograding via `ai-autograding-feedback` (#625)
2331
- Add `ai` to list of testers (#628)
2432
- Fixed an `AttributeError` when handling exceptions in server `update_test_settings` (#629)
33+
- Add tag functionalty to `ai_tester` (#631)
2534
- Added opt out feature to the `ai_tester` by searching for `NO_EXTERNAL_AI_FEEDBACK` (#632)
2635
- Modified R tester to always display test result messages, even when tests pass (#633)
2736

client/.dockerfiles/Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
ARG UBUNTU_VERSION=22.04
1+
ARG UBUNTU_VERSION=24.04
22

33
FROM ubuntu:$UBUNTU_VERSION
44

5+
# Remove ubuntu user, added in the 23.04 image
6+
RUN userdel -r ubuntu
7+
58
RUN apt-get update -y && \
69
DEBIAN_FRONTEND=noninteractive apt-get -y install software-properties-common && \
710
DEBIAN_FRONTEND=noninteractive add-apt-repository -y ppa:deadsnakes/ppa && \

client/requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
flask==3.1.1
2-
python-dotenv==1.1.1
3-
rq==2.4.1
4-
redis==6.2.0
5-
jsonschema==4.25.0
1+
flask==3.1.2
2+
python-dotenv==1.2.1
3+
rq==2.6.0
4+
redis==7.1.0
5+
jsonschema==4.25.1
66
Werkzeug==3.1.3

compose.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ services:
44
context: ./server
55
dockerfile: ./.dockerfiles/Dockerfile
66
args:
7-
UBUNTU_VERSION: '22.04'
7+
UBUNTU_VERSION: '24.04'
88
LOGIN_USER: 'docker'
99
WORKSPACE: '/home/docker/.autotesting'
10-
image: markus-autotest-server-dev:1.3.0
10+
image: markus-autotest-server-dev:1.4.0
1111
volumes:
1212
- ./server:/app:cached
1313
- venv_server:/home/docker/markus_venv
@@ -28,8 +28,8 @@ services:
2828
context: ./client
2929
dockerfile: ./.dockerfiles/Dockerfile
3030
args:
31-
UBUNTU_VERSION: '22.04'
32-
image: markus-autotest-client-dev:1.3.0
31+
UBUNTU_VERSION: '24.04'
32+
image: markus-autotest-client-dev:1.4.0
3333
container_name: 'autotest-client'
3434
volumes:
3535
- ./client:/app:cached

server/.dockerfiles/Dockerfile

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
ARG UBUNTU_VERSION=22.04
1+
ARG UBUNTU_VERSION=24.04
22

33
FROM ubuntu:$UBUNTU_VERSION AS base
44

5+
# Remove ubuntu user, added in the 23.04 image
6+
RUN userdel -r ubuntu
57

68
ARG LOGIN_USER
79
ARG WORKSPACE
@@ -29,6 +31,7 @@ RUN apt-get update -y && \
2931
libharfbuzz-dev \
3032
libfribidi-dev \
3133
libxml2-dev \
34+
libnuma-dev \
3235
r-base
3336

3437
RUN useradd -ms /bin/bash $LOGIN_USER && \
@@ -37,18 +40,17 @@ RUN useradd -ms /bin/bash $LOGIN_USER && \
3740
adduser --disabled-login --no-create-home $worker && \
3841
echo "$LOGIN_USER ALL=($worker) NOPASSWD:ALL" | EDITOR="tee -a" visudo && \
3942
usermod -aG $worker $LOGIN_USER; \
40-
done
41-
42-
RUN chmod a+x /home/${LOGIN_USER}
43+
done && \
44+
chmod a+x /home/${LOGIN_USER}
4345

4446
COPY . /app
4547

4648
RUN find /app/autotest_server/testers -name requirements.system -exec {} \;
4749

4850
RUN echo "TZ=$( cat /etc/timezone )" >> /etc/R/Renviron.site
4951

50-
RUN mkdir -p ${WORKSPACE} && chown ${LOGIN_USER} ${WORKSPACE}
51-
RUN mkdir -p /home/${LOGIN_USER}/markus_venv && chown ${LOGIN_USER} /home/${LOGIN_USER}/markus_venv
52+
RUN mkdir -p ${WORKSPACE} && chown ${LOGIN_USER} ${WORKSPACE} && \
53+
mkdir -p /home/${LOGIN_USER}/markus_venv && chown ${LOGIN_USER} /home/${LOGIN_USER}/markus_venv
5254

5355
WORKDIR /home/${LOGIN_USER}
5456

server/autotest_server/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def _create_test_group_result(
8383
result["annotations"] = res["annotations"]
8484
elif "tags" in res:
8585
result["tags"] = res["tags"]
86+
elif "extra_marks" in res:
87+
result["extra_marks"] = res["extra_marks"]
8688
elif "overall_comment" in res:
8789
result["overall_comment"] = res["overall_comment"]
8890
else:

server/autotest_server/testers/ai/ai_tester.py

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def __init__(
5353
super().__init__(specs, test_class, resource_settings=resource_settings)
5454
self.annotations = []
5555
self.overall_comments = []
56+
self.tags = []
5657

5758
def call_ai_feedback(self) -> dict:
5859
"""
@@ -81,7 +82,17 @@ def call_ai_feedback(self) -> dict:
8182
return results
8283

8384
submission_file = config.get("submission")
84-
if self._term_in_file(submission_file):
85+
try:
86+
disallowed_term_in_file = self._term_in_file(submission_file)
87+
except FileNotFoundError:
88+
results[test_label] = {
89+
"title": test_label,
90+
"status": "error",
91+
"message": f'Could not file submission file "{submission_file}"',
92+
}
93+
return results
94+
95+
if disallowed_term_in_file:
8596
results[test_label] = {
8697
"title": test_label,
8798
"status": "success",
@@ -96,15 +107,26 @@ def call_ai_feedback(self) -> dict:
96107
try:
97108
result = subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=timeout, env=env)
98109
output = result.stdout
110+
111+
parsed = None
112+
try:
113+
parsed = json.loads(output)
114+
except json.JSONDecodeError:
115+
pass
116+
if isinstance(parsed, dict):
117+
if "tags" in parsed:
118+
tags = parsed["tags"]
119+
self.tags.extend(tags)
120+
if "output" in parsed:
121+
output = parsed["output"]
122+
99123
if output_mode == "overall_comment":
100124
self.overall_comments.append(output)
101125
results[test_label] = {"title": test_label, "status": "success"}
102126
elif output_mode == "annotations":
103-
try:
104-
annotations_data = json.loads(output)
105-
annotations = annotations_data.get("annotations", annotations_data)
106-
except json.JSONDecodeError as e:
107-
raise ValueError(f"Invalid JSON in output for {test_label}: {e}")
127+
if parsed is None:
128+
raise ValueError(f"Unable to parse the output of '{output}'")
129+
annotations = parsed.get("annotations", parsed)
108130
self.annotations.extend(annotations)
109131
results[test_label] = {"title": test_label, "status": "success"}
110132
elif output_mode == "message":
@@ -122,23 +144,20 @@ def _term_in_file(self, file_path: str) -> bool:
122144
term = "NO_EXTERNAL_AI_FEEDBACK"
123145
path = Path(file_path)
124146

125-
try:
126-
if path.suffix.lower() == ".pdf":
127-
with open(file_path, "rb") as f:
128-
reader = PyPDF2.PdfReader(f)
129-
for page in reader.pages:
130-
text = page.extract_text() or ""
131-
if term in text:
132-
return True
133-
return False
134-
else:
135-
with open(file_path, "r", encoding="utf-8") as f:
136-
for line in f:
137-
if term in line:
138-
return True
139-
return False
140-
except FileNotFoundError:
141-
return True
147+
if path.suffix.lower() == ".pdf":
148+
with open(file_path, "rb") as f:
149+
reader = PyPDF2.PdfReader(f)
150+
for page in reader.pages:
151+
text = page.extract_text() or ""
152+
if term in text:
153+
return True
154+
return False
155+
else:
156+
with open(file_path, "r", encoding="utf-8") as f:
157+
for line in f:
158+
if term in line:
159+
return True
160+
return False
142161

143162
@Tester.run_decorator
144163
def run(self) -> None:
@@ -157,3 +176,5 @@ def after_tester_run(self) -> None:
157176
print(self.test_class.format_annotations(self.annotations))
158177
if self.overall_comments:
159178
print(self.test_class.format_overall_comment(self.overall_comments, separator="\n\n"))
179+
if self.tags:
180+
print(self.test_class.format_tags(self.tags))

server/autotest_server/testers/haskell/haskell_tester.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from ..tester import Tester, Test, TestError
88
from ..specs import TestSpecs
99

10+
home = os.getenv("HOME")
11+
os.environ["PATH"] = f"{home}/.cabal/bin:{home}/.ghcup/bin:" + os.environ["PATH"]
12+
1013

1114
class HaskellTest(Test):
1215
def __init__(

0 commit comments

Comments
 (0)