dlai-grader 2.0b1__py3-none-any.whl → 2.0b3__py3-none-any.whl

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.

Potentially problematic release.


This version of dlai-grader might be problematic. Click here for more details.

dlai_grader/__init__.py CHANGED
@@ -6,6 +6,6 @@ from . import grading
6
6
  from . import types
7
7
 
8
8
 
9
- __version__ = "2.0b1"
9
+ __version__ = "2.0b3"
10
10
  __author__ = "Andres Zarta"
11
11
  __credits__ = "DeepLearning.AI"
dlai_grader/io.py CHANGED
@@ -5,6 +5,7 @@ import subprocess
5
5
  import tarfile
6
6
  from contextlib import contextmanager, redirect_stderr, redirect_stdout
7
7
  from os import devnull
8
+ from pathlib import Path
8
9
  from textwrap import dedent
9
10
  from zipfile import ZipFile
10
11
 
@@ -245,10 +246,10 @@ def init_grader() -> None:
245
246
  write_file_from_template("./Makefile", template_dict["makefile"])
246
247
  write_file_from_template("./.conf", template_dict["conf"])
247
248
  write_file_from_template("./entry.py", template_dict["entry_py"])
248
- write_file_from_template(
249
- "./copy_assignment_to_submission.sh",
250
- template_dict["copy_assignment_to_submission_sh"],
251
- )
249
+ # write_file_from_template(
250
+ # "./copy_assignment_to_submission.sh",
251
+ # template_dict["copy_assignment_to_submission_sh"],
252
+ # )
252
253
  write_file_from_template("./requirements.txt", "dlai-grader")
253
254
  write_file_from_template("./.env", "")
254
255
 
@@ -313,3 +314,24 @@ def grade_parts(
313
314
  subprocess.run(cmd, shell=True, executable="/bin/bash")
314
315
  except Exception as e:
315
316
  print(f"There was an error grading with coursera_autograder. Details: {e}")
317
+
318
+
319
+ def generate_learner_file(
320
+ filename_source: str,
321
+ filename_target: str,
322
+ ) -> None:
323
+ """
324
+ Generates the learning facing version of any file.
325
+
326
+ Args:
327
+ filename_source (str): Path to original notebook.
328
+ filename_target (str): Path where to save reformatted notebook.
329
+
330
+ """
331
+ solution_code = Path(filename_source).read_text()
332
+
333
+ # format the code to replace with placeholders
334
+ fmt_code = solution_to_learner_format(solution_code)
335
+
336
+ # save the learner files
337
+ Path(filename_target).write_text(fmt_code, encoding="utf-8")
@@ -0,0 +1,54 @@
1
+ .PHONY: sync learner build debug-unsafe debug-safe grade versioning tag undeletable uneditable init upgrade coursera zip
2
+
3
+ include .conf
4
+
5
+ PARTIDS = 123 456
6
+ OS := $(shell uname)
7
+
8
+ sync:
9
+ cp mount/submission.ipynb ../$(ASSIGNMENT_NAME)_Solution.ipynb
10
+ cp learner/$(ASSIGNMENT_NAME).ipynb ../$(ASSIGNMENT_NAME).ipynb
11
+ cp mount/$(UNIT_TESTS_NAME).py ../$(UNIT_TESTS_NAME).py
12
+
13
+ learner:
14
+ dlai_grader --learner --output_notebook=./learner/$(ASSIGNMENT_NAME).ipynb
15
+ rsync -a --exclude="submission.ipynb" --exclude="__pycache__" --exclude=".mypy_cache" ./mount/ ./learner/
16
+
17
+ build:
18
+ docker build -t $(IMAGE_NAME):$(TAG_ID) .
19
+
20
+ debug-unsafe:
21
+ docker run -it --rm --mount type=bind,source=$(PWD)/mount,target=/shared/submission --mount type=bind,source=$(PWD),target=/grader/ --env-file $(PWD)/.env --entrypoint ash $(IMAGE_NAME):$(TAG_ID)
22
+
23
+ debug-safe:
24
+ docker run -it --rm --mount type=bind,source=$(PWD)/mount,target=/shared/submission --env-file $(PWD)/.env --entrypoint ash $(IMAGE_NAME):$(TAG_ID)
25
+
26
+ grade:
27
+ docker run -it --rm --memory=$(HARD_MEMORY) --cpus="$(CPUS)" --memory-reservation=$(SOFT_MEMORY) --mount type=bind,source=$(PWD)/mount,target=/shared/submission --env-file $(PWD)/.env --entrypoint ash $(IMAGE_NAME):$(TAG_ID) -c 'for partId in $(PARTIDS); do export partId=$$partId; echo "Processing part $$partId"; python entry.py; done'
28
+
29
+ versioning:
30
+ dlai_grader --versioning
31
+
32
+ tag:
33
+ dlai_grader --tag
34
+
35
+ undeletable:
36
+ dlai_grader --undeletable
37
+
38
+ uneditable:
39
+ dlai_grader --uneditable
40
+
41
+ init:
42
+ dlai_grader --versioning
43
+ dlai_grader --tag
44
+ dlai_grader --undeletable
45
+ dlai_grader --uneditable
46
+
47
+ upgrade:
48
+ dlai_grader --upgrade
49
+
50
+ coursera:
51
+ dlai_grader --grade --partids="$(PARTIDS)" --docker=$(IMAGE_NAME):$(TAG_ID) --memory=$(MEMORY_LIMIT) --submission=$(SUB_DIR)
52
+
53
+ zip:
54
+ zip -r $(IMAGE_NAME)$(TAG_ID).zip .
@@ -0,0 +1,12 @@
1
+ #!/bin/bash",
2
+ set -euo pipefail
3
+
4
+ Assignment={{ASSIGNMENT_NAME}}
5
+
6
+ SubmissionFile=submission.ipynb
7
+ SubmissionPath=/shared/submission
8
+ SharedDiskPath=/learner_workplace/$UserId/$CourseId/$LessonId
9
+
10
+ # copy synced files (exam image typically sync all files in lesson folder)
11
+ echo "Copy learner submission from $SharedDiskPath/$Assignment to $SubmissionPath/$SubmissionFile"
12
+ cp $SharedDiskPath/$Assignment $SubmissionPath/$SubmissionFile
@@ -0,0 +1,16 @@
1
+ #!/bin/bash",
2
+ set -euo pipefail
3
+
4
+ Assignment={{ASSIGNMENT_NAME}}
5
+ Extra_file={{EXTRA_FILE_NAME}}
6
+
7
+ SubmissionFile=submission.ipynb
8
+ SubmissionPath=/shared/submission
9
+ SharedDiskPath=/learner_workplace/$UserId/$CourseId/$LessonId
10
+
11
+ # copy synced files (exam image typically sync all files in lesson folder)
12
+ echo "Copy learner submission from $SharedDiskPath/$Assignment to $SubmissionPath/$SubmissionFile"
13
+ cp $SharedDiskPath/$Assignment $SubmissionPath/$SubmissionFile
14
+
15
+ echo "Copy learner submission from $SharedDiskPath/$Extra_file to $SubmissionPath/$Extra_file"
16
+ cp $SharedDiskPath/$Extra_file $SubmissionPath/$Extra_file
@@ -0,0 +1,20 @@
1
+ FROM continuumio/miniconda3@sha256:d601a04ea48fd45e60808c7072243d33703d29434d2067816b7f26b0705d889a
2
+
3
+ RUN apk update && apk add libstdc++
4
+
5
+ COPY requirements.txt .
6
+
7
+ RUN pip install -r requirements.txt && rm requirements.txt
8
+
9
+ RUN mkdir /grader && \
10
+ mkdir /grader/submission
11
+
12
+ COPY .conf /grader/.conf
13
+ COPY entry.py /grader/entry.py
14
+ COPY grader.py /grader/grader.py
15
+
16
+ RUN chmod a+rwx /grader/
17
+
18
+ WORKDIR /grader/
19
+
20
+ ENTRYPOINT ["python", "entry.py"]
@@ -0,0 +1,21 @@
1
+ FROM continuumio/miniconda3@sha256:d601a04ea48fd45e60808c7072243d33703d29434d2067816b7f26b0705d889a
2
+
3
+ RUN apk update && apk add libstdc++
4
+
5
+ COPY requirements.txt .
6
+
7
+ RUN pip install -r requirements.txt && rm requirements.txt
8
+
9
+ RUN mkdir /grader && \
10
+ mkdir /grader/submission
11
+
12
+ COPY .conf /grader/.conf
13
+ COPY solution/ /grader/solution/
14
+ COPY entry.py /grader/entry.py
15
+ COPY grader.py /grader/grader.py
16
+
17
+ RUN chmod a+rwx /grader/
18
+
19
+ WORKDIR /grader/
20
+
21
+ ENTRYPOINT ["python", "entry.py"]
@@ -0,0 +1,21 @@
1
+ FROM continuumio/miniconda3@sha256:d601a04ea48fd45e60808c7072243d33703d29434d2067816b7f26b0705d889a
2
+
3
+ RUN apk update && apk add libstdc++
4
+
5
+ COPY requirements.txt .
6
+
7
+ RUN pip install -r requirements.txt && rm requirements.txt
8
+
9
+ RUN mkdir /grader && \
10
+ mkdir /grader/submission
11
+
12
+ COPY .conf /grader/.conf
13
+ COPY data/ /grader/data/
14
+ COPY entry.py /grader/entry.py
15
+ COPY grader.py /grader/grader.py
16
+
17
+ RUN chmod a+rwx /grader/
18
+
19
+ WORKDIR /grader/
20
+
21
+ ENTRYPOINT ["python", "entry.py"]
@@ -0,0 +1,22 @@
1
+ FROM continuumio/miniconda3@sha256:d601a04ea48fd45e60808c7072243d33703d29434d2067816b7f26b0705d889a
2
+
3
+ RUN apk update && apk add libstdc++
4
+
5
+ COPY requirements.txt .
6
+
7
+ RUN pip install -r requirements.txt && rm requirements.txt
8
+
9
+ RUN mkdir /grader && \
10
+ mkdir /grader/submission
11
+
12
+ COPY .conf /grader/.conf
13
+ COPY data/ /grader/data/
14
+ COPY solution/ /grader/solution/
15
+ COPY entry.py /grader/entry.py
16
+ COPY grader.py /grader/grader.py
17
+
18
+ RUN chmod a+rwx /grader/
19
+
20
+ WORKDIR /grader/
21
+
22
+ ENTRYPOINT ["python", "entry.py"]
@@ -0,0 +1,71 @@
1
+ import traceback
2
+ from dlai_grader.config import Config, get_part_id
3
+ from dlai_grader.compiler import compile_partial_module
4
+ from dlai_grader.io import read_notebook, copy_submission_to_workdir, send_feedback
5
+ from dlai_grader.notebook import keep_tagged_cells
6
+ from dlai_grader.grading import compute_grading_score, graded_obj_missing
7
+ from grader import handle_part_id
8
+
9
+
10
+ def notebook_grading(config, compile_solution=False):
11
+ try:
12
+ nb = read_notebook(config.submission_file_path)
13
+ except Exception as e:
14
+ msg = f"There was a problem reading your notebook. Details:\n{e!s}"
15
+ send_feedback(0.0, msg, err=True)
16
+
17
+ transformations = [keep_tagged_cells()]
18
+ for t in transformations:
19
+ nb = t(nb)
20
+
21
+ try:
22
+ learner_mod = compile_partial_module(nb, "learner_mod", verbose=False)
23
+ except Exception as e:
24
+ msg = f"There was a problem compiling the code from your notebook, please check that you saved before submitting. Details:\n{e!s}"
25
+ send_feedback(0.0, msg, err=True)
26
+
27
+ solution_mod = None
28
+ if compile_solution:
29
+ solution_nb = read_notebook(config.solution_file_path)
30
+ for t in transformations:
31
+ solution_nb = t(solution_nb)
32
+ solution_mod = compile_partial_module(
33
+ solution_nb,
34
+ "solution_mod",
35
+ verbose=False,
36
+ )
37
+
38
+ return learner_mod, solution_mod
39
+
40
+
41
+ def main() -> None:
42
+ copy_submission_to_workdir()
43
+
44
+ part_id = get_part_id()
45
+
46
+ c = Config()
47
+
48
+ learner_mod, _ = notebook_grading(c)
49
+
50
+ g_func = handle_part_id(part_id)(learner_mod)
51
+
52
+ try:
53
+ cases = g_func()
54
+ except Exception as e:
55
+ msg = f"There was an error grading your submission. Details:\n{e!s}"
56
+ send_feedback(0.0, msg, err=True)
57
+
58
+ if graded_obj_missing(cases):
59
+ msg = "Object required for grading not found. If you haven't completed the exercise this might be expected. Otherwise, check your solution as grader omits cells that throw errors."
60
+ send_feedback(0.0, msg, err=True)
61
+
62
+ score, feedback = compute_grading_score(cases)
63
+ send_feedback(score, feedback)
64
+
65
+
66
+ if __name__ == "__main__":
67
+ try:
68
+ main()
69
+ except Exception as e:
70
+ msg = f"There was an error with the program. Exception:\n{e!s}.\nTraceback:\n{traceback.format_exc()}"
71
+ send_feedback(0.0, msg, err=True)
@@ -0,0 +1,90 @@
1
+ import traceback
2
+ from dlai_grader.config import Config, get_part_id
3
+ from dlai_grader.compiler import compile_partial_module
4
+ from dlai_grader.io import read_notebook, copy_submission_to_workdir, send_feedback
5
+ from dlai_grader.notebook import keep_tagged_cells
6
+ from dlai_grader.grading import (
7
+ compute_grading_score,
8
+ graded_obj_missing,
9
+ LearnerSubmission,
10
+ )
11
+ from grader import handle_part_id
12
+
13
+
14
+ def notebook_grading(config, compile_solution=False):
15
+ try:
16
+ nb = read_notebook(config.submission_file_path)
17
+ except Exception as e:
18
+ msg = f"There was a problem reading your notebook. Details:\n{e!s}"
19
+ send_feedback(0.0, msg, err=True)
20
+
21
+ transformations = [keep_tagged_cells()]
22
+ for t in transformations:
23
+ nb = t(nb)
24
+
25
+ try:
26
+ learner_mod = compile_partial_module(nb, "learner_mod", verbose=False)
27
+ except Exception as e:
28
+ msg = f"There was a problem compiling the code from your notebook, please check that you saved before submitting. Details:\n{e!s}"
29
+ send_feedback(0.0, msg, err=True)
30
+
31
+ solution_mod = None
32
+ if compile_solution:
33
+ solution_nb = read_notebook(config.solution_file_path)
34
+ for t in transformations:
35
+ solution_nb = t(solution_nb)
36
+ solution_mod = compile_partial_module(
37
+ solution_nb,
38
+ "solution_mod",
39
+ verbose=False,
40
+ )
41
+
42
+ return learner_mod, solution_mod
43
+
44
+
45
+ def non_notebook_grading(config):
46
+ try:
47
+ with open(config.submission_file_path, "r") as file:
48
+ contents = file.read()
49
+ except Exception as e:
50
+ msg = f"There was an error reading your submission. Details:\n{e!s}"
51
+ send_feedback(0.0, msg, err=True)
52
+
53
+ return LearnerSubmission(submission=contents)
54
+
55
+
56
+ def main() -> None:
57
+ part_id = get_part_id()
58
+
59
+ match part_id:
60
+ case "123":
61
+ copy_submission_to_workdir(file_name="{{EXTRA_FILE_NAME}}")
62
+ c = Config(submission_file="{{EXTRA_FILE_NAME}}")
63
+ learner_mod = non_notebook_grading(c)
64
+ case _:
65
+ copy_submission_to_workdir()
66
+ c = Config()
67
+ learner_mod, _ = notebook_grading(c)
68
+
69
+ g_func = handle_part_id(part_id)(learner_mod)
70
+
71
+ try:
72
+ cases = g_func()
73
+ except Exception as e:
74
+ msg = f"There was an error grading your submission. Details:\n{e!s}"
75
+ send_feedback(0.0, msg, err=True)
76
+
77
+ if graded_obj_missing(cases):
78
+ msg = "Object required for grading not found. If you haven't completed the exercise this might be expected. Otherwise, check your solution as grader omits cells that throw errors."
79
+ send_feedback(0.0, msg, err=True)
80
+
81
+ score, feedback = compute_grading_score(cases)
82
+ send_feedback(score, feedback)
83
+
84
+
85
+ if __name__ == "__main__":
86
+ try:
87
+ main()
88
+ except Exception as e:
89
+ msg = f"There was an error with the program. Exception:\n{e!s}.\nTraceback:\n{traceback.format_exc()}"
90
+ send_feedback(0.0, msg, err=True)
@@ -0,0 +1,71 @@
1
+ import traceback
2
+ from dlai_grader.config import Config, get_part_id
3
+ from dlai_grader.compiler import compile_partial_module
4
+ from dlai_grader.io import read_notebook, copy_submission_to_workdir, send_feedback
5
+ from dlai_grader.notebook import keep_tagged_cells
6
+ from dlai_grader.grading import compute_grading_score, graded_obj_missing
7
+ from grader import handle_part_id
8
+
9
+
10
+ def notebook_grading(config, compile_solution=False):
11
+ try:
12
+ nb = read_notebook(config.submission_file_path)
13
+ except Exception as e:
14
+ msg = f"There was a problem reading your notebook. Details:\n{e!s}"
15
+ send_feedback(0.0, msg, err=True)
16
+
17
+ transformations = [keep_tagged_cells()]
18
+ for t in transformations:
19
+ nb = t(nb)
20
+
21
+ try:
22
+ learner_mod = compile_partial_module(nb, "learner_mod", verbose=False)
23
+ except Exception as e:
24
+ msg = f"There was a problem compiling the code from your notebook, please check that you saved before submitting. Details:\n{e!s}"
25
+ send_feedback(0.0, msg, err=True)
26
+
27
+ solution_mod = None
28
+ if compile_solution:
29
+ solution_nb = read_notebook(config.solution_file_path)
30
+ for t in transformations:
31
+ solution_nb = t(solution_nb)
32
+ solution_mod = compile_partial_module(
33
+ solution_nb,
34
+ "solution_mod",
35
+ verbose=False,
36
+ )
37
+
38
+ return learner_mod, solution_mod
39
+
40
+
41
+ def main() -> None:
42
+ copy_submission_to_workdir()
43
+
44
+ part_id = get_part_id()
45
+
46
+ c = Config()
47
+
48
+ learner_mod, solution_mod = notebook_grading(c, compile_solution=True)
49
+
50
+ g_func = handle_part_id(part_id)(learner_mod, solution_mod)
51
+
52
+ try:
53
+ cases = g_func()
54
+ except Exception as e:
55
+ msg = f"There was an error grading your submission. Details:\n{e!s}"
56
+ send_feedback(0.0, msg, err=True)
57
+
58
+ if graded_obj_missing(cases):
59
+ msg = "Object required for grading not found. If you haven't completed the exercise this might be expected. Otherwise, check your solution as grader omits cells that throw errors."
60
+ send_feedback(0.0, msg, err=True)
61
+
62
+ score, feedback = compute_grading_score(cases)
63
+ send_feedback(score, feedback)
64
+
65
+
66
+ if __name__ == "__main__":
67
+ try:
68
+ main()
69
+ except Exception as e:
70
+ msg = f"There was an error with the program. Exception:\n{e!s}.\nTraceback:\n{traceback.format_exc()}"
71
+ send_feedback(0.0, msg, err=True)
@@ -0,0 +1,92 @@
1
+ import traceback
2
+
3
+ from grader import handle_part_id
4
+
5
+ from dlai_grader.compiler import compile_partial_module
6
+ from dlai_grader.config import Config, get_part_id
7
+ from dlai_grader.grading import (
8
+ LearnerSubmission,
9
+ compute_grading_score,
10
+ graded_obj_missing,
11
+ )
12
+ from dlai_grader.io import copy_submission_to_workdir, read_notebook, send_feedback
13
+ from dlai_grader.notebook import keep_tagged_cells
14
+
15
+
16
+ def notebook_grading(config, compile_solution=False):
17
+ try:
18
+ nb = read_notebook(config.submission_file_path)
19
+ except Exception as e:
20
+ msg = f"There was a problem reading your notebook. Details:\n{e!s}"
21
+ send_feedback(0.0, msg, err=True)
22
+
23
+ transformations = [keep_tagged_cells()]
24
+ for t in transformations:
25
+ nb = t(nb)
26
+
27
+ try:
28
+ learner_mod = compile_partial_module(nb, "learner_mod", verbose=False)
29
+ except Exception as e:
30
+ msg = f"There was a problem compiling the code from your notebook, please check that you saved before submitting. Details:\n{e!s}"
31
+ send_feedback(0.0, msg, err=True)
32
+
33
+ solution_mod = None
34
+ if compile_solution:
35
+ solution_nb = read_notebook(config.solution_file_path)
36
+ for t in transformations:
37
+ solution_nb = t(solution_nb)
38
+ solution_mod = compile_partial_module(
39
+ solution_nb,
40
+ "solution_mod",
41
+ verbose=False,
42
+ )
43
+
44
+ return learner_mod, solution_mod
45
+
46
+
47
+ def non_notebook_grading(config):
48
+ try:
49
+ with open(config.submission_file_path, "r") as file:
50
+ contents = file.read()
51
+ except Exception as e:
52
+ msg = f"There was an error reading your submission. Details:\n{e!s}"
53
+ send_feedback(0.0, msg, err=True)
54
+
55
+ return LearnerSubmission(submission=contents)
56
+
57
+
58
+ def main() -> None:
59
+ part_id = get_part_id()
60
+
61
+ match part_id:
62
+ case "123":
63
+ copy_submission_to_workdir(file_name="{{EXTRA_FILE_NAME}}")
64
+ c = Config(submission_file="{{EXTRA_FILE_NAME}}")
65
+ learner_mod, solution_mod = non_notebook_grading(c), None
66
+ case _:
67
+ copy_submission_to_workdir()
68
+ c = Config()
69
+ learner_mod, solution_mod = notebook_grading(c, compile_solution=True)
70
+
71
+ g_func = handle_part_id(part_id)(learner_mod, solution_mod)
72
+
73
+ try:
74
+ cases = g_func()
75
+ except Exception as e:
76
+ msg = f"There was an error grading your submission. Details:\n{e!s}"
77
+ send_feedback(0.0, msg, err=True)
78
+
79
+ if graded_obj_missing(cases):
80
+ msg = "Object required for grading not found. If you haven't completed the exercise this might be expected. Otherwise, check your solution as grader omits cells that throw errors."
81
+ send_feedback(0.0, msg, err=True)
82
+
83
+ score, feedback = compute_grading_score(cases)
84
+ send_feedback(score, feedback)
85
+
86
+
87
+ if __name__ == "__main__":
88
+ try:
89
+ main()
90
+ except Exception as e:
91
+ msg = f"There was an error with the program. Exception:\n{e!s}.\nTraceback:\n{traceback.format_exc()}"
92
+ send_feedback(0.0, msg, err=True)
@@ -0,0 +1,32 @@
1
+ from types import ModuleType, FunctionType
2
+ from dlai_grader.grading import test_case, object_to_grade
3
+ from dlai_grader.types import grading_function, grading_wrapper, learner_submission
4
+
5
+
6
+ def part_1(
7
+ learner_mod: learner_submission,
8
+ solution_mod: ModuleType | None = None,
9
+ ) -> grading_function:
10
+ @object_to_grade(learner_mod, "learner_func")
11
+ def g(learner_func: FunctionType) -> list[test_case]:
12
+
13
+ cases: list[test_case] = []
14
+
15
+ t = test_case()
16
+ if not isinstance(learner_func, FunctionType):
17
+ t.fail()
18
+ t.msg = "learner_func has incorrect type"
19
+ t.want = FunctionType
20
+ t.got = type(learner_func)
21
+ return [t]
22
+
23
+ return cases
24
+
25
+ return g
26
+
27
+
28
+ def handle_part_id(part_id: str) -> grading_wrapper:
29
+ grader_dict: dict[str, grading_wrapper] = {
30
+ "": part_1,
31
+ }
32
+ return grader_dict[part_id]
dlai_grader/templates.py CHANGED
@@ -1,5 +1,9 @@
1
1
  import sys
2
+ import textwrap
2
3
  from textwrap import dedent
4
+ import shutil
5
+ import os
6
+ from pathlib import Path
3
7
 
4
8
 
5
9
  def generate_copy_assignment_script(
@@ -8,213 +12,69 @@ def generate_copy_assignment_script(
8
12
  extra_file_name="foo.txt",
9
13
  ):
10
14
  """
11
- Generate copy_assignment_to_submission.sh script with optional extra file copying.
15
+ Copy the appropriate copy_assignment_to_submission.sh script from templates depending on whether an extra file is required.
12
16
 
13
- Args:
14
- extra_file_required (str): Include extra file copying if "y"
15
- assignment_name (str): The name of the assignment notebook file
16
- extra_file_name (str): The name of the extra file to copy (if required)
17
+ Template files should be named:
18
+ extrafile_n (no extra file)
19
+ extrafile_y (with extra file)
17
20
 
18
21
  Returns:
19
- str: The complete script content
22
+ str: The final shell script contents after variable substitution.
20
23
 
21
24
  """
22
- # Common script header and initial variables
23
- header = [
24
- "#!/bin/bash",
25
- "set -euo pipefail",
26
- "",
27
- "# each grader should modify Assignment and Submission file to fulfill the grader setting",
28
- f"Assignment={assignment_name}",
29
- ]
30
-
31
- # Add extra file declaration if required
32
- if extra_file_required == "y":
33
- header.append(f"Extra_file={extra_file_name}")
34
-
35
- # Common script variables
36
- variables = [
37
- "",
38
- "SubmissionFile=submission.ipynb",
39
- "SubmissionPath=/shared/submission",
40
- "SharedDiskPath=/learner_workplace/$UserId/$CourseId/$LessonId",
41
- "",
42
- "# copy synced files (exam image typically sync all files in lesson folder)",
43
- 'echo "Copy learner submission from $SharedDiskPath/$Assignment to $SubmissionPath/$SubmissionFile"',
44
- "cp $SharedDiskPath/$Assignment $SubmissionPath/$SubmissionFile",
45
- ]
46
-
47
- # Add extra file copying if required
48
- extra_file_copy = []
49
- if extra_file_required == "y":
50
- extra_file_copy = [
51
- 'echo "Copy learner submission from $SharedDiskPath/$Extra_file to $SubmissionPath/$Extra_file"',
52
- "cp $SharedDiskPath/$Extra_file $SubmissionPath/$Extra_file",
53
- ]
25
+ if extra_file_required not in ("y", "n"):
26
+ raise ValueError(f"Invalid extra_file_required value: {extra_file_required!r}")
27
+
28
+ # Define template name pattern
29
+ template_name = f"extrafile_{extra_file_required}"
54
30
 
55
- # Combine all sections
56
- content = header + variables + extra_file_copy
31
+ # Paths
32
+ base_dir = Path(__file__).parent
33
+ src = base_dir / "templates" / "copy_assignment_sh" / template_name
34
+ dst = Path("copy_assignment_to_submission.sh")
57
35
 
58
- return "\n".join(content)
36
+ # Validate existence
37
+ if not src.exists():
38
+ raise FileNotFoundError(f"Template not found: {src}")
39
+
40
+ # Read and substitute placeholders
41
+ content = src.read_text(encoding="utf-8")
42
+ content = content.replace("{{ASSIGNMENT_NAME}}", assignment_name).replace(
43
+ "{{EXTRA_FILE_NAME}}", extra_file_name
44
+ )
59
45
 
46
+ # Write output
47
+ dst.write_text(content, encoding="utf-8")
48
+ dst.chmod(0o755) # make executable
49
+ return content
60
50
 
61
- def generate_entry_py(
62
- non_notebook_grading="n",
51
+
52
+ def copy_entry_script(
53
+ sol_dir_required: str,
54
+ non_notebook_grading: str,
63
55
  extra_file_name="foo.txt",
64
- ):
65
- """
66
- Generate entry.py with optional non-notebook grading capability.
56
+ ) -> str:
57
+ # Validate inputs
58
+ if sol_dir_required not in ("y", "n"):
59
+ raise ValueError(f"Invalid sol_dir_required value: {sol_dir_required!r}")
60
+ if non_notebook_grading not in ("y", "n"):
61
+ raise ValueError(
62
+ f"Invalid non_notebook_grading value: {non_notebook_grading!r}"
63
+ )
67
64
 
68
- Args:
69
- non_notebook_grading (str): Include non-notebook grading if "y"
70
- extra_file_name (str): Name of extra file to grade
65
+ template_name = f"solution_{sol_dir_required}_file_{non_notebook_grading}.py"
71
66
 
72
- Returns:
73
- str: The complete entry.py content
67
+ base_dir = Path(__file__).parent
68
+ src = base_dir / "templates" / "entry_py" / template_name
69
+ content = src.read_text(encoding="utf-8")
70
+ content = content.replace("{{EXTRA_FILE_NAME}}", extra_file_name)
71
+ dst = Path("entry.py")
74
72
 
75
- """
76
- # Common imports for both versions
77
- imports = [
78
- "from dlai_grader.config import Config, get_part_id",
79
- "from dlai_grader.compiler import compile_partial_module",
80
- "from dlai_grader.io import read_notebook, copy_submission_to_workdir, send_feedback",
81
- "from dlai_grader.notebook import keep_tagged_cells",
82
- "from dlai_grader.grading import (",
83
- " compute_grading_score,",
84
- " graded_obj_missing,",
85
- " LearnerSubmission,",
86
- ")",
87
- "from grader import handle_part_id",
88
- "",
89
- ]
90
-
91
- # Notebook grading function (common to both versions)
92
- notebook_grading_func = [
93
- "",
94
- "def notebook_grading(config, compile_solution=False):",
95
- " try:",
96
- " nb = read_notebook(config.submission_file_path)",
97
- " except Exception as e:",
98
- ' msg = f"There was a problem reading your notebook. Details:\\n{e!s}"',
99
- " send_feedback(0.0, msg, err=True)",
100
- "",
101
- " transformations = [keep_tagged_cells()]",
102
- " for t in transformations:",
103
- " nb = t(nb)",
104
- "",
105
- " try:",
106
- ' learner_mod = compile_partial_module(nb, "learner_mod", verbose=False)',
107
- " except Exception as e:",
108
- ' msg = f"There was a problem compiling the code from your notebook, please check that you saved before submitting. Details:\\n{e!s}"',
109
- " send_feedback(0.0, msg, err=True)",
110
- "",
111
- " solution_mod = None",
112
- " if compile_solution:",
113
- " solution_nb = read_notebook(config.solution_file_path)",
114
- " for t in transformations:",
115
- " solution_nb = t(solution_nb)",
116
- " solution_mod = compile_partial_module(",
117
- ' solution_nb, "solution_mod", verbose=False',
118
- " )",
119
- "",
120
- " return learner_mod, solution_mod",
121
- "",
122
- ]
123
-
124
- # Non-notebook grading function (only for version with non_notebook_grading)
125
- non_notebook_grading_func = [
126
- "",
127
- "def non_notebook_grading(config):",
128
- " try:",
129
- ' with open(config.submission_file_path, "r") as file:',
130
- " contents = file.read()",
131
- " except Exception as e:",
132
- ' msg = f"There was an error reading your submission. Details:\\n{e!s}"',
133
- " send_feedback(0.0, msg, err=True)",
134
- "",
135
- " return LearnerSubmission(submission=contents)",
136
- "",
137
- ]
138
-
139
- # Main function for version without non-notebook grading
140
- main_func_simple = [
141
- "def main() -> None:",
142
- " copy_submission_to_workdir()",
143
- "",
144
- " part_id = get_part_id()",
145
- "",
146
- " c = Config()",
147
- "",
148
- " learner_mod, _ = notebook_grading(c)",
149
- "",
150
- " g_func = handle_part_id(part_id)(learner_mod)",
151
- "",
152
- " try:",
153
- " cases = g_func()",
154
- " except Exception as e:",
155
- ' msg = f"There was an error grading your submission. Details:\\n{e!s}"',
156
- " send_feedback(0.0, msg, err=True)",
157
- "",
158
- " if graded_obj_missing(cases):",
159
- ' msg = "Object required for grading not found. If you haven\'t completed the exercise this might be expected. Otherwise, check your solution as grader omits cells that throw errors."',
160
- " send_feedback(0.0, msg, err=True)",
161
- "",
162
- " score, feedback = compute_grading_score(cases)",
163
- " send_feedback(score, feedback)",
164
- "",
165
- ]
166
-
167
- # Main function for version with non-notebook grading
168
- main_func_with_non_notebook = [
169
- "def main() -> None:",
170
- " copy_submission_to_workdir()",
171
- "",
172
- " part_id = get_part_id()",
173
- "",
174
- " match part_id:",
175
- ' case "123":',
176
- f' c = Config(submission_file="{extra_file_name}")',
177
- " learner_mod = non_notebook_grading(c)",
178
- " case _:",
179
- " c = Config()",
180
- " learner_mod, _ = notebook_grading(c)",
181
- "",
182
- " g_func = handle_part_id(part_id)(learner_mod)",
183
- "",
184
- " try:",
185
- " cases = g_func()",
186
- " except Exception as e:",
187
- ' msg = f"There was an error grading your submission. Details:\\n{e!s}"',
188
- " send_feedback(0.0, msg, err=True)",
189
- "",
190
- " if graded_obj_missing(cases):",
191
- ' msg = "Object required for grading not found. If you haven\'t completed the exercise this might be expected. Otherwise, check your solution as grader omits cells that throw errors."',
192
- " send_feedback(0.0, msg, err=True)",
193
- "",
194
- " score, feedback = compute_grading_score(cases)",
195
- " send_feedback(score, feedback)",
196
- "",
197
- ]
198
-
199
- # Common script entry point
200
- entry_point = [
201
- 'if __name__ == "__main__":',
202
- " main()",
203
- "",
204
- ]
205
-
206
- # Combine all sections based on configuration
207
- content = imports + notebook_grading_func
208
-
209
- if non_notebook_grading == "y":
210
- content.extend(non_notebook_grading_func)
211
- content.extend(main_func_with_non_notebook)
212
- else:
213
- content.extend(main_func_simple)
214
-
215
- content.extend(entry_point)
216
-
217
- return "\n".join(content)
73
+ if not src.exists():
74
+ raise FileNotFoundError(f"Template not found: {src}")
75
+
76
+ # shutil.copy(src, dst)
77
+ return content
218
78
 
219
79
 
220
80
  def generate_dockerfile(data_dir_required="n", sol_dir_required="n"):
@@ -225,46 +85,57 @@ def generate_dockerfile(data_dir_required="n", sol_dir_required="n"):
225
85
  data_dir_required (str): Include data directory if "y"
226
86
  sol_dir_required (str): Include solution directory if "y"
227
87
 
228
- Returns:
229
- str: The complete Dockerfile content
230
-
231
88
  """
232
- base_content = [
233
- "FROM continuumio/miniconda3@sha256:d601a04ea48fd45e60808c7072243d33703d29434d2067816b7f26b0705d889a",
234
- "",
235
- "RUN apk update && apk add libstdc++",
236
- "",
237
- "COPY requirements.txt .",
238
- "",
239
- "RUN pip install -r requirements.txt && rm requirements.txt",
240
- "",
241
- "RUN mkdir /grader && \\ \nmkdir /grader/submission",
242
- "",
243
- "COPY .conf /grader/.conf",
244
- ]
245
-
246
- # Add optional file copies based on config
247
- if data_dir_required == "y":
248
- base_content.append("COPY data/ /grader/data/")
249
-
250
- if sol_dir_required == "y":
251
- base_content.append("COPY solution/ /grader/solution/")
252
-
253
- # Add final common parts
254
- base_content.extend(
255
- [
256
- "COPY entry.py /grader/entry.py",
257
- "COPY grader.py /grader/grader.py",
258
- "",
259
- "RUN chmod a+rwx /grader/",
260
- "",
261
- "WORKDIR /grader/",
262
- "",
263
- 'ENTRYPOINT ["python", "entry.py"]',
264
- ]
265
- )
89
+ # Validate inputs
90
+ if data_dir_required not in ("y", "n"):
91
+ raise ValueError(f"Invalid data_dir_required value: {data_dir_required!r}")
92
+ if sol_dir_required not in ("y", "n"):
93
+ raise ValueError(f"Invalid sol_dir_required value: {sol_dir_required!r}")
266
94
 
267
- return "\n".join(base_content)
95
+ template_name = f"data_{data_dir_required}_solution_{sol_dir_required}"
96
+
97
+ # Define paths
98
+ base_dir = Path(__file__).parent
99
+ src = base_dir / "templates" / "dockerfile" / template_name
100
+ dst = Path("Dockerfile")
101
+
102
+ # Ensure the source exists
103
+ if not src.exists():
104
+ raise FileNotFoundError(f"Template not found: {src}")
105
+
106
+ # Copy template to current directory
107
+ # shutil.copy(src, dst)
108
+
109
+ # Return the Dockerfile contents
110
+ return src.read_text(encoding="utf-8")
111
+
112
+
113
+ def copy_makefile() -> str:
114
+ base_dir = Path(__file__).parent
115
+ src = base_dir / "templates" / "Makefile"
116
+ # content = src.read_text(encoding="utf-8")
117
+ # content = content.replace("{{HARD_MEMORY}}", hard_memory)
118
+ # content = content.replace("{{CPUS}}", cpus)
119
+ # content = content.replace("{{SOFT_MEMORY}}", soft_memory)
120
+ dst = Path("Makefile")
121
+
122
+ if not src.exists():
123
+ raise FileNotFoundError(f"Template not found: {src}")
124
+
125
+ # shutil.copy(src, dst)
126
+ return src.read_text(encoding="utf-8")
127
+
128
+
129
+ def copy_grader_py() -> str:
130
+ base_dir = Path(__file__).parent
131
+ src = base_dir / "templates" / "grader.py"
132
+ dst = Path("grader.py")
133
+
134
+ if not src.exists():
135
+ raise FileNotFoundError(f"Template not found: {src}")
136
+
137
+ # shutil.copy(src, dst)
138
+ return src.read_text(encoding="utf-8")
268
139
 
269
140
 
270
141
  def load_templates() -> dict[str, str]:
@@ -272,38 +143,115 @@ def load_templates() -> dict[str, str]:
272
143
  course = input("Number of the course: ")
273
144
  module = input("Number of the module: ")
274
145
 
275
- unit_test_filename = input("Filename for unit tests (leave empty for unittests): ")
276
- unit_test_filename = unit_test_filename if unit_test_filename else "unittests"
277
- version = input("Version of the grader (leave empty for version 1): ")
278
- version = version if version else "1"
279
- data_dir_required = input("Do you require a data dir? y/n (leave empty for n): ")
280
- data_dir_required = data_dir_required if data_dir_required else "n"
281
-
282
- if data_dir_required not in ["y", "n"]:
283
- print("invalid option selected")
284
- sys.exit(1)
285
-
286
- sol_dir_required = input(
287
- "Do you require a solution file? y/n (leave empty for n): "
146
+ grader_mvp = input(
147
+ "Use minimum grader (no extra config)? y/n (leave empty for n): ",
288
148
  )
289
- sol_dir_required = sol_dir_required if sol_dir_required else "n"
290
- if sol_dir_required not in ["y", "n"]:
291
- print("invalid option selected")
292
- sys.exit(1)
293
149
 
294
- non_notebook_grading = input(
295
- "Will you grade a file different from a notebook? y/n (leave empty for n): ",
296
- )
297
- non_notebook_grading = non_notebook_grading if non_notebook_grading else "n"
298
- if non_notebook_grading not in ["y", "n"]:
150
+ grader_mvp = grader_mvp if grader_mvp else "n"
151
+ if grader_mvp not in ["y", "n"]:
299
152
  print("invalid option selected")
300
153
  sys.exit(1)
301
154
 
302
- extra_file_name = ""
303
- if non_notebook_grading == "y":
304
- extra_file_name = input(
305
- "Name of the extra file to grade: ",
155
+ if grader_mvp == "n":
156
+ unit_test_filename = input(
157
+ "Filename for unit tests (leave empty for unittests): "
306
158
  )
159
+ unit_test_filename = unit_test_filename if unit_test_filename else "unittests"
160
+ # version = input("Version of the grader (leave empty for version 1): ")
161
+ version = "1"
162
+
163
+ data_dir_required = ""
164
+ while data_dir_required not in ["y", "n"]:
165
+ data_dir_required = input(
166
+ "Do you require a data dir? y/n (leave empty for n): ",
167
+ )
168
+ if data_dir_required == "":
169
+ data_dir_required = "n"
170
+ # data_dir_required = data_dir_required if data_dir_required else "n"
171
+
172
+ sol_dir_required = ""
173
+ while sol_dir_required not in ["y", "n"]:
174
+ sol_dir_required = input(
175
+ "Do you require a solution file? y/n (leave empty for n): ",
176
+ )
177
+ if sol_dir_required == "":
178
+ sol_dir_required = "n"
179
+
180
+ non_notebook_grading = ""
181
+ while non_notebook_grading not in ["y", "n"]:
182
+ non_notebook_grading = input(
183
+ "Will you grade a file different from a notebook? y/n (leave empty for n): ",
184
+ )
185
+ if non_notebook_grading == "":
186
+ non_notebook_grading = "n"
187
+
188
+ extra_file_name = ""
189
+ if non_notebook_grading == "y":
190
+ extra_file_name = input(
191
+ "Name of the extra file to grade: ",
192
+ )
193
+
194
+ cpus = ""
195
+ while cpus not in ["0.25", "0.5", "0.75", "1"]:
196
+ cpus = input("CPU Units (leave empty for 0.25): ")
197
+ if cpus == "":
198
+ cpus = "0.25"
199
+
200
+ if cpus not in ["0.25", "0.5", "0.75", "1"]:
201
+ print(f"Options are: {['0.25', '0.5', '0.75', '1']}")
202
+
203
+ soft_memory = ""
204
+ soft_memory_options = [
205
+ "512m",
206
+ "768m",
207
+ "1024m",
208
+ "2048m",
209
+ "4096m",
210
+ "8192m",
211
+ "1g",
212
+ "2g",
213
+ "4g",
214
+ "8g",
215
+ ]
216
+ while soft_memory not in soft_memory_options:
217
+ soft_memory = input("Memory Reservation (leave empty for 512m): ")
218
+ if soft_memory == "":
219
+ soft_memory = "512m"
220
+
221
+ if soft_memory not in soft_memory_options:
222
+ print(f"Options are: {soft_memory_options}")
223
+
224
+ hard_memory = ""
225
+ hard_memory_options = [
226
+ "1024m",
227
+ "2048m",
228
+ "4096m",
229
+ "8192m",
230
+ "15000m",
231
+ "1g",
232
+ "2g",
233
+ "4g",
234
+ "8g",
235
+ "15g",
236
+ ]
237
+ while hard_memory not in hard_memory_options:
238
+ hard_memory = input("Memory Limit (leave empty for 1g): ")
239
+ if hard_memory == "":
240
+ hard_memory = "1g"
241
+
242
+ if hard_memory not in hard_memory_options:
243
+ print(f"Options are: {hard_memory_options}")
244
+
245
+ if grader_mvp == "y":
246
+ unit_test_filename = "unittests"
247
+ version = "1"
248
+ data_dir_required = "n"
249
+ sol_dir_required = "n"
250
+ non_notebook_grading = "n"
251
+ extra_file_name = ""
252
+ cpus = "0.25"
253
+ soft_memory = "512m"
254
+ hard_memory = "1g"
307
255
 
308
256
  dockerfile = generate_dockerfile(
309
257
  data_dir_required=data_dir_required,
@@ -318,6 +266,9 @@ def load_templates() -> dict[str, str]:
318
266
  TAG_ID=V$(GRADER_VERSION)
319
267
  SUB_DIR=mount
320
268
  MEMORY_LIMIT=4096
269
+ CPUS={cpus}
270
+ SOFT_MEMORY={soft_memory}
271
+ HARD_MEMORY={hard_memory}
321
272
  """
322
273
 
323
274
  assignment_name = f"C{course}M{module}_Assignment.ipynb"
@@ -328,111 +279,23 @@ def load_templates() -> dict[str, str]:
328
279
  extra_file_name=extra_file_name,
329
280
  )
330
281
 
331
- makefile = """
332
- .PHONY: sync learner build debug-unsafe debug-safe grade versioning tag undeletable uneditable init upgrade coursera zip
333
-
334
- include .conf
335
-
336
- PARTIDS = 123 456
337
- OS := $(shell uname)
338
-
339
- sync:
340
- cp mount/submission.ipynb ../$(ASSIGNMENT_NAME)_Solution.ipynb
341
- cp learner/$(ASSIGNMENT_NAME).ipynb ../$(ASSIGNMENT_NAME).ipynb
342
- cp mount/$(UNIT_TESTS_NAME).py ../$(UNIT_TESTS_NAME).py
343
-
344
- learner:
345
- dlai_grader --learner --output_notebook=./learner/$(ASSIGNMENT_NAME).ipynb
346
- rsync -a --exclude="submission.ipynb" --exclude="__pycache__" --exclude=".mypy_cache" ./mount/ ./learner/
347
-
348
- build:
349
- docker build -t $(IMAGE_NAME):$(TAG_ID) .
350
-
351
- debug-unsafe:
352
- docker run -it --rm --mount type=bind,source=$(PWD)/mount,target=/shared/submission --mount type=bind,source=$(PWD),target=/grader/ --env-file $(PWD)/.env --entrypoint ash $(IMAGE_NAME):$(TAG_ID)
353
-
354
- debug-safe:
355
- docker run -it --rm --mount type=bind,source=$(PWD)/mount,target=/shared/submission --env-file $(PWD)/.env --entrypoint ash $(IMAGE_NAME):$(TAG_ID)
356
-
357
- grade:
358
- docker run -it --rm --mount type=bind,source=$(PWD)/mount,target=/shared/submission --env-file $(PWD)/.env --entrypoint ash $(IMAGE_NAME):$(TAG_ID) -c 'for partId in $(PARTIDS); do export partId=$$partId; echo "Processing part $$partId"; python entry.py; done'
359
-
360
- versioning:
361
- dlai_grader --versioning
362
-
363
- tag:
364
- dlai_grader --tag
365
-
366
- undeletable:
367
- dlai_grader --undeletable
282
+ makefile = copy_makefile()
368
283
 
369
- uneditable:
370
- dlai_grader --uneditable
284
+ grader_py = copy_grader_py()
371
285
 
372
- init:
373
- dlai_grader --versioning
374
- dlai_grader --tag
375
- dlai_grader --undeletable
376
- dlai_grader --uneditable
377
-
378
- upgrade:
379
- dlai_grader --upgrade
380
-
381
- coursera:
382
- dlai_grader --grade --partids="$(PARTIDS)" --docker=$(IMAGE_NAME):$(TAG_ID) --memory=$(MEMORY_LIMIT) --submission=$(SUB_DIR)
383
-
384
- zip:
385
- zip -r $(IMAGE_NAME)$(TAG_ID).zip .
386
-
387
- """
388
-
389
- grader_py = """
390
- from types import ModuleType, FunctionType
391
- from dlai_grader.grading import test_case, object_to_grade
392
- from dlai_grader.types import grading_function, grading_wrapper, learner_submission
393
-
394
-
395
- def part_1(
396
- learner_mod: learner_submission,
397
- solution_mod: ModuleType | None = None,
398
- ) -> grading_function:
399
- @object_to_grade(learner_mod, "learner_func")
400
- def g(learner_func: FunctionType) -> list[test_case]:
401
-
402
- cases: list[test_case] = []
403
-
404
- t = test_case()
405
- if not isinstance(learner_func, FunctionType):
406
- t.fail()
407
- t.msg = "learner_func has incorrect type"
408
- t.want = FunctionType
409
- t.got = type(learner_func)
410
- return [t]
411
-
412
- return cases
413
-
414
- return g
415
-
416
-
417
- def handle_part_id(part_id: str) -> grading_wrapper:
418
- grader_dict: dict[str, grading_wrapper] = {
419
- "": part_1,
420
- }
421
- return grader_dict[part_id]
422
- """
423
-
424
- entry_py = generate_entry_py(
286
+ entry_py = copy_entry_script(
287
+ sol_dir_required=sol_dir_required,
425
288
  non_notebook_grading=non_notebook_grading,
426
289
  extra_file_name=extra_file_name,
427
290
  )
428
291
 
429
292
  template_dict = {
430
293
  "dockerfile": dedent(dockerfile),
431
- "makefile": dedent(makefile[1:]),
294
+ "makefile": dedent(makefile),
432
295
  "conf": dedent(conf[1:]),
433
- "grader_py": dedent(grader_py[1:]),
296
+ "grader_py": dedent(grader_py),
434
297
  "entry_py": dedent(entry_py),
435
- "copy_assignment_to_submission_sh": dedent(copy_assignment_to_submission_sh),
298
+ # "copy_assignment_to_submission_sh": dedent(copy_assignment_to_submission_sh),
436
299
  }
437
300
 
438
301
  return template_dict
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dlai-grader
3
- Version: 2.0b1
3
+ Version: 2.0b3
4
4
  Summary: Grading utilities for DLAI courses
5
5
  Home-page: https://github.com/https-deeplearning-ai/grader
6
6
  Author: Andres Zarta
@@ -0,0 +1,27 @@
1
+ dlai_grader/__init__.py,sha256=kisxRYjpk8bk5dZK5AMHM69PqUB6bwdnig0LWjY9E0o,210
2
+ dlai_grader/cli.py,sha256=NIwboE-AFn1LXOFmF4O70Ow0fkRxgclG_eMwmWiua38,4917
3
+ dlai_grader/compiler.py,sha256=elbHNUCqBCoOOoNmMRXbgeNL0nt0RM57eZi0-6AqycA,3036
4
+ dlai_grader/config.py,sha256=DokK1tVF_r7v0p9tWpBN-7lOAlPmHSpFXDZiI8cGw7s,1821
5
+ dlai_grader/grading.py,sha256=BMIoZ_loQDuNCEk1Dj3on4IWz-FGgIbnhbDyk8HmQ7c,5041
6
+ dlai_grader/io.py,sha256=5hs7Bv5zfyHKZUrFP-2gVH7y9dbIFsyGiKgsqFYbrMs,9250
7
+ dlai_grader/notebook.py,sha256=MgxZFuetTXwwZ-HXSB5ItLVD_9LP45E0xHAngS0g4EU,12101
8
+ dlai_grader/templates.py,sha256=OdmwnmNS2KCCcZ4-9jqfclV43tmbJoSuCk-5QWU6KoE,9291
9
+ dlai_grader/types.py,sha256=5uiFaF3aDn-vjxTp9ec-ND-PRqeeV2_NfPHS2ngGsRo,306
10
+ dlai_grader/templates/Makefile,sha256=PhRJ-87fU3IMqYMt9ChrAAfr2BPlk0uwogygLZItZL8,1795
11
+ dlai_grader/templates/grader.py,sha256=492Dzs3enoCGfDviq_mdnrzeF5e1qNl21i42M5tjv4Y,896
12
+ dlai_grader/templates/copy_assignment_sh/extrafile_n,sha256=qB9ZViBm69r69nT9WXDaJfrW57sVho03aCZXbTS-lTc,430
13
+ dlai_grader/templates/copy_assignment_sh/extrafile_y,sha256=Zh07IAViyN-rELp4_-ZOJJSmTIigh0GxHLexW0ZGnMQ,616
14
+ dlai_grader/templates/dockerfile/data_n_solution_n,sha256=mD_fHhkzrGCCG3EPwXmafYVn1AoIcgBmSZLX8eCeH_g,447
15
+ dlai_grader/templates/dockerfile/data_n_solution_y,sha256=9iSyDj3-Jwm6RyAHNtVUDeD-XrYODB61i-HB3HfA70g,480
16
+ dlai_grader/templates/dockerfile/data_y_solution_n,sha256=i9nWcTAUCQzyhx3kM1aH64j0dkEMlYlEb6FFiE5k5oc,472
17
+ dlai_grader/templates/dockerfile/data_y_solution_y,sha256=xs6p-puJ-j5AeIuHiESL7X9kNSFZVCZ3Wb9SAW9KlUU,505
18
+ dlai_grader/templates/entry_py/solution_n_file_n.py,sha256=Y9cuwGuCrRHWfWmcvNP-Gh8mFcJL_JE0e9Ed09O7x6Q,2349
19
+ dlai_grader/templates/entry_py/solution_n_file_y.py,sha256=yHXK4iRVaxlhAwq3FiOfFEJdSRApfOgs3FMQtIe-A_c,2984
20
+ dlai_grader/templates/entry_py/solution_y_file_n.py,sha256=MKVmgXJVC1UtxtChsah7jUQ6-SL93kafZ02KrSayN0k,2397
21
+ dlai_grader/templates/entry_py/solution_y_file_y.py,sha256=7ioDw33vx0bRDw3yFlMX5DsjA2ZJxOL0g3s7ukQ36so,3054
22
+ dlai_grader-2.0b3.dist-info/licenses/LICENSE,sha256=a_kch_UqdJPtyxk35QJr9O84K_koPixqWPYW9On4-io,1072
23
+ dlai_grader-2.0b3.dist-info/METADATA,sha256=6Q4Ioa45aZ9Pof5bpa-pMm2av7gryZwnzf0Hcp94bdM,8776
24
+ dlai_grader-2.0b3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
+ dlai_grader-2.0b3.dist-info/entry_points.txt,sha256=4OcSAUIluONXa3ymViQ7CBQ2Lk52nb6xZnfph1rlMnk,71
26
+ dlai_grader-2.0b3.dist-info/top_level.txt,sha256=4YKtA3ztisFtx_g4hsGivy3J2NHnXxFziIMqawC8HWg,12
27
+ dlai_grader-2.0b3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
dlai_grader/py.typed DELETED
File without changes
@@ -1,16 +0,0 @@
1
- dlai_grader/__init__.py,sha256=V0DW6eFVqfyWdxFvJavFxD2QV63rZCpT400IzY1QBkk,210
2
- dlai_grader/cli.py,sha256=NIwboE-AFn1LXOFmF4O70Ow0fkRxgclG_eMwmWiua38,4917
3
- dlai_grader/compiler.py,sha256=elbHNUCqBCoOOoNmMRXbgeNL0nt0RM57eZi0-6AqycA,3036
4
- dlai_grader/config.py,sha256=DokK1tVF_r7v0p9tWpBN-7lOAlPmHSpFXDZiI8cGw7s,1821
5
- dlai_grader/grading.py,sha256=BMIoZ_loQDuNCEk1Dj3on4IWz-FGgIbnhbDyk8HmQ7c,5041
6
- dlai_grader/io.py,sha256=TEp5R8014EL9e-_RID5qp9INOujAGi7KLuy-SWWsPkM,8654
7
- dlai_grader/notebook.py,sha256=MgxZFuetTXwwZ-HXSB5ItLVD_9LP45E0xHAngS0g4EU,12101
8
- dlai_grader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- dlai_grader/templates.py,sha256=SEMv4KFlq_2jy4UORMzHStOOex9P6LXr48Ie-r1JZcc,14627
10
- dlai_grader/types.py,sha256=5uiFaF3aDn-vjxTp9ec-ND-PRqeeV2_NfPHS2ngGsRo,306
11
- dlai_grader-2.0b1.dist-info/licenses/LICENSE,sha256=a_kch_UqdJPtyxk35QJr9O84K_koPixqWPYW9On4-io,1072
12
- dlai_grader-2.0b1.dist-info/METADATA,sha256=0-7wdm6Otf4nHWASbjMHMBt8ruXvl3RrDakgAVPCMlA,8776
13
- dlai_grader-2.0b1.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
14
- dlai_grader-2.0b1.dist-info/entry_points.txt,sha256=4OcSAUIluONXa3ymViQ7CBQ2Lk52nb6xZnfph1rlMnk,71
15
- dlai_grader-2.0b1.dist-info/top_level.txt,sha256=4YKtA3ztisFtx_g4hsGivy3J2NHnXxFziIMqawC8HWg,12
16
- dlai_grader-2.0b1.dist-info/RECORD,,