summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2022-01-10 17:08:42 +0000
committerjwansek <eddie.atten.ea29@gmail.com>2022-01-10 17:08:42 +0000
commit37236fdc957f5900f6a4bbffbff6ccc07d412c44 (patch)
tree8f26b1f8a03ffd135fd8814ffc8daa9daf0e41e3
parentd262c125550dfb952abeb1c953731f470c52decd (diff)
downloadSmarker-37236fdc957f5900f6a4bbffbff6ccc07d412c44.tar.gz
Smarker-37236fdc957f5900f6a4bbffbff6ccc07d412c44.zip
added to report maker and removed the first markdown renderer
-rw-r--r--ExampleAssessments/example.yml16
-rw-r--r--ExampleAssessments/smol.yml1
-rw-r--r--ExampleSubmission/example.py51
-rw-r--r--examplerun.bat3
-rw-r--r--mark.py64
-rw-r--r--reflect.py79
-rw-r--r--reportWriter.py36
-rw-r--r--smarker.conf7
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
diff --git a/mark.py b/mark.py
index 8078b62..93909a7 100644
--- a/mark.py
+++ b/mark.py
@@ -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
diff --git a/reflect.py b/reflect.py
index 1cb6241..b8f83c4 100644
--- a/reflect.py
+++ b/reflect.py
@@ -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