assignment-codeval 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/PKG-INFO +8 -6
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/README.md +7 -5
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/pyproject.toml +1 -1
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/canvas_utils.py +13 -2
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/cli.py +3 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/commons.py +1 -1
- assignment_codeval-0.0.3/src/assignment_codeval/convertMD2Html.py +62 -0
- assignment_codeval-0.0.3/src/assignment_codeval/create_assignment.py +169 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/evaluate.py +21 -19
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/github_connect.py +1 -1
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/submissions.py +21 -13
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/PKG-INFO +8 -6
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/SOURCES.txt +2 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/setup.cfg +0 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/__init__.py +0 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/file_utils.py +0 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/dependency_links.txt +0 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/entry_points.txt +0 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/requires.txt +0 -0
- {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: assignment-codeval
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: CodEval for evaluating programming assignments
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -185,7 +185,7 @@ DISCSN_URL
|
|
|
185
185
|
|
|
186
186
|
EXMPLS <no_of_test_cases>
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
FILE[file_name]
|
|
189
189
|
|
|
190
190
|
### MODIFICATIONS REQUIRED IN THE SPECIFICATION FILE.
|
|
191
191
|
1) Start the specification file with the tag CRT_HW START followed by a space followed by the name of assignment.
|
|
@@ -208,12 +208,13 @@ URL_OF_HW "file_name"
|
|
|
208
208
|
1) In order to add hyperlink to a file the markdown format is as follows:
|
|
209
209
|
[file_name_to_be_displayed](Url_of_the_file)
|
|
210
210
|
Here in the parenthesis where the Url is required,insert the tag
|
|
211
|
-
|
|
212
|
-
For ex:
|
|
213
|
-
|
|
211
|
+
FILE[name of file].
|
|
212
|
+
For ex: FILE[file_name.extension]
|
|
213
|
+
If the file is not already in the Codeval folder, it will be extracted from a zip file in the
|
|
214
|
+
CodEval spec and uploaded automatically.
|
|
214
215
|
|
|
215
216
|
### UPLOAD THE REQUIRED FILES IN CODEVAL FOLDER IN FILES SECTION.
|
|
216
|
-
1) Create a folder called `assignmentFiles` which should
|
|
217
|
+
1) Create a folder called `assignmentFiles` which should contain all the necessary files including
|
|
217
218
|
the specification file.
|
|
218
219
|
|
|
219
220
|
### EXAMPLE OF THE SPECIFICATION FILE.
|
|
@@ -239,3 +240,4 @@ URL_OF_HW "file_name"
|
|
|
239
240
|
|
|
240
241
|
C cc -o bigbag --std=gnu11 bigbag.c
|
|
241
242
|
|
|
243
|
+
|
|
@@ -167,7 +167,7 @@ DISCSN_URL
|
|
|
167
167
|
|
|
168
168
|
EXMPLS <no_of_test_cases>
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
FILE[file_name]
|
|
171
171
|
|
|
172
172
|
### MODIFICATIONS REQUIRED IN THE SPECIFICATION FILE.
|
|
173
173
|
1) Start the specification file with the tag CRT_HW START followed by a space followed by the name of assignment.
|
|
@@ -190,12 +190,13 @@ URL_OF_HW "file_name"
|
|
|
190
190
|
1) In order to add hyperlink to a file the markdown format is as follows:
|
|
191
191
|
[file_name_to_be_displayed](Url_of_the_file)
|
|
192
192
|
Here in the parenthesis where the Url is required,insert the tag
|
|
193
|
-
|
|
194
|
-
For ex:
|
|
195
|
-
|
|
193
|
+
FILE[name of file].
|
|
194
|
+
For ex: FILE[file_name.extension]
|
|
195
|
+
If the file is not already in the Codeval folder, it will be extracted from a zip file in the
|
|
196
|
+
CodEval spec and uploaded automatically.
|
|
196
197
|
|
|
197
198
|
### UPLOAD THE REQUIRED FILES IN CODEVAL FOLDER IN FILES SECTION.
|
|
198
|
-
1) Create a folder called `assignmentFiles` which should
|
|
199
|
+
1) Create a folder called `assignmentFiles` which should contain all the necessary files including
|
|
199
200
|
the specification file.
|
|
200
201
|
|
|
201
202
|
### EXAMPLE OF THE SPECIFICATION FILE.
|
|
@@ -221,3 +222,4 @@ URL_OF_HW "file_name"
|
|
|
221
222
|
|
|
222
223
|
C cc -o bigbag --std=gnu11 bigbag.c
|
|
223
224
|
|
|
225
|
+
|
{assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/canvas_utils.py
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import sys
|
|
3
|
+
from configparser import ConfigParser
|
|
3
4
|
from functools import cache
|
|
4
5
|
from typing import NamedTuple
|
|
5
|
-
from configparser import ConfigParser
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
8
|
from canvasapi import Canvas
|
|
@@ -56,9 +56,20 @@ def get_course(canvas, name, is_active=True):
|
|
|
56
56
|
return course_list[0]
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def is_teacher(course):
|
|
60
|
+
if hasattr(course, "enrollments"):
|
|
61
|
+
for e in course.enrollments:
|
|
62
|
+
if 'role' not in e:
|
|
63
|
+
continue
|
|
64
|
+
type = e['role']
|
|
65
|
+
if type == 'TeacherEnrollment' or type == 'TaEnrollment':
|
|
66
|
+
return True
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
59
70
|
def get_courses(canvas, name: str, is_active=True, is_finished=False):
|
|
60
71
|
''' find the courses based on partial match '''
|
|
61
|
-
courses = canvas.get_courses(
|
|
72
|
+
courses = [c for c in canvas.get_courses() if is_teacher(c)]
|
|
62
73
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
63
74
|
course_list = []
|
|
64
75
|
for c in courses:
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
|
+
|
|
3
|
+
from assignment_codeval.create_assignment import create_assignment
|
|
2
4
|
from assignment_codeval.evaluate import run_evaluation
|
|
3
5
|
from assignment_codeval.github_connect import github_setup_repo
|
|
4
6
|
from assignment_codeval.submissions import download_submissions, upload_submission_comments, evaluate_submissions
|
|
@@ -13,6 +15,7 @@ cli.add_command(download_submissions)
|
|
|
13
15
|
cli.add_command(upload_submission_comments)
|
|
14
16
|
cli.add_command(github_setup_repo)
|
|
15
17
|
cli.add_command(evaluate_submissions)
|
|
18
|
+
cli.add_command(create_assignment)
|
|
16
19
|
|
|
17
20
|
if __name__ == "__main__":
|
|
18
21
|
cli()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import markdown
|
|
4
|
+
|
|
5
|
+
from assignment_codeval.commons import info, get_config
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def sampleTestCases(listOfTC, numOfTC):
|
|
9
|
+
counter = 0
|
|
10
|
+
samples = "<pre><code>"
|
|
11
|
+
for line in listOfTC:
|
|
12
|
+
if line.startswith('T ', 0, 2):
|
|
13
|
+
counter = counter + 1
|
|
14
|
+
if counter > numOfTC:
|
|
15
|
+
break
|
|
16
|
+
samples = samples + "\n" + "Command to RUN: " + line[2:]
|
|
17
|
+
elif line.startswith('I '):
|
|
18
|
+
samples = samples + "<span style=\"color:green\">" + line[2:] + "</span>"
|
|
19
|
+
elif line.startswith('O '):
|
|
20
|
+
samples = samples + "<span style=\"color:blue\">" + line[2:] + "</span>"
|
|
21
|
+
elif line.startswith('X '):
|
|
22
|
+
samples = samples + "Expected Exit Code: " + line[2:]
|
|
23
|
+
elif line.startswith('E '):
|
|
24
|
+
samples = samples + "<span style=\"color:Tomato\">" + line[2:] + "</span>"
|
|
25
|
+
else:
|
|
26
|
+
continue
|
|
27
|
+
samples = samples + "</code></pre>"
|
|
28
|
+
return samples
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def mdToHtml(file_name, files_resolver=None):
|
|
32
|
+
with open(file_name, 'r') as f:
|
|
33
|
+
text = ""
|
|
34
|
+
examples = []
|
|
35
|
+
assignment = ""
|
|
36
|
+
numOfSampleTC = 1
|
|
37
|
+
for line in f:
|
|
38
|
+
if 'CRT_HW START' in line:
|
|
39
|
+
assignment_name = line[13:].strip()
|
|
40
|
+
elif 'CRT_HW END' in line:
|
|
41
|
+
assignment = text
|
|
42
|
+
elif line.startswith(('T ', 'I ', 'O ', 'X ', 'E ')):
|
|
43
|
+
examples.append(line)
|
|
44
|
+
elif line.startswith('HT '):
|
|
45
|
+
break
|
|
46
|
+
else:
|
|
47
|
+
if 'EXMPLS ' in line:
|
|
48
|
+
numOfSampleTC = int(line[7:])
|
|
49
|
+
text = text + line
|
|
50
|
+
samples = sampleTestCases(examples, numOfSampleTC)
|
|
51
|
+
assignment = re.sub('EXMPLS [0-9]+', samples, assignment)
|
|
52
|
+
assignment = re.sub(r'FILE\[([^]]+)]',
|
|
53
|
+
lambda m: files_resolver(m.group(1)) if files_resolver else f'FILE[{m.group(1)}]',
|
|
54
|
+
assignment)
|
|
55
|
+
html = markdown.markdown(assignment, extensions=['markdown.extensions.tables'])
|
|
56
|
+
|
|
57
|
+
if get_config().dry_run:
|
|
58
|
+
html_file_name = file_name + '.html'
|
|
59
|
+
with open(html_file_name, 'w') as f:
|
|
60
|
+
f.write(html)
|
|
61
|
+
info(f'HTML preview created in the path : {html_file_name}')
|
|
62
|
+
return assignment_name, html
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
from tempfile import TemporaryDirectory
|
|
4
|
+
from zipfile import ZipFile
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from assignment_codeval import convertMD2Html
|
|
9
|
+
from assignment_codeval.canvas_utils import connect_to_canvas, get_course
|
|
10
|
+
from assignment_codeval.commons import debug, errorWithException, info, get_config, set_config, error, warn
|
|
11
|
+
|
|
12
|
+
file_dict = {}
|
|
13
|
+
zip_files = []
|
|
14
|
+
canvas_folder = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_codeval_folder(course):
|
|
18
|
+
canvas_folders = course.get_folders()
|
|
19
|
+
for folder in canvas_folders:
|
|
20
|
+
if folder.name == "CodEval":
|
|
21
|
+
return folder
|
|
22
|
+
error("could not file CodEval folder")
|
|
23
|
+
exit(2)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def upload_assignment_files(folder, files):
|
|
27
|
+
global file_dict
|
|
28
|
+
for file in files:
|
|
29
|
+
if get_config().dry_run:
|
|
30
|
+
info(f'would upload the {file}')
|
|
31
|
+
file_dict[file] = f"http://bogus/{file}"
|
|
32
|
+
else:
|
|
33
|
+
try:
|
|
34
|
+
file_spec = folder.upload(file)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
traceback.print_exc()
|
|
37
|
+
errorWithException(f'Error uploading the file {file} in CodEval folder due to error : {e}. Exiting!!')
|
|
38
|
+
else:
|
|
39
|
+
file_dict[file_spec[1]['filename']] = file_spec[1]['url']
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def files_resolver(filename):
|
|
43
|
+
global file_dict
|
|
44
|
+
global canvas_folder
|
|
45
|
+
global zip_files
|
|
46
|
+
|
|
47
|
+
if filename in file_dict:
|
|
48
|
+
return file_dict[filename]
|
|
49
|
+
for z in zip_files:
|
|
50
|
+
with ZipFile(z) as zf:
|
|
51
|
+
matches = [f for f in zf.namelist() if os.path.basename(f) == filename]
|
|
52
|
+
if not matches:
|
|
53
|
+
warn(f"could not find {filename} in {z}")
|
|
54
|
+
elif len(matches) > 1:
|
|
55
|
+
warn("found multiple matches for {filename} in {z}: {matches}")
|
|
56
|
+
if matches:
|
|
57
|
+
zfname = matches[0]
|
|
58
|
+
with TemporaryDirectory("ce") as tmpdir:
|
|
59
|
+
extracted_path = zf.extract(zfname, path=tmpdir)
|
|
60
|
+
upload_assignment_files(canvas_folder, [extracted_path])
|
|
61
|
+
return file_dict[filename]
|
|
62
|
+
else:
|
|
63
|
+
return f"FILE[{filename}]"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@click.command()
|
|
67
|
+
@click.argument("course_name")
|
|
68
|
+
@click.argument("specname")
|
|
69
|
+
@click.argument("extra", nargs=-1)
|
|
70
|
+
@click.option("--dryrun/--no-dryrun", default=True, show_default=True,
|
|
71
|
+
help="Create assignment but don't update Canvas.")
|
|
72
|
+
@click.option("--verbose/--no-verbose", default=False, show_default=True, help="Verbose actions")
|
|
73
|
+
@click.option("--group_name", default="Assignments", show_default=True,
|
|
74
|
+
help="Group name in which assignments needs to be created.")
|
|
75
|
+
def create_assignment(dryrun, verbose, course_name, group_name, specname, extra):
|
|
76
|
+
"""
|
|
77
|
+
Create the assignment in the given course.
|
|
78
|
+
"""
|
|
79
|
+
global canvas_folder
|
|
80
|
+
global zip_files
|
|
81
|
+
set_config(verbose, dryrun, False, False)
|
|
82
|
+
if not os.path.isfile(specname):
|
|
83
|
+
errorWithException(f'The specification file:{specname} does not exist in the CodEval folder. Exiting!!')
|
|
84
|
+
|
|
85
|
+
(canvas, user) = connect_to_canvas()
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
course = get_course(canvas, course_name)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
errorWithException(f'get_course api failed with following error : {e}')
|
|
91
|
+
else:
|
|
92
|
+
debug(f'Successfully retrieved the course: {course_name}')
|
|
93
|
+
canvas_folder = get_codeval_folder(course)
|
|
94
|
+
if extra:
|
|
95
|
+
upload_assignment_files(canvas_folder, extra)
|
|
96
|
+
# find zipfiles in spec
|
|
97
|
+
with open(specname) as f:
|
|
98
|
+
for line in f:
|
|
99
|
+
if line.startswith("Z "):
|
|
100
|
+
zipfile = line[2:].strip()
|
|
101
|
+
if zipfile not in zip_files:
|
|
102
|
+
zip_files.append(zipfile)
|
|
103
|
+
|
|
104
|
+
debug(f'Successfully uploaded the files in the CodEval folder')
|
|
105
|
+
try:
|
|
106
|
+
(assign_name, html) = convertMD2Html.mdToHtml(specname, files_resolver)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
traceback.print_exc()
|
|
109
|
+
errorWithException(f'Error in convertMD2Html::mdToHtml function')
|
|
110
|
+
else:
|
|
111
|
+
debug(f'Successfully converted the assignment description to HTML')
|
|
112
|
+
|
|
113
|
+
grp_name = None
|
|
114
|
+
for assign_group in course.get_assignment_groups():
|
|
115
|
+
if assign_group.name == group_name:
|
|
116
|
+
grp_name = assign_group
|
|
117
|
+
debug(f'The group id is: {grp_name.id}')
|
|
118
|
+
if grp_name is None:
|
|
119
|
+
errorWithException(f'The group name : {group_name} does not exist. Exiting!')
|
|
120
|
+
|
|
121
|
+
canvas_assignments = course.get_assignments()
|
|
122
|
+
debug(f'Successfully got all the assignments from the desired course')
|
|
123
|
+
canvas_assign_names = [assign.name for assign in canvas_assignments]
|
|
124
|
+
if assign_name in canvas_assign_names:
|
|
125
|
+
for assignment in canvas_assignments:
|
|
126
|
+
if assignment.name == assign_name:
|
|
127
|
+
if dryrun:
|
|
128
|
+
info(f"would update {assign_name}.")
|
|
129
|
+
else:
|
|
130
|
+
for discussion in course.get_discussion_topics():
|
|
131
|
+
if discussion.title == assign_name:
|
|
132
|
+
disUrlHtml = f'<a href={discussion.html_url}>{discussion.title}</a>'
|
|
133
|
+
try:
|
|
134
|
+
assignment.edit(assignment={'name': assign_name, 'assignment_group_id': grp_name.id,
|
|
135
|
+
'description': html.replace("DISCUSSION_LINK", disUrlHtml),
|
|
136
|
+
'points_possible': 100, 'allowed_extensions': ["zip"], })
|
|
137
|
+
except Exception as e:
|
|
138
|
+
traceback.print_exc()
|
|
139
|
+
errorWithException(f'Editing assignment {assign_name} failed with the exception : {e}')
|
|
140
|
+
else:
|
|
141
|
+
info(f'Successfully edited assignment {assign_name}')
|
|
142
|
+
|
|
143
|
+
else:
|
|
144
|
+
if dryrun:
|
|
145
|
+
info(f"would create {assign_name}")
|
|
146
|
+
else:
|
|
147
|
+
try:
|
|
148
|
+
# Create the discussion Topic
|
|
149
|
+
dis_topic = course.create_discussion_topic(title=assign_name, message="")
|
|
150
|
+
debug(f'Created Discussion Topic: {assign_name}')
|
|
151
|
+
# get the url of the discussion topic
|
|
152
|
+
disUrlHtml = f'<a href={dis_topic.html_url}>{dis_topic.title}</a>'
|
|
153
|
+
# Create the assignment with the assign_name
|
|
154
|
+
created_assignment = course.create_assignment({'name': assign_name, 'assignment_group_id': grp_name.id,
|
|
155
|
+
'description': html.replace("DISCUSSION_LINK",
|
|
156
|
+
disUrlHtml),
|
|
157
|
+
'points_possible': 100, 'published': False,
|
|
158
|
+
'submission_types': ["online_upload"],
|
|
159
|
+
'allowed_extensions': ["zip"], })
|
|
160
|
+
debug(f'Created new assignment: {assign_name}')
|
|
161
|
+
# Update the discussion topic with the assignment link
|
|
162
|
+
dis_topic.update(
|
|
163
|
+
message=f'This Discussion is for Assignment <a href={created_assignment.html_url}>{assign_name}</a>', )
|
|
164
|
+
debug(f'Updated the Discussion Topic by linking it with the corresponding assignment: {assign_name}')
|
|
165
|
+
except Exception as e:
|
|
166
|
+
traceback.print_exc()
|
|
167
|
+
errorWithException(f'Creating Discussion topic and assignment failed due to the exception: {e}')
|
|
168
|
+
else:
|
|
169
|
+
info(f'Successfully created assignment and Discussion Topic {assign_name}')
|
|
@@ -171,11 +171,12 @@ def run_command_noerror(command):
|
|
|
171
171
|
|
|
172
172
|
if command_popen.returncode:
|
|
173
173
|
print("FAILED")
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
if os.path.exists("evaluationLogs"):
|
|
175
|
+
for file in os.listdir("evaluationLogs"):
|
|
176
|
+
with open(file, "r") as infile:
|
|
177
|
+
file_lines = infile.readlines()
|
|
178
|
+
# Print entire file
|
|
179
|
+
print("\n".join(file_lines))
|
|
179
180
|
|
|
180
181
|
# Exit entire program with error
|
|
181
182
|
sys.exit(1)
|
|
@@ -209,8 +210,8 @@ def test_case(test_case_command):
|
|
|
209
210
|
check_test()
|
|
210
211
|
|
|
211
212
|
# Clear hint
|
|
212
|
-
global
|
|
213
|
-
|
|
213
|
+
global test_case_hint
|
|
214
|
+
test_case_hint= ""
|
|
214
215
|
|
|
215
216
|
# Set new test case command
|
|
216
217
|
global test_args
|
|
@@ -237,8 +238,8 @@ def test_case_hidden(test_case_command):
|
|
|
237
238
|
check_test()
|
|
238
239
|
|
|
239
240
|
# Clear hint
|
|
240
|
-
global
|
|
241
|
-
|
|
241
|
+
global test_case_hint
|
|
242
|
+
test_case_hint = ""
|
|
242
243
|
|
|
243
244
|
# Set new test case command
|
|
244
245
|
global test_args
|
|
@@ -361,7 +362,7 @@ def exit_code(test_case_exit_code):
|
|
|
361
362
|
None
|
|
362
363
|
"""
|
|
363
364
|
global expected_exit_code
|
|
364
|
-
expected_exit_code =
|
|
365
|
+
expected_exit_code = int(test_case_exit_code)
|
|
365
366
|
|
|
366
367
|
|
|
367
368
|
def start_server(timeout_sec, kill_timeout_sec, *server_cmd):
|
|
@@ -593,20 +594,21 @@ def check_test():
|
|
|
593
594
|
# Hidden test case handling
|
|
594
595
|
if test_case_hidden:
|
|
595
596
|
print(" Test Case is Hidden")
|
|
596
|
-
if
|
|
597
|
-
print(f"HINT: {
|
|
597
|
+
if test_case_hint:
|
|
598
|
+
print(f"HINT: {test_case_hint}")
|
|
598
599
|
else:
|
|
599
|
-
if
|
|
600
|
-
print(f"HINT: {
|
|
600
|
+
if test_case_hint:
|
|
601
|
+
print(f"HINT: {test_case_hint}")
|
|
601
602
|
|
|
602
603
|
# Cleanup
|
|
603
604
|
print(f" Command ran: {test_args}")
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
605
|
+
if os.path.exists("evaluationLogs"):
|
|
606
|
+
for file in os.listdir("evaluationLogs"):
|
|
607
|
+
with open("evaluationLogs/" + file, "r") as infile:
|
|
608
|
+
file_lines = infile.readlines()
|
|
607
609
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
+
# Print entire file
|
|
611
|
+
print("".join(file_lines))
|
|
610
612
|
|
|
611
613
|
cleanup()
|
|
612
614
|
|
{assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/github_connect.py
RENAMED
|
@@ -42,7 +42,7 @@ def github_setup_repo(course_name, assignment_name, target_dir, github_field, al
|
|
|
42
42
|
|
|
43
43
|
users = course.get_users(include=["enrollments"])
|
|
44
44
|
for user in users:
|
|
45
|
-
ssid = user.
|
|
45
|
+
ssid = str(user.id)
|
|
46
46
|
ssid_dir = os.path.join(submission_dir, ssid)
|
|
47
47
|
click.echo(f"Checking {ssid_dir}")
|
|
48
48
|
if not all_repos and not os.path.exists(ssid_dir):
|
|
@@ -5,7 +5,7 @@ import subprocess
|
|
|
5
5
|
from configparser import ConfigParser
|
|
6
6
|
from datetime import datetime, timezone
|
|
7
7
|
from functools import cache
|
|
8
|
-
from tempfile import
|
|
8
|
+
from tempfile import TemporaryDirectory
|
|
9
9
|
from zipfile import ZipFile
|
|
10
10
|
|
|
11
11
|
import click
|
|
@@ -58,7 +58,8 @@ def upload_submission_comments(submissions_dir, codeval_prefix):
|
|
|
58
58
|
|
|
59
59
|
@click.command()
|
|
60
60
|
@click.argument('codeval_dir', metavar="CODEVAL_DIR")
|
|
61
|
-
@click.option("--submissions-dir", help="directory containing submissions COURSE/ASSIGNMENT/STUDENT_ID",
|
|
61
|
+
@click.option("--submissions-dir", help="directory containing submissions COURSE/ASSIGNMENT/STUDENT_ID",
|
|
62
|
+
default='./submissions', show_default=True)
|
|
62
63
|
def evaluate_submissions(codeval_dir, submissions_dir):
|
|
63
64
|
"""
|
|
64
65
|
Evaluate submissions stored in the form COURSE/ASSIGNMENT/STUDENT_ID.
|
|
@@ -101,9 +102,10 @@ def evaluate_submissions(codeval_dir, submissions_dir):
|
|
|
101
102
|
except Exception:
|
|
102
103
|
warn(f"could not parse compile timeout from {line}, using default {compile_timeout}")
|
|
103
104
|
if line.startswith("CD"):
|
|
104
|
-
assignment_working_dir = os.path.normpath(
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
assignment_working_dir = os.path.normpath(
|
|
106
|
+
os.path.join(assignment_working_dir, line.split()[1].strip()))
|
|
107
|
+
if not os.path.isdir(os.path.join(repo_dir, assignment_working_dir)):
|
|
108
|
+
out = f"{assignment_working_dir} does not exist or is not a directory\n".encode('utf-8')
|
|
107
109
|
move_to_next_submission = True
|
|
108
110
|
break
|
|
109
111
|
if line.startswith("Z"):
|
|
@@ -114,7 +116,7 @@ def evaluate_submissions(codeval_dir, submissions_dir):
|
|
|
114
116
|
if not move_to_next_submission:
|
|
115
117
|
command = raw_command.replace("EVALUATE", "cd /submissions; assignment-codeval run-evaluation codeval.txt")
|
|
116
118
|
|
|
117
|
-
with TemporaryDirectory("cedir", dir="/var/tmp"
|
|
119
|
+
with TemporaryDirectory("cedir", dir="/var/tmp") as link_dir:
|
|
118
120
|
repo_link = os.path.join(link_dir, "submissions")
|
|
119
121
|
os.symlink(repo_dir, repo_link)
|
|
120
122
|
full_assignment_working_dir = os.path.join(repo_link, assignment_working_dir)
|
|
@@ -147,11 +149,15 @@ def evaluate_submissions(codeval_dir, submissions_dir):
|
|
|
147
149
|
@click.argument("course_name", metavar="COURSE")
|
|
148
150
|
@click.argument("assignment_name", metavar="ASSIGNMENT")
|
|
149
151
|
@click.option("--target-dir", help="directory to download submissions to", default='./submissions', show_default=True)
|
|
150
|
-
@click.option("--include-commented", is_flag=True,
|
|
151
|
-
|
|
152
|
+
@click.option("--include-commented", is_flag=True,
|
|
153
|
+
help="even download submissionsthat already have codeval comments since last submission")
|
|
154
|
+
@click.option("--uncommented_for",
|
|
155
|
+
help="only download submission where the last comment is more than these minutes ago", default=0,
|
|
156
|
+
show_default=True)
|
|
152
157
|
@click.option("--codeval-prefix", help="prefix for codeval comments", default="codeval: ", show_default=True)
|
|
153
158
|
@click.option("--include-empty", is_flag=True, help="include empty submissions")
|
|
154
|
-
def download_submissions(course_name, assignment_name, target_dir, include_commented, codeval_prefix, include_empty,
|
|
159
|
+
def download_submissions(course_name, assignment_name, target_dir, include_commented, codeval_prefix, include_empty,
|
|
160
|
+
uncommented_for):
|
|
155
161
|
"""
|
|
156
162
|
Download submissions for a given assignment in a course from Canvas.
|
|
157
163
|
|
|
@@ -174,7 +180,7 @@ def download_submissions(course_name, assignment_name, target_dir, include_comme
|
|
|
174
180
|
last_comment_date = submission_comments[-1]
|
|
175
181
|
else:
|
|
176
182
|
last_comment_date = None
|
|
177
|
-
if not include_commented and last_comment_date and submission.submitted_at <= last_comment_date:
|
|
183
|
+
if not include_commented and last_comment_date and submission.submitted_at and submission.submitted_at <= last_comment_date:
|
|
178
184
|
continue
|
|
179
185
|
|
|
180
186
|
if uncommented_for > 0 and last_comment_date:
|
|
@@ -183,13 +189,15 @@ def download_submissions(course_name, assignment_name, target_dir, include_comme
|
|
|
183
189
|
if delta.total_seconds() < uncommented_for * 60:
|
|
184
190
|
continue
|
|
185
191
|
|
|
186
|
-
student_id = submission.
|
|
192
|
+
student_id = str(submission.user_id)
|
|
193
|
+
student_name = submission.user['name']
|
|
187
194
|
student_submission_dir = os.path.join(submission_dir, student_id)
|
|
188
195
|
os.makedirs(student_submission_dir, exist_ok=True)
|
|
189
196
|
|
|
190
197
|
metapath = os.path.join(student_submission_dir, "metadata.txt")
|
|
191
198
|
with open(metapath, "w") as fd:
|
|
192
199
|
print(f"""id={student_id}
|
|
200
|
+
name={student_name}
|
|
193
201
|
course={course.name}
|
|
194
202
|
assignment={assignment.name}
|
|
195
203
|
attempt={submission.attempt}
|
|
@@ -227,6 +235,6 @@ last_comment={last_comment_date}""", file=fd)
|
|
|
227
235
|
def get_submissions_by_id(assignment):
|
|
228
236
|
submissions_by_id = {}
|
|
229
237
|
for submission in assignment.get_submissions(include=["user"]):
|
|
230
|
-
student_id = submission.
|
|
238
|
+
student_id = str(submission.user_id)
|
|
231
239
|
submissions_by_id[student_id] = submission
|
|
232
|
-
return submissions_by_id
|
|
240
|
+
return submissions_by_id
|
{assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: assignment-codeval
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: CodEval for evaluating programming assignments
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -185,7 +185,7 @@ DISCSN_URL
|
|
|
185
185
|
|
|
186
186
|
EXMPLS <no_of_test_cases>
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
FILE[file_name]
|
|
189
189
|
|
|
190
190
|
### MODIFICATIONS REQUIRED IN THE SPECIFICATION FILE.
|
|
191
191
|
1) Start the specification file with the tag CRT_HW START followed by a space followed by the name of assignment.
|
|
@@ -208,12 +208,13 @@ URL_OF_HW "file_name"
|
|
|
208
208
|
1) In order to add hyperlink to a file the markdown format is as follows:
|
|
209
209
|
[file_name_to_be_displayed](Url_of_the_file)
|
|
210
210
|
Here in the parenthesis where the Url is required,insert the tag
|
|
211
|
-
|
|
212
|
-
For ex:
|
|
213
|
-
|
|
211
|
+
FILE[name of file].
|
|
212
|
+
For ex: FILE[file_name.extension]
|
|
213
|
+
If the file is not already in the Codeval folder, it will be extracted from a zip file in the
|
|
214
|
+
CodEval spec and uploaded automatically.
|
|
214
215
|
|
|
215
216
|
### UPLOAD THE REQUIRED FILES IN CODEVAL FOLDER IN FILES SECTION.
|
|
216
|
-
1) Create a folder called `assignmentFiles` which should
|
|
217
|
+
1) Create a folder called `assignmentFiles` which should contain all the necessary files including
|
|
217
218
|
the specification file.
|
|
218
219
|
|
|
219
220
|
### EXAMPLE OF THE SPECIFICATION FILE.
|
|
@@ -239,3 +240,4 @@ URL_OF_HW "file_name"
|
|
|
239
240
|
|
|
240
241
|
C cc -o bigbag --std=gnu11 bigbag.c
|
|
241
242
|
|
|
243
|
+
|
{assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/SOURCES.txt
RENAMED
|
@@ -4,6 +4,8 @@ src/assignment_codeval/__init__.py
|
|
|
4
4
|
src/assignment_codeval/canvas_utils.py
|
|
5
5
|
src/assignment_codeval/cli.py
|
|
6
6
|
src/assignment_codeval/commons.py
|
|
7
|
+
src/assignment_codeval/convertMD2Html.py
|
|
8
|
+
src/assignment_codeval/create_assignment.py
|
|
7
9
|
src/assignment_codeval/evaluate.py
|
|
8
10
|
src/assignment_codeval/file_utils.py
|
|
9
11
|
src/assignment_codeval/github_connect.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/requires.txt
RENAMED
|
File without changes
|
{assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/top_level.txt
RENAMED
|
File without changes
|