summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2021-11-15 19:46:31 +0000
committerjwansek <eddie.atten.ea29@gmail.com>2021-11-15 19:46:31 +0000
commitd262c125550dfb952abeb1c953731f470c52decd (patch)
tree9b46fae13dbf589f8a7165ccb133794c97d049cf
parent4a0b15e2aba519d184f36f330a7b4af05bc2fd7d (diff)
downloadSmarker-d262c125550dfb952abeb1c953731f470c52decd.tar.gz
Smarker-d262c125550dfb952abeb1c953731f470c52decd.zip
made a start on analysis
-rw-r--r--.gitignore2
-rw-r--r--ExampleAssessments/CMP-4009B.yml6
-rw-r--r--ExampleAssessments/smol.yml21
-rw-r--r--mark.py46
-rw-r--r--reflect.py60
-rw-r--r--reportWriter.py36
6 files changed, 165 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index b6e4761..70f2228 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+*_report.md
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
diff --git a/ExampleAssessments/CMP-4009B.yml b/ExampleAssessments/CMP-4009B.yml
index 373f7f4..571c97f 100644
--- a/ExampleAssessments/CMP-4009B.yml
+++ b/ExampleAssessments/CMP-4009B.yml
@@ -17,8 +17,12 @@ files:
- tester.py:
functions:
- dateTester(2)
+ printIfExecuted: False
- turbine.py:
classes:
- Turbine:
attributes:
- - rating:int \ No newline at end of file
+ - rating:int
+ - aNonExistantModule.py:
+ functions:
+ - aNonExistantFunction(1) \ No newline at end of file
diff --git a/ExampleAssessments/smol.yml b/ExampleAssessments/smol.yml
new file mode 100644
index 0000000..d53e721
--- /dev/null
+++ b/ExampleAssessments/smol.yml
@@ -0,0 +1,21 @@
+files:
+ - pjtool.py:
+ classes:
+ - Date:
+ methods:
+ - __init__(4)
+ - __str__(1)
+ - __eq__(2)
+ - __lt__(2)
+ - numYears(2)
+ - numMonths(2)
+ - AnotherClass:
+ - floofleBerries(2)
+ tests:
+ - |
+ d1 = Date(2001, 8, 12)
+ d2 = Date(2001, 8, 12)
+ return d1 == d2
+ - aNonExistantModule.py:
+ functions:
+ - aNonExistantFunction(1) \ No newline at end of file
diff --git a/mark.py b/mark.py
index f1ffa59..8078b62 100644
--- a/mark.py
+++ b/mark.py
@@ -1,6 +1,8 @@
+import reportWriter
import argparse
import tempfile
import zipfile
+import reflect
import yaml
import os
@@ -10,11 +12,47 @@ def main(assessment_path, submission_path, student_no):
with open(assessment_path, "r") as f:
assessment_struct = yaml.safe_load(f)
- print(assessment_struct)
-
- for required_file in assessment_struct["files"]:
+ 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]
- print(required_file, required_file in os.listdir(submission_path))
+ 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"))
+
if __name__ == "__main__":
parser = argparse.ArgumentParser()
diff --git a/reflect.py b/reflect.py
index 2cfb2f3..1cb6241 100644
--- a/reflect.py
+++ b/reflect.py
@@ -15,17 +15,75 @@ class Reflect:
self.client_modules = [p for p in pkgutil.iter_modules() if 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
+ must be imported. WARNING: This will execute module code if it isn't
+ in a if __name__ == "__main__". Takes a module name (that the student made)
+ as the first argument.
+
+ Args:
+ module_name (str): The name of a student's module to import
+ """
for module in self.client_modules:
if module.name == module_name:
self.imported_modules[module_name] = importlib.import_module(module.name)
def get_module_doc(self, module_name):
+ """Gets the documentation provided for a module.
+
+ Args:
+ module_name (str): The student's module name to get documentation for
+
+ Returns:
+ str: Provided documentation
+ """
return inspect.getdoc(self.imported_modules[module_name])
+ def get_classes(self, module_name):
+ """Gets the classes in a given module. The module must be imported first.
+
+ Args:
+ module_name (str): The name of an imported module to get the name of.
+
+ Returns:
+ dict: Dictionary of classes. The name of the class is the index, followed by
+ a tuple containing the class object and the classes' documentation.
+ """
+ return {
+ i[0]: (i[1], inspect.getdoc(i[1]))
+ for i in inspect.getmembers(self.imported_modules[module_name])
+ if inspect.isclass(i[1])
+ }
+
+ def get_class_methods(self, module_name, class_name):
+ """Gets the user generated methods of a given class. The module must be imported first.
+
+ Args:
+ module_name (str): The name of the module in which the class is contained.
+ class_name (str): The name of the class.
+
+ 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.
+ """
+ return {
+ i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])[0])
+ for i in inspect.getmembers(
+ self.get_classes(module_name)[class_name][0],
+ predicate=inspect.isfunction
+ )
+ }
+
+ def get_functions(self, module_name):
+ return {
+ i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])[0])
+ for i in inspect.getmembers(self.imported_modules[module_name])
+ if inspect.isfunction(i[1])
+ }
if __name__ == "__main__":
user_code_path = "/media/veracrypt1/Nextcloud/UniStuff/3.0 - CMP 3rd Year Project/ExampleSubmissions/Submission_A"
reflect = Reflect(user_code_path)
reflect.import_module("pjtool")
- print(reflect.get_module_doc("pjtool"))
+ print(reflect.get_class_methods("pjtool", "Date"))
diff --git a/reportWriter.py b/reportWriter.py
index e69de29..ebbfecf 100644
--- a/reportWriter.py
+++ b/reportWriter.py
@@ -0,0 +1,36 @@
+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