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 | 
