From e5203f74000dc083d3e4024725a0e50656bf776b Mon Sep 17 00:00:00 2001 From: jwansek Date: Wed, 16 Feb 2022 15:45:37 +0000 Subject: fixed bug when client classes weren't present --- ExampleAssessments/example.yml | 114 ++++++------ ExampleSubmission/animals.py | 55 +++--- ExampleSubmission/example.py | 146 ++++++++-------- ExampleSubmission/test_dont_test_me.py | 12 +- examplerun.bat | 4 +- examplerun.sh | 3 + jinja_helpers.py | 1 + misc_classes.py | 168 +++++++++--------- pytest_template.jinja2 | 14 +- reflect.py | 10 +- requirements.txt | 10 +- smarker.conf | 32 ++-- templates/html.jinja2 | 0 templates/md.jinja2 | 306 ++++++++++++++++++--------------- templates/text.jinja2 | Bin 28 -> 10 bytes templates/txt.jinja2 | 38 +++- 16 files changed, 487 insertions(+), 426 deletions(-) create mode 100644 examplerun.sh delete mode 100644 templates/html.jinja2 mode change 100644 => 120000 templates/text.jinja2 diff --git a/ExampleAssessments/example.yml b/ExampleAssessments/example.yml index c8192a1..215a959 100644 --- a/ExampleAssessments/example.yml +++ b/ExampleAssessments/example.yml @@ -1,58 +1,58 @@ -name: CMP-5021B-19-20 -files: - - example.py: - classes: - - Application: - methods: - - __init__(3) - - a_method_with_defaults(3) - - add(3) - - aMethodThatIsntThere(1) - functions: - - hello_world(2) - - an_undocumented_function(0) - - aFunctionThatIsntThere(2) - - greet(2) - tests: - - | - dateOne = example.MyDate(day = 12, month = 8, year = 2001) - dateTwo = example.MyDate(day = 12, month = 8, year = 2001) - assert dateOne == dateTwo - - | - dateOne = example.MyDate(day = 12, month = 8, year = 2001) - dateTwo = example.MyDate(day = 5, month = 4, year = 1999) - assert dateOne == dateTwo - run: - - python example.py 3: - regexes: - - hello world\! - - python example.py 1: - regexes: - - hello world\! - - aFileThatIsntThere.py: - functions: - - hello_world(2) - - animals.py: - classes: - - Dog: - - Cat: - - Kitten: - tests: - - | - nibbles = animals.Kitten() - assert nibbles.speak() == "nyaa~~" - - | - milton = animals.Dog() - assert milton.move() == "*moves*" - run: - - python animals.py: - monitor: animals.txt - regexes: - - TURRÓN - - meow \(but cuter\) -produced_files: - - animals.txt -dependencies: - files: - - ../wsData.txt +name: CMP-5021B-19-20 +files: + - example.py: + classes: + - Application: + methods: + - __init__(3) + - a_method_with_defaults(3) + - add(3) + - aMethodThatIsntThere(1) + functions: + - hello_world(2) + - an_undocumented_function(0) + - aFunctionThatIsntThere(2) + - greet(2) + tests: + - | + dateOne = example.MyDate(day = 12, month = 8, year = 2001) + dateTwo = example.MyDate(day = 12, month = 8, year = 2001) + assert dateOne == dateTwo + - | + dateOne = example.MyDate(day = 12, month = 8, year = 2001) + dateTwo = example.MyDate(day = 5, month = 4, year = 1999) + assert dateOne == dateTwo + run: + - python example.py 3: + regexes: + - hello world\! + - python example.py 1: + regexes: + - hello world\! + - aFileThatIsntThere.py: + functions: + - hello_world(2) + - animals.py: + classes: + - Dog: + - Cat: + - Kitten: + tests: + - | + nibbles = animals.Kitten() + assert nibbles.speak() == "nyaa~~" + - | + milton = animals.Dog() + assert milton.move() == "*moves*" + run: + - python animals.py: + monitor: animals.txt + regexes: + - TURRÓN + - meow \(but cuter\) +produced_files: + - animals.txt +dependencies: + files: + - ../wsData.txt - ../IncludeDirectory \ No newline at end of file diff --git a/ExampleSubmission/animals.py b/ExampleSubmission/animals.py index 924c6fb..d1c933a 100644 --- a/ExampleSubmission/animals.py +++ b/ExampleSubmission/animals.py @@ -1,26 +1,31 @@ -import datetime - -class Animal: - def __init__(self): - self.birthday = datetime.datetime.now() - - def move(self): - return "*moves*" - -class Dog(Animal): - def speak(self): - return "woof" - -class Cat(Animal): - def speak(self): - return "meow" - -class Kitten(Cat): - """nyaa~~~ - """ - def speak(self): - return "meow (but cuter)" - -kitten = Kitten() -with open("animals.txt", "w") as f: +import datetime + +class Animal: + def __init__(self): + self.birthday = datetime.datetime.now() + + def move(self): + return "*moves*" + +class Dog(Animal): + """Some + + multiline + + docs""" + def speak(self): + return "woof" + +class Cat(Animal): + def speak(self): + return "meow" + +class Kitten(Cat): + """nyaa~~~ + """ + def speak(self): + return "meow (but cuter)" + +kitten = Kitten() +with open("animals.txt", "w") as f: f.write(kitten.speak()) \ No newline at end of file diff --git a/ExampleSubmission/example.py b/ExampleSubmission/example.py index 7029b33..9b5f0e8 100644 --- a/ExampleSubmission/example.py +++ b/ExampleSubmission/example.py @@ -1,74 +1,74 @@ -# Eden Attenborough -# 12-01-21 - -import tkinter as tk -from dataclasses import dataclass -import sys - -class Application(tk.Tk): - """An example class, which implements a GUI by inheriting from tkinter.Tk - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.title("Hello World!") - - def a_method_with_defaults(self, n:str, arg_1 = "hello", arg_2 = "world", count:int = 3): - """Adds two strings together with a space between them. - - Args: - arg_1 (str, optional): The first string to add. Defaults to "hello". - arg_2 (str, optional): The second string to add. Defaults to "world". - - Returns: - str: A concatinated string. - """ - return "%s %s" % (arg_1, arg_2) - - def add(self, num1:int, num2:int) -> int: - """Adds two numbers together and returns the output - - Args: - num1 (int): The first number to add - num2 (int): The second number to add - - Returns: - int: The two numbers added together - """ - return num1 + num2 - -@dataclass -class MyDate: - year:int - month:int - day:int - - def __eq__(self, otherDate): - return self.year == otherDate.year and self.month == otherDate.month and self.day == otherDate.day - - def __str__(self): - "%d-%d-%4d" % (self.day, self.month, self.year) - - -# hello world! -def hello_world(times): - """Prints 'hello world!' to stdout. Prints it out `times` times. - - Args: - times (int): The number of times to print out hello world. - - Returns: - str: Hello world, repeated as many times as nessicary - """ - return "hello world! " * times - -def an_undocumented_function(): - return 3.14156 - -# kwonlyargs demo -def greet(*names, greeting="Hello"): - for name in names: - print(greeting, name) - -if __name__ == "__main__": +# Eden Attenborough +# 12-01-21 + +import tkinter as tk +from dataclasses import dataclass +import sys + +class Application(tk.Tk): + """An example class, which implements a GUI by inheriting from tkinter.Tk + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.title("Hello World!") + + def a_method_with_defaults(self, n:str, arg_1 = "hello", arg_2 = "world", count:int = 3): + """Adds two strings together with a space between them. + + Args: + arg_1 (str, optional): The first string to add. Defaults to "hello". + arg_2 (str, optional): The second string to add. Defaults to "world". + + Returns: + str: A concatinated string. + """ + return "%s %s" % (arg_1, arg_2) + + def add(self, num1:int, num2:int) -> int: + """Adds two numbers together and returns the output + + Args: + num1 (int): The first number to add + num2 (int): The second number to add + + Returns: + int: The two numbers added together + """ + return num1 + num2 + +@dataclass +class MyDate: + year:int + month:int + day:int + + def __eq__(self, otherDate): + return self.year == otherDate.year and self.month == otherDate.month and self.day == otherDate.day + + def __str__(self): + "%d-%d-%4d" % (self.day, self.month, self.year) + + +# hello world! +def hello_world(times): + """Prints 'hello world!' to stdout. Prints it out `times` times. + + Args: + times (int): The number of times to print out hello world. + + Returns: + str: Hello world, repeated as many times as nessicary + """ + return "hello world! " * times + +def an_undocumented_function(): + return 3.14156 + +# kwonlyargs demo +def greet(*names, greeting="Hello"): + for name in names: + print(greeting, name) + +if __name__ == "__main__": print(hello_world(int(sys.argv[1])) + "\n\nowo" ) \ No newline at end of file diff --git a/ExampleSubmission/test_dont_test_me.py b/ExampleSubmission/test_dont_test_me.py index 511c713..808879c 100644 --- a/ExampleSubmission/test_dont_test_me.py +++ b/ExampleSubmission/test_dont_test_me.py @@ -1,7 +1,7 @@ -"""My default pytest will assume that all files prefixed with -'test' are test files. This file is here to make sure that -pytest only runs on the files it should run on. -""" - -def test_1(): +"""My default pytest will assume that all files prefixed with +'test' are test files. This file is here to make sure that +pytest only runs on the files it should run on. +""" + +def test_1(): assert 1 == 2 \ No newline at end of file diff --git a/examplerun.bat b/examplerun.bat index 48aa4eb..3364a63 100644 --- a/examplerun.bat +++ b/examplerun.bat @@ -1,3 +1,3 @@ -zip -r 100301654.zip .\ExampleSubmission\ -python .\mark.py -s 100301654.zip -a .\ExampleAssessments\example.yml -f md -o auto +zip -r 100301654.zip .\ExampleSubmission\ +python .\mark.py -s 100301654.zip -a .\ExampleAssessments\example.yml -f md -o auto rm 100301654.zip \ No newline at end of file diff --git a/examplerun.sh b/examplerun.sh new file mode 100644 index 0000000..7d4e3b4 --- /dev/null +++ b/examplerun.sh @@ -0,0 +1,3 @@ +zip -r 100301654.zip ./ExampleSubmission/ +python ./mark.py -s 100301654.zip -a ./ExampleAssessments/example.yml -f md -o auto +rm 100301654.zip \ No newline at end of file diff --git a/jinja_helpers.py b/jinja_helpers.py index 86b02e2..e72db35 100644 --- a/jinja_helpers.py +++ b/jinja_helpers.py @@ -10,6 +10,7 @@ def recurse_class_tree_text(tree, indent = 4): return yaml.dump(tree, indent = indent).replace(": {}", "") def flatten_struct(struct): + # print("Attempting to flatten: ", struct) out = {} for s in struct: key = list(s.keys())[0] diff --git a/misc_classes.py b/misc_classes.py index 6ac5897..9fdded3 100644 --- a/misc_classes.py +++ b/misc_classes.py @@ -1,84 +1,84 @@ -from dataclasses import dataclass -import tempfile -import zipfile -import shutil -import os - -@dataclass -class ExtractZipToTempDir(tempfile.TemporaryDirectory): - zip_file:str - - def __post_init__(self): - super().__init__() - - def __enter__(self): - return self.extract() - - def __exit__(self, exc, value, tb): - self.cleanup() - - def extract(self): - with zipfile.ZipFile(self.zip_file) as z: - z.extractall(self.name) - - # some zipping applications make a folder inside the zip with the files in that folder. - # try to deal with this here. - submission_files = self.name - if os.path.isdir( - os.path.join(submission_files, os.listdir(submission_files)[0]) - ) and len(os.listdir(submission_files)) == 1: - submission_files = os.path.join(submission_files, os.listdir(submission_files)[0]) - - return submission_files - -@dataclass -class FileDependencies: - assessment_struct:dict - to_:str=str(os.getcwd()) - - def __enter__(self): - self.get_deps() - - def __exit__(self, type, value, traceback): - self.rm_deps() - - def get_deps(self): - try: - for file_dep in self.assessment_struct["dependencies"]["files"]: - if os.path.isfile(file_dep): - shutil.copy(file_dep, os.path.join(self.to_, os.path.split(file_dep)[-1])) - else: - shutil.copytree(file_dep, os.path.join(self.to_, os.path.split(file_dep)[-1])) - except KeyError: - pass - - def rm_deps(self): - stuff_to_remove = [] - try: - stuff_to_remove += [os.path.split(f)[-1] for f in self.assessment_struct["dependencies"]["files"]] - except KeyError: - pass - try: - stuff_to_remove += self.assessment_struct["produced_files"] - except KeyError: - pass - - for file_dep in stuff_to_remove: - file_dep = os.path.join(self.to_, file_dep) - # print("rm: ", file_dep) - if os.path.exists(file_dep): - if os.path.isfile(file_dep): - os.remove(file_dep) - else: - shutil.rmtree(file_dep) - -@dataclass -class ChangeDirectory: - target:str - cwd:str=str(os.getcwd()) - - def __enter__(self): - os.chdir(self.target) - - def __exit__(self, type, value, traceback): - os.chdir(self.cwd) +from dataclasses import dataclass +import tempfile +import zipfile +import shutil +import os + +@dataclass +class ExtractZipToTempDir(tempfile.TemporaryDirectory): + zip_file:str + + def __post_init__(self): + super().__init__() + + def __enter__(self): + return self.extract() + + def __exit__(self, exc, value, tb): + self.cleanup() + + def extract(self): + with zipfile.ZipFile(self.zip_file) as z: + z.extractall(self.name) + + # some zipping applications make a folder inside the zip with the files in that folder. + # try to deal with this here. + submission_files = self.name + if os.path.isdir( + os.path.join(submission_files, os.listdir(submission_files)[0]) + ) and len(os.listdir(submission_files)) == 1: + submission_files = os.path.join(submission_files, os.listdir(submission_files)[0]) + + return submission_files + +@dataclass +class FileDependencies: + assessment_struct:dict + to_:str=str(os.getcwd()) + + def __enter__(self): + self.get_deps() + + def __exit__(self, type, value, traceback): + self.rm_deps() + + def get_deps(self): + try: + for file_dep in self.assessment_struct["dependencies"]["files"]: + if os.path.isfile(file_dep): + shutil.copy(file_dep, os.path.join(self.to_, os.path.split(file_dep)[-1])) + else: + shutil.copytree(file_dep, os.path.join(self.to_, os.path.split(file_dep)[-1])) + except KeyError: + pass + + def rm_deps(self): + stuff_to_remove = [] + try: + stuff_to_remove += [os.path.split(f)[-1] for f in self.assessment_struct["dependencies"]["files"]] + except KeyError: + pass + try: + stuff_to_remove += self.assessment_struct["produced_files"] + except KeyError: + pass + + for file_dep in stuff_to_remove: + file_dep = os.path.join(self.to_, file_dep) + # print("rm: ", file_dep) + if os.path.exists(file_dep): + if os.path.isfile(file_dep): + os.remove(file_dep) + else: + shutil.rmtree(file_dep) + +@dataclass +class ChangeDirectory: + target:str + cwd:str=str(os.getcwd()) + + def __enter__(self): + os.chdir(self.target) + + def __exit__(self, type, value, traceback): + os.chdir(self.cwd) diff --git a/pytest_template.jinja2 b/pytest_template.jinja2 index 473b7b4..73c9a40 100644 --- a/pytest_template.jinja2 +++ b/pytest_template.jinja2 @@ -1,8 +1,8 @@ -{# generating python with python :3 #} - -import {{ module }} - -{% for i, test_code in enumerate(filestests, 1) %} -def test_{{ i }}(): - {{ test_code|indent(4, False) }} {# the code in the config file must be indented with 4 spaces only #} +{# generating python with python :3 #} + +import {{ module }} + +{% for i, test_code in enumerate(filestests, 1) %} +def test_{{ i }}(): + {{ test_code|indent(4, False) }} {# the code in the config file must be indented with 4 spaces only #} {% endfor %} \ No newline at end of file diff --git a/reflect.py b/reflect.py index da6e998..b508f15 100644 --- a/reflect.py +++ b/reflect.py @@ -204,7 +204,7 @@ class Reflect: with tempfile.TemporaryDirectory() as tmp: junitxmlpath = os.path.join(tmp, "report.xml") test_files = [os.path.join(self.client_code_path, "test_%s" % f) for f in tests.keys()] - cmd = ["pytest", "-v"] + test_files + ["--junitxml=%s" % junitxmlpath] + cmd = ["pytest", "-vv"] + test_files + ["--junitxml=%s" % junitxmlpath] # print("cmd: ", " ".join(cmd)) if test_files == []: test_results["pytest_report"] = "*** No Tests ***" @@ -347,8 +347,12 @@ def gen_reflection_report(client_code_path, assessment_struct, student_no, confi raise MonitoredFileNotInProducedFilesException("The monitored file %s is not in the list of produced files. It needs to be added." % contents["monitor"]) subprocess.run(cmd.split()) - with open(contents["monitor"], "r") as f: - lines = f.read() + if os.path.exists(contents["monitor"]): + with open(contents["monitor"], "r") as f: + lines = f.read() + else: + lines = "*** File not produced ***" + # yes, this could potentially cause regexes to still be found else: proc = subprocess.Popen(cmd.split(), stdout = subprocess.PIPE) diff --git a/requirements.txt b/requirements.txt index f88b5ac..6d0d84b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -Jinja2==3.0.3 -misaka==2.1.1 -Pygments==2.10.0 -PyYAML==6.0 -pytest +Jinja2==3.0.3 +misaka==2.1.1 +Pygments==2.10.0 +PyYAML==6.0 +pytest diff --git a/smarker.conf b/smarker.conf index b700f85..e31970d 100644 --- a/smarker.conf +++ b/smarker.conf @@ -1,17 +1,17 @@ -[mysql] -host = 192.168.1.92 -port = 3306 -user = smarker -passwd = smarkerPassword - -[md] -show_full_docs = True -show_source = True -show_all_regex_occurrences = True -show_all_run_output = True - -[txt] -show_full_docs = True -show_source = True -show_all_regex_occurrences = True +[mysql] +host = 192.168.1.92 +port = 3306 +user = smarker +passwd = smarkerPassword + +[md] +show_full_docs = False +show_source = False +show_all_regex_occurrences = True +show_all_run_output = True + +[txt] +show_full_docs = False +show_source = False +show_all_regex_occurrences = True show_all_run_output = True \ No newline at end of file diff --git a/templates/html.jinja2 b/templates/html.jinja2 deleted file mode 100644 index e69de29..0000000 diff --git a/templates/md.jinja2 b/templates/md.jinja2 index f998f7c..e1d95ca 100644 --- a/templates/md.jinja2 +++ b/templates/md.jinja2 @@ -1,142 +1,166 @@ -{%- macro expand_function(function_name, function_contents, x = "Function") -%} - - `{{ function_name }}`: -{%- if function_contents["present"] %} - - **Arguments:** - - `{{ function_contents["arguments"] }}` - - {{ bool_to_checkbox(function_contents["minimum_arguments"] >= get_required_num_args(function_name)) }} Enough? - - **Documentation**: - - {{ len_documentation(function_contents["documentation"]["comments"], function_contents["documentation"]["doc"]) }} characters long -{%- if md_show_full_docs == "True" %} - - Comments: - {%- if function_contents["documentation"]["comments"] == "None" %} - - [ ] No comments present -{%- else %} -{{ code_block(function_contents["documentation"]["comments"])|indent(12, True) }} -{%- endif %} - - Docstring: -{%- if function_contents["documentation"]["doc"] == "None" %} - - [ ] No docstring present -{%- else %} -{{ code_block(function_contents["documentation"]["doc"])|indent(12, True) }} -{%- endif -%} -{%- endif %} - - **Source**: - - {{ get_source_numlines(function_contents["source_code"]) }} -{%- if md_show_source == "True" %} - - Code: -{{ code_block(function_contents["source_code"])|indent(12, True) }} -{%- endif %} -{%- else %} - - [ ] {{ x }} not present -{%- endif %} -{%- endmacro -%} - -{%- macro code_block(code) -%} -``` -{{ code }} -``` -{%- endmacro -%} - -# {{ name }} - Student ID: {{ student_no }} Automatic marking report -Report generated at {{ get_datetime() }} -## Class Tree: - -``` -{{ recurse_class_tree_text(class_tree) }} -``` - -## File Analysis - -{%- set flat_files = flatten_struct(files) %} -{% for filename, files_contents in flat_files.items() %} -### File `{{ filename }}`: -{%- if files_contents["present"] -%} -{%- if files_contents["has_exception"] %} -*** File cannot be run - has compile time exception *** -Please note that this file cannot be analysed or have tests preformed upon it- - this can lead to the whole test suite failing if another module imports this. - - Exception Type: `{{ files_contents["exception"]["type"] }}` - - Exception String: `{{ files_contents["exception"]["str"] }}` - - Full Traceback: -``` -{{ files_contents["exception"]["traceback"] }} -``` -{%- else %} - - #### Documentation: - {%- set len_docs = len_documentation(files_contents["documentation"]["comments"], files_contents["documentation"]["doc"]) %} - - {{ len_docs }} characters long -{%- if md_show_full_docs == "True" %} - - ##### Comments: -{%- if files_contents["documentation"]["comments"] == "None" %} - - [ ] No comments present -{%- else %} -{{ code_block(files_contents["documentation"]["comments"])|indent(8, True) }} -{%- endif %} - - ##### Docstring: -{%- if files_contents["documentation"]["doc"] == "None" %} - - [ ] No docstring present -{%- else %} -{{ code_block(files_contents["documentation"]["doc"])|indent(8, True) }} -{%- endif -%} -{%- endif %} -{%- if "classes" in files_contents.keys() %} - - #### Classes: -{%- set flat_classes = flatten_struct(files_contents["classes"]) -%} -{% for class_name, class_contents in flat_classes.items() %} - - ##### `{{ class_name}}`: -{%- if "methods" in class_contents.keys() %} - - ###### Methods: -{%- set flat_methods = flatten_struct(class_contents["methods"]) -%} -{%- for method_name, method_contents in flat_methods.items() %} -{{ expand_function(method_name, method_contents, "Method")|indent(16, True) }} -{%- endfor -%} -{%- endif -%} -{%- endfor -%} -{%- endif -%} -{% if "functions" in files_contents.keys() %} - - #### Functions: -{%- set flat_functions = flatten_struct(files_contents["functions"]) %} -{%- for function_name, function_contents in flat_functions.items() %} -{{ expand_function(function_name, function_contents)|indent(8, True) }} -{%- endfor -%} -{%- endif -%} -{% if "run" in files_contents.keys() %} - - #### Runtime Analysis: -{%- set flat_runtime = flatten_struct(files_contents["run"]) %} -{%- for cmd, runtime_contents in flat_runtime.items() %} - - ##### Command `{{ cmd }}`: - - **Monitor:** -{%- if "monitor" in runtime_contents.keys() %} - - {{ runtime_contents["monitor"] }} -{%- else %} - - stdout -{%- endif %} - - **Regexes:** -{%- for regex_, results in runtime_contents["regexes"].items() %} - - `{{regex_}}`: - - Found occurrences: {{ len_(results) }} -{%- if md_show_all_regex_occurrences == "True" and len_(results) > 0 %} - - Occurrences list: -{%- for result in results %} - - `{{ result }}` -{%- endfor -%} -{%- if txt_show_all_run_output == "True" %} - - Full runtime output: -{{ code_block(runtime_contents["full_output"])|indent(24, True) }} -{%- endif -%} -{%- endif -%} -{%- endfor -%} -{%- endfor -%} -{%- endif -%} -{%- endif -%} -{% else %} - - [ ] File not present -{% endif %} -{% endfor %} - -{% if out != "stdout" and format != "html" -%} -## Tests: -``` -{{ test_results["pytest_report"].replace("\n", "") }} -``` +{%- macro expand_function(function_name, function_contents, x = "Function") -%} + - `{{ function_name }}`: +{%- if function_contents["present"] %} + - **Arguments:** + - `{{ function_contents["arguments"] }}` + - {{ bool_to_checkbox(function_contents["minimum_arguments"] >= get_required_num_args(function_name)) }} Enough? + - **Documentation**: + - {{ len_documentation(function_contents["documentation"]["comments"], function_contents["documentation"]["doc"]) }} characters long +{%- if md_show_full_docs == "True" %} + - Comments: + {%- if function_contents["documentation"]["comments"] == "None" %} + - [ ] No comments present +{%- else %} +{{ code_block(function_contents["documentation"]["comments"])|indent(12, True) }} +{%- endif %} + - Docstring: +{%- if function_contents["documentation"]["doc"] == "None" %} + - [ ] No docstring present +{%- else %} +{{ code_block(function_contents["documentation"]["doc"])|indent(12, True) }} +{%- endif -%} +{%- endif %} + - **Source**: + - {{ get_source_numlines(function_contents["source_code"]) }} +{%- if md_show_source == "True" %} + - Code: +{{ code_block(function_contents["source_code"])|indent(12, True) }} +{%- endif %} +{%- else %} + - [ ] {{ x }} not present +{%- endif %} +{%- endmacro -%} + +{%- macro code_block(code) -%} +``` +{{ code }} +``` +{%- endmacro -%} + +# {{ name }} - Student ID: {{ student_no }} Automatic marking report +Report generated at {{ get_datetime() }} +## Class Tree: + +``` +{{ recurse_class_tree_text(class_tree) }} +``` + +## File Analysis + +{%- set flat_files = flatten_struct(files) %} +{% for filename, files_contents in flat_files.items() %} +### File `{{ filename }}`: +{%- if files_contents["present"] -%} +{%- if files_contents["has_exception"] %} +*** File cannot be run - has compile time exception *** +Please note that this file cannot be analysed or have tests preformed upon it- + this can lead to the whole test suite failing if another module imports this. + - Exception Type: `{{ files_contents["exception"]["type"] }}` + - Exception String: `{{ files_contents["exception"]["str"] }}` + - Full Traceback: +``` +{{ files_contents["exception"]["traceback"] }} +``` +{%- else %} + - #### Documentation: + {%- set len_docs = len_documentation(files_contents["documentation"]["comments"], files_contents["documentation"]["doc"]) %} + - {{ len_docs }} characters long +{%- if md_show_full_docs == "True" %} + - ##### Comments: +{%- if files_contents["documentation"]["comments"] == "None" %} + - [ ] No comments present +{%- else %} +{{ code_block(files_contents["documentation"]["comments"])|indent(8, True) }} +{%- endif %} + - ##### Docstring: +{%- if files_contents["documentation"]["doc"] == "None" %} + - [ ] No docstring present +{%- else %} +{{ code_block(files_contents["documentation"]["doc"])|indent(8, True) }} +{%- endif -%} +{%- endif %} +{%- if "classes" in files_contents.keys() %} + - #### Classes: +{%- set flat_classes = flatten_struct(files_contents["classes"]) -%} +{% for class_name, class_contents in flat_classes.items() %} + - ##### `{{ class_name}}`: +{%- if class_contents["present"] %} + - ###### Documentation: + {%- set len_docs = len_documentation(class_contents["documentation"]["comments"], class_contents["documentation"]["doc"]) %} + - {{ len_docs }} characters long +{%- if md_show_full_docs == "True" %} + - *Comments*: +{%- if class_contents["documentation"]["comments"] == "None" %} + - [ ] No comments present +{%- else %} +{{ code_block(class_contents["documentation"]["comments"])|indent(20, True) }} +{%- endif %} + - *Docstring*: +{%- if class_contents["documentation"]["doc"] == "None" %} + - [ ] No docstring present +{%- else %} +{{ code_block(class_contents["documentation"]["doc"])|indent(20, True) }} +{%- endif -%} +{%- endif %} +{%- if "methods" in class_contents.keys() %} + - ###### Methods: +{%- set flat_methods = flatten_struct(class_contents["methods"]) -%} +{%- for method_name, method_contents in flat_methods.items() %} +{{ expand_function(method_name, method_contents, "Method")|indent(16, True) }} +{%- endfor -%} +{%- endif -%} +{%- else %} + - [ ] Class not present +{%- endif -%} +{%- endfor -%} +{%- endif -%} +{% if "functions" in files_contents.keys() %} + - #### Functions: +{%- set flat_functions = flatten_struct(files_contents["functions"]) %} +{%- for function_name, function_contents in flat_functions.items() %} +{{ expand_function(function_name, function_contents)|indent(8, True) }} +{%- endfor -%} +{%- endif -%} +{% if "run" in files_contents.keys() %} + - #### Runtime Analysis: +{%- set flat_runtime = flatten_struct(files_contents["run"]) %} +{%- for cmd, runtime_contents in flat_runtime.items() %} + - ##### Command `{{ cmd }}`: + - **Monitor:** +{%- if "monitor" in runtime_contents.keys() %} + - {{ runtime_contents["monitor"] }} +{%- else %} + - stdout +{%- endif %} + - **Regexes:** +{%- for regex_, results in runtime_contents["regexes"].items() %} + - `{{regex_}}`: + - Found occurrences: {{ len_(results) }} +{%- if code_block(runtime_contents["full_output"]) == "*** File not produced ***" %} + - *** File was not produced- no occurrences *** +{%- endif -%} +{%- if md_show_all_regex_occurrences == "True" and len_(results) > 0 %} + - Occurrences list: +{%- for result in results %} + - `{{ result }}` +{%- endfor -%} +{%- if md_show_all_run_output == "True" %} + - Full runtime output: +{{ code_block(runtime_contents["full_output"])|indent(24, True) }} +{%- endif -%} +{%- endif -%} +{%- endfor -%} +{%- endfor -%} +{%- endif -%} +{%- endif -%} +{% else %} + - [ ] File not present +{% endif %} +{% endfor %} + +{% if out != "stdout" and format != "html" -%} +## Tests: +``` +{{ test_results["pytest_report"] }} +``` {%- endif -%} \ No newline at end of file diff --git a/templates/text.jinja2 b/templates/text.jinja2 deleted file mode 100644 index eca6ebd..0000000 Binary files a/templates/text.jinja2 and /dev/null differ diff --git a/templates/text.jinja2 b/templates/text.jinja2 new file mode 120000 index 0000000..aad87bd --- /dev/null +++ b/templates/text.jinja2 @@ -0,0 +1 @@ +txt.jinja2 \ No newline at end of file diff --git a/templates/txt.jinja2 b/templates/txt.jinja2 index 5ca0c62..0d31047 100644 --- a/templates/txt.jinja2 +++ b/templates/txt.jinja2 @@ -69,17 +69,17 @@ Report generated at {{ get_datetime() }} {%- if files_contents["documentation"]["comments"] == "None" %} *** No comments present *** {%- else %} - ``` - {{ files_contents["documentation"]["comments"]|indent(8, False) }} - ``` + ``` + {{ files_contents["documentation"]["comments"]|indent(16, False) }} + ``` {%- endif %} Docstring: {%- if files_contents["documentation"]["doc"] == "None" %} *** No docstring present *** {%- else %} - ``` - {{ files_contents["documentation"]["doc"]|indent(8, False) }} - ``` + ``` + {{ files_contents["documentation"]["doc"]|indent(16, False) }} + ``` {%- endif -%} {%- endif %} {%- if "classes" in files_contents.keys() %} @@ -87,6 +87,27 @@ Report generated at {{ get_datetime() }} {%- set flat_classes = flatten_struct(files_contents["classes"]) -%} {% for class_name, class_contents in flat_classes.items() %} {{ class_name + ":" }} + {%- if class_contents["present"] %} + Documentation: + {{ len_documentation(class_contents["documentation"]["comments"], class_contents["documentation"]["doc"]) }} characters long + {%- if txt_show_full_docs == "True" %} + Comments: + {%- if class_contents["documentation"]["comments"] == "None" %} + *** No comments present *** + {%- else %} + ``` + {{ class_contents["documentation"]["comments"]|indent(16, False) }} + ``` + {%- endif %} + Docstring: + {%- if class_contents["documentation"]["doc"] == "None" %} + *** No docstring present *** + {%- else %} + ``` + {{ class_contents["documentation"]["doc"]|indent(16, False) }} + ``` + {%- endif -%} + {%- endif %} {%- if "methods" in class_contents.keys() %} Methods: {%- set flat_methods = flatten_struct(class_contents["methods"]) -%} @@ -94,6 +115,9 @@ Report generated at {{ get_datetime() }} {{ expand_function(method_name, method_contents, "Method")|indent(20, False) }} {%- endfor -%} {%- endif -%} + {%- else %} + *** Class not present *** + {%- endif -%} {%- endfor -%} {%- endif -%} {% if "functions" in files_contents.keys() %} @@ -140,5 +164,5 @@ Report generated at {{ get_datetime() }} {% endfor %} {% if out != "stdout" -%} -{{ test_results["pytest_report"].replace("\n", "") }} +{{ test_results["pytest_report"] }} {%- endif -%} \ No newline at end of file -- cgit v1.2.3