From 7e055c6eaf4291539c77932b29b8db0cc42c5d8c Mon Sep 17 00:00:00 2001 From: jwansek Date: Thu, 20 Jan 2022 18:51:27 +0000 Subject: started work on templating --- .gitignore | 3 +- ExampleAssessments/CMP-4009B.yml | 40 ++++++++- ExampleAssessments/example.yml | 75 ++++++++--------- ExampleSubmission/animals.py | 44 +++++----- ExampleSubmission/example.py | 146 ++++++++++++++++----------------- ExampleSubmission/test_dont_test_me.py | 12 +-- examplerun.bat | 4 +- jinja_helpers.py | 30 +++++++ mark.py | 14 +++- reflect.py | 29 +++++-- smarker.conf | 18 ++-- templates/text.jinja2 | 1 + templates/txt.jinja2 | 29 +++++++ 13 files changed, 280 insertions(+), 165 deletions(-) create mode 100644 jinja_helpers.py create mode 120000 templates/text.jinja2 create mode 100644 templates/txt.jinja2 diff --git a/.gitignore b/.gitignore index 70f2228..b8bb272 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -*_report.md +*_report.* +*.zip # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/ExampleAssessments/CMP-4009B.yml b/ExampleAssessments/CMP-4009B.yml index fbadc90..c01979b 100644 --- a/ExampleAssessments/CMP-4009B.yml +++ b/ExampleAssessments/CMP-4009B.yml @@ -1,4 +1,4 @@ -assessment_name: CMP-4009B +name: CMP-4009B files: - pjtool.py: classes: @@ -19,8 +19,42 @@ files: d1 = pjtool.Date(2001, 8, 12) d2 = pjtool.Date(1999, 4, 5) assert d1 != d2 + - | + d1 = pjtool.Date(2001, 8, 12) + d2 = pjtool.Date(1999, 4, 5) + assert d1 > d2 + - | + import random + d1 = pjtool.Date(random.randint(2000, 2050), 8, 12) + d2 = pjtool.Date(random.randint(1900, 2050), 4, 5) + assert d1.numYears(d2) == abs(d1.year - d2.year) + - | + d1 = pjtool.Date(2020, 5, 1) + d2 = pjtool.Date(2020, 6, 1) + assert d1.numMonths(d2) == 0 + - | + d1 = pjtool.Date(2020, 5, 1) + d2 = pjtool.Date(2020, 8, 1) + assert d1.numMonths(d2) == 2 - tester.py: functions: - dateTester(2) -dependencies: - - matplotlib \ No newline at end of file + - turbine.py: + classes: + - Turbine: + methods: + - __init__(5) + - __str__(1) + - numYearsInst(2) + - serviceDue(2) + - serviceAt(2) + - powerAt(2) + - AdvTurbine: + methods: + - __init__(5) + - __str__(1) + - numYearsInst(2) + - serviceDue(2) + - serviceAt(2) + - powerAt(2) + diff --git a/ExampleAssessments/example.yml b/ExampleAssessments/example.yml index 17ab9b0..46cab56 100644 --- a/ExampleAssessments/example.yml +++ b/ExampleAssessments/example.yml @@ -1,38 +1,39 @@ -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 - - aFileThatIsntThere.py: - functions: - - hello_world(2) - - animals.py: - classes: - - Dog: - - Cat: - - Kitten: - tests: - - | - nibbles = animals.Kitten() - assert nibbles.speak() == "nyaa~~" - - | - milton = animals.Dog() +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 + - 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*" \ No newline at end of file diff --git a/ExampleSubmission/animals.py b/ExampleSubmission/animals.py index 10c1cb4..a8c76d8 100644 --- a/ExampleSubmission/animals.py +++ b/ExampleSubmission/animals.py @@ -1,22 +1,22 @@ -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)" +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)" diff --git a/ExampleSubmission/example.py b/ExampleSubmission/example.py index cc61122..af0331c 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 - -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! " * 3 - -def an_undocumented_function(): - return 3.14156 - -# kwonlyargs demo -def greet(*names, greeting="Hello"): - for name in names: - print(greeting, name) - -if __name__ == "__main__": - app = Application() +# Eden Attenborough +# 12-01-21 + +import tkinter as tk +from dataclasses import dataclass + +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! " * 3 + +def an_undocumented_function(): + return 3.14156 + +# kwonlyargs demo +def greet(*names, greeting="Hello"): + for name in names: + print(greeting, name) + +if __name__ == "__main__": + app = Application() app.mainloop() \ 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 8ddbc7d..6ca9a04 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 yaml +zip -r 100301654.zip .\ExampleSubmission\ +python .\mark.py -s 100301654.zip -a .\ExampleAssessments\example.yml -f yaml rm 100301654.zip \ No newline at end of file diff --git a/jinja_helpers.py b/jinja_helpers.py new file mode 100644 index 0000000..af91786 --- /dev/null +++ b/jinja_helpers.py @@ -0,0 +1,30 @@ +"""Functions in this module will be avaliable to call in jinja templates""" +import yaml + +def recurse_class_tree_text(tree, indent = 4): + return yaml.dump(tree, indent = indent).replace(": {}", "") + +def flatten_struct(struct): + out = {} + for s in struct: + key = list(s.keys())[0] + out[key] = s[key] + return out + +def _get_helpers(): + import reflect + import os + + r = reflect.Reflect(os.getcwd()) + r.import_module("jinja_helpers") + return {k: v[0] for k, v in r.get_functions("jinja_helpers").items()} + +if __name__ == "__main__": + import json + with open("100301654_report.json", "r") as f: + init_struct = json.load(f)["files"] + + print(flatten_struct(flatten_struct(init_struct)["example.py"]["functions"])) + + + \ No newline at end of file diff --git a/mark.py b/mark.py index 95bba55..755ed6f 100644 --- a/mark.py +++ b/mark.py @@ -1,8 +1,10 @@ +import jinja_helpers import configparser import argparse import tempfile import zipfile import reflect +import jinja2 import yaml import json import os @@ -25,12 +27,18 @@ def main(**kwargs): with open(kwargs["assessment"], "r") as f: assessment_struct = yaml.safe_load(f) - output = reflect.gen_reflection_report(submission_files, assessment_struct, kwargs) + output = reflect.gen_reflection_report(submission_files, assessment_struct, student_no, kwargs) output_file = kwargs["out"] + if kwargs["format"] == "yaml": strout = yaml.dump(output) elif kwargs["format"] == "json": strout = json.dumps(output, indent = 4) + else: + with open(os.path.join("templates", "%s.jinja2" % kwargs["format"]), "r") as f: + jinja_template = jinja2.Template(f.read()) + + strout = jinja_template.render(**output, **jinja_helpers._get_helpers()) if output_file == "stdout": print(strout) @@ -66,8 +74,8 @@ if __name__ == "__main__": "-f", "--format", help = "Output format type", type = str, - choices = ["yaml", "json"], - required = True + choices = ["yaml", "json"] + [os.path.splitext(f)[0] for f in os.listdir("templates")], + default = "txt" ) parser.add_argument( "-o", "--out", diff --git a/reflect.py b/reflect.py index a6532da..8d9cb8a 100644 --- a/reflect.py +++ b/reflect.py @@ -34,7 +34,12 @@ class Reflect: """ for module in self.client_modules: if module.name == module_name: - self.imported_modules[module_name] = importlib.import_module(module.name) + try: + self.imported_modules[module_name] = importlib.import_module(module.name) + except ModuleNotFoundError as e: + print("Missing library dependency for client module:") + print(e) + exit() def get_module_doc(self, module_name): """Gets the documentation provided for a module. @@ -210,11 +215,12 @@ class Reflect: return test_results -def gen_reflection_report(client_code_path, assessment_struct, configuration): +def gen_reflection_report(client_code_path, assessment_struct, student_no, configuration): # print(configuration) reflection = Reflect(client_code_path) present_module_names = [i.name for i in reflection.client_modules] out = assessment_struct + out["student_no"] = student_no tests_to_run = {} for i, required_file in enumerate(assessment_struct["files"], 0): @@ -301,11 +307,16 @@ def gen_reflection_report(client_code_path, assessment_struct, configuration): return out if __name__ == "__main__": - user_code_path = "D:\\Edencloud\\UniStuff\\3.0 - CMP 3rd Year Project\\Smarker\\../ExampleSubmissions/Submission_A" + # user_code_path = "D:\\Edencloud\\UniStuff\\3.0 - CMP 3rd Year Project\\Smarker\\../ExampleSubmissions/Submission_A" - reflect = Reflect(user_code_path) - reflect.import_module("pjtool") - # for c, v in reflect.get_classes(("pjtool")).items(): - # print(c, v) - for k, v in reflect.get_functions("pjtool").items(): - print(k, v) \ No newline at end of file + # reflect = Reflect(user_code_path) + # reflect.import_module("pjtool") + # # for c, v in reflect.get_classes(("pjtool")).items(): + # # print(c, v) + # for k, v in reflect.get_functions("pjtool").items(): + # print(k, v) + + reflect = Reflect(os.getcwd()) + print(reflect.client_modules) + reflect.import_module("jinja_helpers") + print({k: v[0] for k, v in reflect.get_functions("jinja_helpers").items()}) \ No newline at end of file diff --git a/smarker.conf b/smarker.conf index 598ffa5..180b8cf 100644 --- a/smarker.conf +++ b/smarker.conf @@ -1,10 +1,10 @@ -[mysql] -host = 192.168.1.92 -port = 3306 -user = smarker -passwd = smarkerPassword - -[.md] -show_full_docs = True -show_source = True +[mysql] +host = 192.168.1.92 +port = 3306 +user = smarker +passwd = smarkerPassword + +[.md] +show_full_docs = True +show_source = True show_numlines = True \ No newline at end of file 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 new file mode 100644 index 0000000..1daf52a --- /dev/null +++ b/templates/txt.jinja2 @@ -0,0 +1,29 @@ +=== {{ name }} - Student ID: {{ student_no }} Automatic marking report === + +== Class Tree: == + +{{ recurse_class_tree_text(class_tree) }} + +== File Analysis == +{%- set flat_files = flatten_struct(files) %} +{% for filename, files_contents in flat_files.items() %} + = {{ filename + " =" -}} + {%- if files_contents["present"] -%} + {% 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 }} + {%- 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() %} + {{ function_name }} + {%- endfor -%} + {%- endif -%} + {% else %} + *** File not present *** + {% endif %} +{% endfor %} \ No newline at end of file -- cgit v1.2.3