summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2022-02-09 15:43:06 +0000
committerjwansek <eddie.atten.ea29@gmail.com>2022-02-09 15:43:06 +0000
commit8966f20ccb9897282d2ac7c174b87934fc38fc15 (patch)
tree4934257c0a857c94907a72aae0e0de05867a8fb1
parentff07842dc629e07f081ae578e50c42e4a54bc810 (diff)
downloadSmarker-8966f20ccb9897282d2ac7c174b87934fc38fc15.tar.gz
Smarker-8966f20ccb9897282d2ac7c174b87934fc38fc15.zip
started working on monitoring execution environments
-rw-r--r--ExampleAssessments/example.yml95
-rw-r--r--ExampleSubmission/animals.py50
-rw-r--r--ExampleSubmission/example.py148
-rw-r--r--ExampleSubmission/test_dont_test_me.py12
-rw-r--r--examplerun.bat4
-rw-r--r--mark.py51
-rw-r--r--misc_classes.py84
-rw-r--r--reflect.py46
-rw-r--r--smarker.conf24
-rw-r--r--[l---------]templates/text.jinja2bin28 -> 28 bytes
10 files changed, 303 insertions, 211 deletions
diff --git a/ExampleAssessments/example.yml b/ExampleAssessments/example.yml
index be1609f..b6e7b12 100644
--- a/ExampleAssessments/example.yml
+++ b/ExampleAssessments/example.yml
@@ -1,43 +1,54 @@
-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*"
-dependencies:
- files:
- - ../wsData.txt
+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
+ run:
+ - python example.py 1:
+ regexes:
+ - hello world\!
+ - 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*"
+ run:
+ - python animals.py:
+ monitor: animals.txt
+ regexes:
+ - TURRÓN
+produced_files:
+ - animals.txt
+dependencies:
+ files:
+ - ../wsData.txt
- ../IncludeDirectory \ No newline at end of file
diff --git a/ExampleSubmission/animals.py b/ExampleSubmission/animals.py
index d32d6ff..924c6fb 100644
--- a/ExampleSubmission/animals.py
+++ b/ExampleSubmission/animals.py
@@ -1,24 +1,26 @@
-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)"
-
-print() \ No newline at end of file
+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)"
+
+kitten = Kitten()
+with open("animals.txt", "w") as f:
+ f.write(kitten.speak()) \ No newline at end of file
diff --git a/ExampleSubmission/example.py b/ExampleSubmission/example.py
index af0331c..9c1d06a 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()
- app.mainloop() \ No newline at end of file
+# Eden Attenborough
+# 12-01-21
+
+import tkinter as tk
+from dataclasses import dataclass
+import sys
+
+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! " * times
+
+def an_undocumented_function():
+ return 3.14156
+
+# kwonlyargs demo
+def greet(*names, greeting="Hello"):
+ for name in names:
+ print(greeting, name)
+
+if __name__ == "__main__":
+ print(hello_world(int(sys.argv[1]))) \ No newline at end of file
diff --git a/ExampleSubmission/test_dont_test_me.py b/ExampleSubmission/test_dont_test_me.py
index 808879c..511c713 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 3364a63..8ddbc7d 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 md -o auto
+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/mark.py b/mark.py
index 2fe28c5..bb1ad62 100644
--- a/mark.py
+++ b/mark.py
@@ -1,6 +1,7 @@
from dataclasses import dataclass
import jinja_helpers
import configparser
+import misc_classes
import argparse
import tempfile
import zipfile
@@ -11,59 +12,15 @@ import yaml
import json
import os
-@dataclass
-class FileDependencies:
- assessment_struct:dict
-
- def __enter__(self):
- try:
- for file_dep in self.assessment_struct["dependencies"]["files"]:
- if os.path.isfile(file_dep):
- shutil.copy(file_dep, os.path.split(file_dep)[-1])
- else:
- shutil.copytree(file_dep, os.path.split(file_dep)[-1])
- # print("%s --> %s" % (file_dep, os.path.join(os.getcwd(), os.path.split(file_dep)[-1])))
- except KeyError:
- pass
-
- def __exit__(self, type, value, traceback):
- stuff_to_remove = []
- try:
- stuff_to_remove += [os.path.split(f)[-1] for f in self.assessment_struct["dependencies"]["files"]]
- except KeyError:
- pass
- try:
- stuff_to_remove += self.assessment_struct["produced_files"]
- except KeyError:
- pass
-
- for file_dep in stuff_to_remove:
- if os.path.exists(file_dep):
- if os.path.isfile(file_dep):
- os.remove(file_dep)
- else:
- shutil.rmtree(file_dep)
-
def main(**kwargs):
student_no = os.path.splitext(os.path.split(args["submission"])[-1])[0]
- with tempfile.TemporaryDirectory() as tempdir:
- with zipfile.ZipFile(args["submission"]) as z:
- z.extractall(tempdir)
-
- # some zipping applications make a folder inside the zip with the files in that folder.
- # try to deal with this here.
- submission_files = tempdir
- if os.path.isdir(
- os.path.join(submission_files, os.listdir(submission_files)[0])
- ) and len(os.listdir(submission_files)) == 1:
- submission_files = os.path.join(submission_files, os.listdir(submission_files)[0])
-
+ with misc_classes.ExtractZipToTempDir(args["submission"]) as submission_files:
with open(kwargs["assessment"], "r") as f:
assessment_struct = yaml.safe_load(f)
- with FileDependencies(assessment_struct):
- output = reflect.gen_reflection_report(submission_files, assessment_struct, student_no, kwargs)
+ with misc_classes.FileDependencies(assessment_struct):
+ output = reflect.gen_reflection_report(submission_files, assessment_struct, student_no, kwargs, args["submission"])
output_file = kwargs["out"]
if kwargs["format"] == "yaml":
diff --git a/misc_classes.py b/misc_classes.py
new file mode 100644
index 0000000..6ac5897
--- /dev/null
+++ b/misc_classes.py
@@ -0,0 +1,84 @@
+from dataclasses import dataclass
+import tempfile
+import zipfile
+import shutil
+import os
+
+@dataclass
+class ExtractZipToTempDir(tempfile.TemporaryDirectory):
+ zip_file:str
+
+ def __post_init__(self):
+ super().__init__()
+
+ def __enter__(self):
+ return self.extract()
+
+ def __exit__(self, exc, value, tb):
+ self.cleanup()
+
+ def extract(self):
+ with zipfile.ZipFile(self.zip_file) as z:
+ z.extractall(self.name)
+
+ # some zipping applications make a folder inside the zip with the files in that folder.
+ # try to deal with this here.
+ submission_files = self.name
+ if os.path.isdir(
+ os.path.join(submission_files, os.listdir(submission_files)[0])
+ ) and len(os.listdir(submission_files)) == 1:
+ submission_files = os.path.join(submission_files, os.listdir(submission_files)[0])
+
+ return submission_files
+
+@dataclass
+class FileDependencies:
+ assessment_struct:dict
+ to_:str=str(os.getcwd())
+
+ def __enter__(self):
+ self.get_deps()
+
+ def __exit__(self, type, value, traceback):
+ self.rm_deps()
+
+ def get_deps(self):
+ try:
+ for file_dep in self.assessment_struct["dependencies"]["files"]:
+ if os.path.isfile(file_dep):
+ shutil.copy(file_dep, os.path.join(self.to_, os.path.split(file_dep)[-1]))
+ else:
+ shutil.copytree(file_dep, os.path.join(self.to_, os.path.split(file_dep)[-1]))
+ except KeyError:
+ pass
+
+ def rm_deps(self):
+ stuff_to_remove = []
+ try:
+ stuff_to_remove += [os.path.split(f)[-1] for f in self.assessment_struct["dependencies"]["files"]]
+ except KeyError:
+ pass
+ try:
+ stuff_to_remove += self.assessment_struct["produced_files"]
+ except KeyError:
+ pass
+
+ for file_dep in stuff_to_remove:
+ file_dep = os.path.join(self.to_, file_dep)
+ # print("rm: ", file_dep)
+ if os.path.exists(file_dep):
+ if os.path.isfile(file_dep):
+ os.remove(file_dep)
+ else:
+ shutil.rmtree(file_dep)
+
+@dataclass
+class ChangeDirectory:
+ target:str
+ cwd:str=str(os.getcwd())
+
+ def __enter__(self):
+ os.chdir(self.target)
+
+ def __exit__(self, type, value, traceback):
+ os.chdir(self.cwd)
diff --git a/reflect.py b/reflect.py
index 46c69da..531c41b 100644
--- a/reflect.py
+++ b/reflect.py
@@ -2,6 +2,8 @@ import xml.etree.ElementTree as etree
from dataclasses import dataclass
from functools import reduce
from operator import getitem
+import jinja_helpers
+import misc_classes
import subprocess
import importlib
import traceback
@@ -17,6 +19,8 @@ import re
@dataclass
class Reflect:
client_code_path:str
+ assessment_struct:dict
+ zip_file:str
imported_modules = {}
def __post_init__(self):
@@ -227,14 +231,18 @@ class Reflect:
def __format_doc(*doc):
return str(doc[1]).rstrip()
-def gen_reflection_report(client_code_path, assessment_struct, student_no, configuration):
- # print(configuration)
- reflection = Reflect(client_code_path)
+def gen_reflection_report(client_code_path, assessment_struct, student_no, configuration, zip_file):
+ reflection = Reflect(client_code_path, assessment_struct, zip_file)
present_module_names = [i.name for i in reflection.client_modules]
out = assessment_struct
out["student_no"] = student_no
tests_to_run = {}
+ try:
+ produced_files = assessment_struct["produced_files"]
+ except KeyError:
+ produced_files = []
+
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]
@@ -327,9 +335,39 @@ def gen_reflection_report(client_code_path, assessment_struct, student_no, confi
except KeyError:
tests_to_run[filename] = [test]
+ if "run" in required_files_features.keys():
+ with misc_classes.ExtractZipToTempDir(zip_file) as tempdir:
+ with misc_classes.FileDependencies(assessment_struct, tempdir):
+ with misc_classes.ChangeDirectory(tempdir):
+ for cmd, contents in jinja_helpers.flatten_struct(required_files_features["run"]).items():
+
+ lines = ""
+ if "monitor" in contents.keys():
+
+ if contents["monitor"] not in produced_files:
+ raise MonitoredFileNotInProducedFilesException("The monitored file %s is not in the list of produced files. It needs to be added." % contents["monitor"])
+
+ subprocess.run(cmd.split())
+ with open(contents["monitor"], "r") as f:
+ lines = f.read()
+
+ else:
+ proc = subprocess.Popen(cmd.split(), stdout = subprocess.PIPE)
+ while True:
+ line = proc.stdout.readline()
+ if not line:
+ break
+ lines += line.decode()
+
+ print("===Lines===")
+ print(lines)
+
out["test_results"] = reflection.run_tests(tests_to_run, configuration["out"] == "stdout" and configuration["format"] in ["text", "txt"])
out["class_tree"] = reflection.get_class_tree()
- return out
+ # return out
+
+class MonitoredFileNotInProducedFilesException(Exception):
+ pass
if __name__ == "__main__":
# user_code_path = "D:\\Edencloud\\UniStuff\\3.0 - CMP 3rd Year Project\\Smarker\\../ExampleSubmissions/Submission_A"
diff --git a/smarker.conf b/smarker.conf
index aa5a415..44aea24 100644
--- a/smarker.conf
+++ b/smarker.conf
@@ -1,13 +1,13 @@
-[mysql]
-host = 192.168.1.92
-port = 3306
-user = smarker
-passwd = smarkerPassword
-
-[md]
-show_full_docs = True
-show_source = True
-
-[txt]
-show_full_docs = True
+[mysql]
+host = 192.168.1.92
+port = 3306
+user = smarker
+passwd = smarkerPassword
+
+[md]
+show_full_docs = True
+show_source = True
+
+[txt]
+show_full_docs = True
show_source = True \ No newline at end of file
diff --git a/templates/text.jinja2 b/templates/text.jinja2
index eca6ebd..eca6ebd 120000..100644
--- a/templates/text.jinja2
+++ b/templates/text.jinja2
Binary files differ