diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | ExampleAssessments/CMP-4009B.yml | 40 | ||||
-rw-r--r-- | ExampleAssessments/example.yml | 75 | ||||
-rw-r--r-- | ExampleSubmission/animals.py | 44 | ||||
-rw-r--r-- | ExampleSubmission/example.py | 146 | ||||
-rw-r--r-- | ExampleSubmission/test_dont_test_me.py | 12 | ||||
-rw-r--r-- | examplerun.bat | 4 | ||||
-rw-r--r-- | jinja_helpers.py | 30 | ||||
-rw-r--r-- | mark.py | 14 | ||||
-rw-r--r-- | reflect.py | 29 | ||||
-rw-r--r-- | smarker.conf | 18 | ||||
l--------- | templates/text.jinja2 | 1 | ||||
-rw-r--r-- | templates/txt.jinja2 | 29 |
13 files changed, 280 insertions, 165 deletions
@@ -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 @@ -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", @@ -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 |