summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2022-02-16 15:45:37 +0000
committerjwansek <eddie.atten.ea29@gmail.com>2022-02-16 15:45:37 +0000
commite5203f74000dc083d3e4024725a0e50656bf776b (patch)
tree4666d361615e5ed5503470c6e0a8f6e4e352dc8b
parent376c8bef903657e020e482037b94a7b63514f640 (diff)
downloadSmarker-e5203f74000dc083d3e4024725a0e50656bf776b.tar.gz
Smarker-e5203f74000dc083d3e4024725a0e50656bf776b.zip
fixed bug when client classes weren't present
-rw-r--r--ExampleAssessments/example.yml114
-rw-r--r--ExampleSubmission/animals.py55
-rw-r--r--ExampleSubmission/example.py146
-rw-r--r--ExampleSubmission/test_dont_test_me.py12
-rw-r--r--examplerun.bat4
-rw-r--r--examplerun.sh3
-rw-r--r--jinja_helpers.py1
-rw-r--r--misc_classes.py168
-rw-r--r--pytest_template.jinja214
-rw-r--r--reflect.py10
-rw-r--r--requirements.txt10
-rw-r--r--smarker.conf32
-rw-r--r--templates/html.jinja20
-rw-r--r--templates/md.jinja2306
l---------[-rw-r--r--]templates/text.jinja2bin28 -> 10 bytes
-rw-r--r--templates/txt.jinja238
16 files changed, 487 insertions, 426 deletions
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
--- a/templates/html.jinja2
+++ /dev/null
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
index eca6ebd..aad87bd 100644..120000
--- a/templates/text.jinja2
+++ b/templates/text.jinja2
Binary files differ
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