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.
Files changed (20) hide show
  1. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/PKG-INFO +8 -6
  2. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/README.md +7 -5
  3. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/pyproject.toml +1 -1
  4. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/canvas_utils.py +13 -2
  5. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/cli.py +3 -0
  6. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/commons.py +1 -1
  7. assignment_codeval-0.0.3/src/assignment_codeval/convertMD2Html.py +62 -0
  8. assignment_codeval-0.0.3/src/assignment_codeval/create_assignment.py +169 -0
  9. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/evaluate.py +21 -19
  10. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/github_connect.py +1 -1
  11. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/submissions.py +21 -13
  12. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/PKG-INFO +8 -6
  13. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/SOURCES.txt +2 -0
  14. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/setup.cfg +0 -0
  15. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/__init__.py +0 -0
  16. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval/file_utils.py +0 -0
  17. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/dependency_links.txt +0 -0
  18. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/entry_points.txt +0 -0
  19. {assignment_codeval-0.0.2 → assignment_codeval-0.0.3}/src/assignment_codeval.egg-info/requires.txt +0 -0
  20. {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.2
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
- URL_OF_HW "file_name"
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
- URL_OF_HW followed by space followed by the file name of the file required to be linked in double quotes.
212
- For ex: URL_OF_HW "file name.extension"
213
- Note: The file should be present in the Codeval folder.
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 conatin all the necessary files including
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
- URL_OF_HW "file_name"
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
- URL_OF_HW followed by space followed by the file name of the file required to be linked in double quotes.
194
- For ex: URL_OF_HW "file name.extension"
195
- Note: The file should be present in the Codeval folder.
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 conatin all the necessary files including
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
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "assignment-codeval"
7
- version = "0.0.2"
7
+ version = "0.0.3"
8
8
  description = "CodEval for evaluating programming assignments"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -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(enrollment_type="teacher")
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()
@@ -29,7 +29,7 @@ def set_config(show_debug, dry_run, force, copy_tmpdir):
29
29
 
30
30
 
31
31
  def _now():
32
- return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
32
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
33
33
 
34
34
 
35
35
  def debug(message):
@@ -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
- for file in os.listdir("evaluationLogs"):
175
- with open(file, "r") as infile:
176
- file_lines = infile.readlines()
177
- # Print entire file
178
- print("\n".join(file_lines))
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 hint
213
- hint = ""
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 hint
241
- hint = ""
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 = float(test_case_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 hint:
597
- print(f"HINT: {hint}")
597
+ if test_case_hint:
598
+ print(f"HINT: {test_case_hint}")
598
599
  else:
599
- if hint:
600
- print(f"HINT: {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
- for file in os.listdir("evaluationLogs"):
605
- with open("evaluationLogs/" + file, "r") as infile:
606
- file_lines = infile.readlines()
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
- # Print entire file
609
- print("".join(file_lines))
610
+ # Print entire file
611
+ print("".join(file_lines))
610
612
 
611
613
  cleanup()
612
614
 
@@ -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.login_id
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 TemporaryFile, TemporaryDirectory
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", default='./submissions', show_default=True)
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(os.path.join(assignment_working_dir, line.split()[1].strip()))
105
- if not os.path.exists(os.path.join(repo_dir, assignment_working_dir)):
106
- out = f"{assignment_working_dir} does not exist\n".encode('utf-8')
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", delete=False) as link_dir:
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, help="even download submissionsthat already have codeval comments since last submission")
151
- @click.option("--uncommented_for", help="only download submission where the last comment is more than these minutes ago", default=0, show_default=True)
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, uncommented_for):
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.user['login_id']
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.user['login_id']
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assignment-codeval
3
- Version: 0.0.2
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
- URL_OF_HW "file_name"
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
- URL_OF_HW followed by space followed by the file name of the file required to be linked in double quotes.
212
- For ex: URL_OF_HW "file name.extension"
213
- Note: The file should be present in the Codeval folder.
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 conatin all the necessary files including
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
+
@@ -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