diff options
author | jwansek <eddie.atten.ea29@gmail.com> | 2022-01-10 17:08:42 +0000 |
---|---|---|
committer | jwansek <eddie.atten.ea29@gmail.com> | 2022-01-10 17:08:42 +0000 |
commit | 37236fdc957f5900f6a4bbffbff6ccc07d412c44 (patch) | |
tree | 8f26b1f8a03ffd135fd8814ffc8daa9daf0e41e3 | |
parent | d262c125550dfb952abeb1c953731f470c52decd (diff) | |
download | Smarker-37236fdc957f5900f6a4bbffbff6ccc07d412c44.tar.gz Smarker-37236fdc957f5900f6a4bbffbff6ccc07d412c44.zip |
added to report maker and removed the first markdown renderer
-rw-r--r-- | ExampleAssessments/example.yml | 16 | ||||
-rw-r--r-- | ExampleAssessments/smol.yml | 1 | ||||
-rw-r--r-- | ExampleSubmission/example.py | 51 | ||||
-rw-r--r-- | examplerun.bat | 3 | ||||
-rw-r--r-- | mark.py | 64 | ||||
-rw-r--r-- | reflect.py | 79 | ||||
-rw-r--r-- | reportWriter.py | 36 | ||||
-rw-r--r-- | smarker.conf | 7 |
8 files changed, 169 insertions, 88 deletions
diff --git a/ExampleAssessments/example.yml b/ExampleAssessments/example.yml new file mode 100644 index 0000000..2ca03d3 --- /dev/null +++ b/ExampleAssessments/example.yml @@ -0,0 +1,16 @@ +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) + - aFileThatIsntThere.py: + functions: + - hello_world(2)
\ No newline at end of file diff --git a/ExampleAssessments/smol.yml b/ExampleAssessments/smol.yml index d53e721..22937d7 100644 --- a/ExampleAssessments/smol.yml +++ b/ExampleAssessments/smol.yml @@ -10,6 +10,7 @@ files: - numYears(2) - numMonths(2) - AnotherClass: + methods: - floofleBerries(2) tests: - | diff --git a/ExampleSubmission/example.py b/ExampleSubmission/example.py new file mode 100644 index 0000000..b64c98d --- /dev/null +++ b/ExampleSubmission/example.py @@ -0,0 +1,51 @@ +import tkinter as tk + +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, arg_1 = "hello", arg_2 = "world"): + """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 + +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 + +if __name__ == "__main__": + app = Application() + app.mainloop()
\ No newline at end of file diff --git a/examplerun.bat b/examplerun.bat new file mode 100644 index 0000000..5252460 --- /dev/null +++ b/examplerun.bat @@ -0,0 +1,3 @@ +zip -r 100301654.zip .\ExampleSubmission\ +python .\mark.py -s 100301654.zip -a .\ExampleAssessments\example.yml -f json +rm 100301654.zip
\ No newline at end of file @@ -1,71 +1,42 @@ -import reportWriter import argparse import tempfile import zipfile import reflect import yaml +import json import os -def main(assessment_path, submission_path, student_no): - print(student_no) +def main(assessment_path, submission_path, student_no, output_format): + # print(student_no) with open(assessment_path, "r") as f: assessment_struct = yaml.safe_load(f) - reflection = reflect.Reflect(submission_path) - present_module_names = [i.name for i in reflection.client_modules] - writer = reportWriter.MarkDownReportWriter(student_no) - - for i, required_file in enumerate(assessment_struct["files"], 0): - required_file = list(required_file.keys())[0] - module_name = os.path.splitext(required_file)[0] - - if module_name not in present_module_names: - writer.append_module(module_name, False) - continue - - reflection.import_module(module_name) - writer.append_module(module_name, True, reflection.get_module_doc(module_name)) - - this_files_features = assessment_struct["files"][i][required_file] - if "classes" in this_files_features.keys(): - - present_classes = reflection.get_classes(module_name) - for j, class_name in enumerate(this_files_features["classes"], 0): - class_name = list(class_name.keys())[0] - - if class_name not in present_classes.keys(): - writer.append_class(class_name, False) - continue - - writer.append_class(class_name, True, present_classes[class_name][1]) - - present_methods = reflection.get_class_methods(module_name, class_name) - print(present_methods) - for required_method in this_files_features["classes"][j][class_name]["methods"]: - print(required_method) - - # print(submission_path) - # reflection = reflect.Reflect(submission_path) - # # reflection.import_module("pjtool") - # # print(reflection.get_classes("pjtool")) - # # print(reflection.get_class_methods("pjtool", "Date")["__eq__"]) - # reflection.import_module("tester") - # print(reflection.get_functions("tester")) - + output = reflect.gen_reflection_report(submission_path, assessment_struct) + if output_format == "yaml": + print(yaml.dump(output)) + elif output_format == "json": + print(json.dumps(output, indent = 4)) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "-a", "--assessment", help = "Path to an assessment .yml file", - type = str, + type = os.path.abspath, required = True ) parser.add_argument( "-s", "--submission", help = "Path to a zip of a student's code", + type = os.path.abspath, + required = True + ) + parser.add_argument( + "-f", "--format", + help = "Output format type", type = str, + choices = ["yaml", "json"], required = True ) args = vars(parser.parse_args()) @@ -85,6 +56,7 @@ if __name__ == "__main__": main( args["assessment"], submission_files, - os.path.splitext(os.path.split(args["submission"])[-1])[0] + os.path.splitext(os.path.split(args["submission"])[-1])[0], + args["format"] )
\ No newline at end of file @@ -4,6 +4,7 @@ import inspect import pkgutil import sys import os +import re @dataclass class Reflect: @@ -11,8 +12,9 @@ class Reflect: imported_modules = {} def __post_init__(self): + self.client_code_path = os.path.normpath(self.client_code_path) sys.path.insert(1, self.client_code_path) - self.client_modules = [p for p in pkgutil.iter_modules() if str(p[0])[12:-2] == self.client_code_path] + self.client_modules = [p for p in pkgutil.iter_modules() if os.path.normpath(str(p[0])[12:-2]) == self.client_code_path] def import_module(self, module_name): """Imports a module. Before reflection can be conducted, a module @@ -63,11 +65,10 @@ class Reflect: Returns: dict: A dictionary of the methods. The index is the function name, followed by a tuple - containing the function object, the documentation, and a list of the named arguments. - WARNING: Does not deal with *args and **kwargs and stuff. + containing the function object, the documentation, and the args as a dict. """ return { - i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])[0]) + i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])._asdict()) for i in inspect.getmembers( self.get_classes(module_name)[class_name][0], predicate=inspect.isfunction @@ -76,13 +77,79 @@ class Reflect: def get_functions(self, module_name): return { - i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])[0]) + i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])._asdict()) for i in inspect.getmembers(self.imported_modules[module_name]) if inspect.isfunction(i[1]) } +def gen_reflection_report(client_code_path, assessment_struct): + reflection = Reflect(client_code_path) + present_module_names = [i.name for i in reflection.client_modules] + out = assessment_struct + + for i, required_file in enumerate(assessment_struct["files"], 0): + required_file = list(required_file.keys())[0] + module_name = os.path.splitext(required_file)[0] + + if module_name in present_module_names: + out["files"][i][required_file]["present"] = True + else: + out["files"][i][required_file]["present"] = False + continue + + reflection.import_module(module_name) + required_files_features = assessment_struct["files"][i][required_file] + if "classes" in required_files_features.keys(): + + present_classes = reflection.get_classes(module_name) + for j, class_name in enumerate(required_files_features["classes"], 0): + class_name = list(class_name.keys())[0] + + if class_name in present_classes.keys(): + out["files"][i][required_file]["classes"][j][class_name]["present"] = True + else: + out["files"][i][required_file]["classes"][j][class_name]["present"] = True + continue + + out["files"][i][required_file]["classes"][j][class_name]["documentation"] = present_classes[class_name][1] + present_methods = reflection.get_class_methods(module_name, class_name) + + for k, required_method in enumerate(assessment_struct["files"][i][required_file]["classes"][j][class_name]["methods"], 0): + out["files"][i][required_file]["classes"][j][class_name]["methods"][k] = {required_method: {}} + + method_name = re.sub(r"\(\d+\)", "", required_method) + if method_name in present_methods.keys(): + out["files"][i][required_file]["classes"][j][class_name]["methods"][k][required_method]["present"] = True + else: + out["files"][i][required_file]["classes"][j][class_name]["methods"][k][required_method]["present"] = False + continue + + out["files"][i][required_file]["classes"][j][class_name]["methods"][k][required_method]["arguments"] = present_methods[method_name][-1] + out["files"][i][required_file]["classes"][j][class_name]["methods"][k][required_method]["documentation"] = present_methods[method_name][-2] + + if "functions" in required_files_features.keys(): + present_functions = reflection.get_functions(module_name) + # print(present_functions) + for j, required_function in enumerate(assessment_struct["files"][i][required_file]["functions"], 0): + function_name = re.sub(r"\(\d+\)", "", required_function) + out["files"][i][required_file]["functions"][j] = {required_function: {}} + + if function_name in present_functions.keys(): + out["files"][i][required_file]["functions"][j][required_function]["present"] = True + else: + out["files"][i][required_file]["functions"][j][required_function]["present"] = False + continue + + out["files"][i][required_file]["functions"][j][required_function]["documentation"] = present_functions[function_name][-2] + out["files"][i][required_file]["functions"][j][required_function]["arguments"] = present_functions[function_name][-1] + + return out + + + + if __name__ == "__main__": - user_code_path = "/media/veracrypt1/Nextcloud/UniStuff/3.0 - CMP 3rd Year Project/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") diff --git a/reportWriter.py b/reportWriter.py deleted file mode 100644 index ebbfecf..0000000 --- a/reportWriter.py +++ /dev/null @@ -1,36 +0,0 @@ -from dataclasses import dataclass -import datetime - -@dataclass -class MarkDownReportWriter: - student_no:str - - def __post_init__(self): - self.__push_line(""" -# %s Submission Report - -Report automatically generated at %s - -## Files\n\n""" % (self.student_no, datetime.datetime.now())) - - def __push_line(self, line): - with open("%s_report.md" % self.student_no, "a") as f: - f.write(line) - - def append_module(self, module_name, found = True, docs = None): - self.__push_line("### File: `%s.py`\n\n" % module_name) - if found: - self.__push_line(" - [x] Present\n") - if len(docs) > 2: - self.__push_line(" - [x] Documented (%d characters)\n\n" % (len(docs))) - else: - self.__push_line(" - [ ] Present\n\n") - - def append_class(self, class_name, found = True, docs = None): - self.__push_line("#### Class: `%s`\n\n" % class_name) - if found: - self.__push_line(" - [x] Present\n") - if len(docs) > 2: - self.__push_line(" - [x] Documented (%d characters)\n\n" % (len(docs))) - else: - self.__push_line(" - [ ] Present\n\n")
\ No newline at end of file diff --git a/smarker.conf b/smarker.conf new file mode 100644 index 0000000..bcb7922 --- /dev/null +++ b/smarker.conf @@ -0,0 +1,7 @@ +[mysql] +host = 192.168.1.92 +user = smarker +passwd = smarkerPassword + +[report] +show_full_docs = True
\ No newline at end of file |