mirror of
https://github.com/JonasunderscoreJones/epr_grader.git
synced 2025-10-28 09:39:18 +01:00
Grouping the violations and updating the overall grading in by finalising
This commit is contained in:
parent
6c1381ffb0
commit
ce6d67a83b
3 changed files with 199 additions and 133 deletions
37
eprgrader.py
37
eprgrader.py
|
|
@ -29,6 +29,7 @@ from datetime import datetime
|
||||||
from pylint.lint import Run as RunPylint
|
from pylint.lint import Run as RunPylint
|
||||||
import pycodestyle
|
import pycodestyle
|
||||||
|
|
||||||
|
from rating_table_adjuster import get_points, update_rating
|
||||||
from violation_checker import ViolationChecker
|
from violation_checker import ViolationChecker
|
||||||
|
|
||||||
PYLINT_ARGS = [
|
PYLINT_ARGS = [
|
||||||
|
|
@ -61,26 +62,20 @@ PYLINT_ARGS = [
|
||||||
# E0102: function-redefined
|
# E0102: function-redefined
|
||||||
# E0211: no-method-argument [when it should at least have self]
|
# E0211: no-method-argument [when it should at least have self]
|
||||||
"E0001,E0102,E0211," +
|
"E0001,E0102,E0211," +
|
||||||
# I0011: locally-disabled [to notify of 'pylint disable=...' comments]
|
|
||||||
# I0013: file-ignored
|
|
||||||
# I0020: suppressed-message
|
|
||||||
"I0011,I0013,I0020," +
|
|
||||||
# W0104: pointless-statement
|
# W0104: pointless-statement
|
||||||
# W0106: expression-not-assigned
|
|
||||||
# W0201: attribute-defined-outside-init
|
# W0201: attribute-defined-outside-init
|
||||||
# W0231: super-init-not-called
|
# W0231: super-init-not-called
|
||||||
# W0232: no-init
|
# W0232: no-init
|
||||||
# W0301: unnecessary-semicolon
|
# W0301: unnecessary-semicolon
|
||||||
# W0311: bad-indentation
|
# W0311: bad-indentation
|
||||||
# W0401: wildcard-import
|
# W0401: wildcard-import
|
||||||
# W0402: deprecated-module
|
|
||||||
# W0404: reimported
|
# W0404: reimported
|
||||||
# W0603: global-statement
|
# W0603: global-statement
|
||||||
# W0622: redefined-builtin
|
# W0622: redefined-builtin
|
||||||
# W0702: bare-except
|
# W0702: bare-except
|
||||||
# W0705: duplicate-except
|
# W0705: duplicate-except
|
||||||
# W0706: try-except-raise
|
# W0706: try-except-raise
|
||||||
"W0104,W0106,W0201,W0231,W0232,W0301,W0311,W0401,W0402,W0404,W0603,W0622,W0702,W0705,W0706"
|
"W0104,W0201,W0231,W0232,W0301,W0311,W0401,W0404,W0603,W0622,W0702,W0705,W0706"
|
||||||
]
|
]
|
||||||
|
|
||||||
PYCODESTYLE_SELECT = [
|
PYCODESTYLE_SELECT = [
|
||||||
|
|
@ -125,10 +120,6 @@ PYCODESTYLE_SELECT = [
|
||||||
'E714',
|
'E714',
|
||||||
# E721: use 'isinstance' instead of comparing types
|
# E721: use 'isinstance' instead of comparing types
|
||||||
'E721',
|
'E721',
|
||||||
# E731: use 'def' instead of assigning lambdas
|
|
||||||
'E731',
|
|
||||||
# E741-3: ambiguous single-character names (I, l, O) for var/class/func
|
|
||||||
'E741', 'E742', 'E743',
|
|
||||||
]
|
]
|
||||||
tmp_storage = {}
|
tmp_storage = {}
|
||||||
|
|
||||||
|
|
@ -183,13 +174,15 @@ def lint_files(folders, author_pairs):
|
||||||
with open(folder / 'stylecheck.txt', 'w', encoding='utf-8') as outfile:
|
with open(folder / 'stylecheck.txt', 'w', encoding='utf-8') as outfile:
|
||||||
if lintcache.tell() > 0:
|
if lintcache.tell() > 0:
|
||||||
style_check = remove_unnecessary_violations(lintcache.getvalue())
|
style_check = remove_unnecessary_violations(lintcache.getvalue())
|
||||||
violation_checker = ViolationChecker(style_check)
|
|
||||||
violation_checker.check_violations()
|
|
||||||
style_check += (f'\n-----Verstöße insgesamt-----'
|
|
||||||
f'\n{violation_checker.list_violation()}')
|
|
||||||
outfile.write(style_check)
|
|
||||||
else:
|
else:
|
||||||
outfile.write("Alles sieht gut aus -- weiter so!\n")
|
style_check = ""
|
||||||
|
violation_checker = ViolationChecker(style_check)
|
||||||
|
violation_checker.check_violations()
|
||||||
|
if violation_checker.count_violations(-1) == 0:
|
||||||
|
style_check = "Alles sieht gut aus -- weiter so!\n"
|
||||||
|
violation_string = violation_checker.list_violation()
|
||||||
|
style_check += f'\n{violation_string}'
|
||||||
|
outfile.write(style_check)
|
||||||
|
|
||||||
|
|
||||||
def remove_unnecessary_violations(style_check):
|
def remove_unnecessary_violations(style_check):
|
||||||
|
|
@ -284,7 +277,13 @@ def finalise_grading(folder: pathlib.Path):
|
||||||
issues = 0
|
issues = 0
|
||||||
print("Copying grades...")
|
print("Copying grades...")
|
||||||
folders = list(folder.glob("**/abgaben"))
|
folders = list(folder.glob("**/abgaben"))
|
||||||
|
count = 0
|
||||||
for f in folders:
|
for f in folders:
|
||||||
|
overall_rating_path = ''
|
||||||
|
for file_name in os.listdir(f.parent):
|
||||||
|
if file_name.startswith('Bewertungen-'):
|
||||||
|
overall_rating_path = os.path.join(f.parent, file_name)
|
||||||
|
break
|
||||||
target = f.parent / 'korrekturen'
|
target = f.parent / 'korrekturen'
|
||||||
target.mkdir()
|
target.mkdir()
|
||||||
for handin in (x for x in f.iterdir() if x.name != '.DS_Store'):
|
for handin in (x for x in f.iterdir() if x.name != '.DS_Store'):
|
||||||
|
|
@ -297,6 +296,10 @@ def finalise_grading(folder: pathlib.Path):
|
||||||
glob = list(handin.glob('Bewertung *'))
|
glob = list(handin.glob('Bewertung *'))
|
||||||
if len(glob) == 1:
|
if len(glob) == 1:
|
||||||
shutil.copy(glob[0], this_target)
|
shutil.copy(glob[0], this_target)
|
||||||
|
# If the overall rating file is given, the points will be written in
|
||||||
|
if len(overall_rating_path) != 0:
|
||||||
|
student_name = handin.name.split('_')[0]
|
||||||
|
update_rating(overall_rating_path, glob[0], student_name)
|
||||||
elif not glob:
|
elif not glob:
|
||||||
print(f" ! {handin.name}: no grading file")
|
print(f" ! {handin.name}: no grading file")
|
||||||
issues += 1
|
issues += 1
|
||||||
|
|
|
||||||
52
rating_table_adjuster.py
Normal file
52
rating_table_adjuster.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
__author__ = 'Lukas Horst'
|
||||||
|
|
||||||
|
import csv
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
|
|
||||||
|
|
||||||
|
def get_points(file_path):
|
||||||
|
"""Reads the given rating table and returns the total points"""
|
||||||
|
wb = openpyxl.load_workbook(file_path, data_only=True)
|
||||||
|
ws = wb['Sheet1']
|
||||||
|
rows = ws.iter_rows(min_row=20, max_row=75, min_col=1, max_col=1)
|
||||||
|
row_of_sum = -1
|
||||||
|
for i in rows: # searching the right row
|
||||||
|
if i[0].value == 'Summe':
|
||||||
|
row_of_sum = i[0].row
|
||||||
|
break
|
||||||
|
return ws[f'C{row_of_sum}'].value
|
||||||
|
|
||||||
|
|
||||||
|
def read_csv_file(file_path):
|
||||||
|
"""Function to read a csv file and returns a list with each row in a dic"""
|
||||||
|
with open(file_path, mode='r', newline='', encoding='utf-8') as file:
|
||||||
|
reader = csv.DictReader(file)
|
||||||
|
rows = []
|
||||||
|
for row in reader:
|
||||||
|
rows.append(row)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def write_csv_file(file_path, data):
|
||||||
|
"""Function to (over)write a csv file with the given data"""
|
||||||
|
with open(file_path, mode='w', newline='', encoding='utf-8') as file:
|
||||||
|
fieldnames = list(data[0].keys())
|
||||||
|
writer = csv.DictWriter(file, fieldnames=fieldnames)
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(data)
|
||||||
|
|
||||||
|
|
||||||
|
def update_rating(overall_rating_path, student_rating_path, student_name):
|
||||||
|
"""Function to update the points of the given student"""
|
||||||
|
csv_data = read_csv_file(overall_rating_path)
|
||||||
|
for row in csv_data:
|
||||||
|
if row['Vollständiger Name'] == student_name:
|
||||||
|
points = str(get_points(student_rating_path)).replace('.', ',')
|
||||||
|
row['Bewertung'] = points
|
||||||
|
write_csv_file(overall_rating_path, csv_data)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print(*read_csv_file('Test.csv'), sep='\n')
|
||||||
|
|
@ -5,129 +5,140 @@ import re
|
||||||
|
|
||||||
class ViolationChecker:
|
class ViolationChecker:
|
||||||
|
|
||||||
w0311 = 0 # Bad indention
|
violation_groups = [
|
||||||
w0401 = 0 # Wildcard import
|
'No deduction',
|
||||||
w0622 = 0 # Redefined builtin
|
'Global statements',
|
||||||
c0103 = 0 # Invalid name
|
'Imports',
|
||||||
c0116 = 0 # Missing function or method docstring
|
'Author variable',
|
||||||
c0114 = 0 # Missing module docstring
|
'Naming',
|
||||||
c0121 = 0 # Singleton-comparison
|
'Docstring',
|
||||||
c0325 = 0 # Superfluous-parens
|
'Spacing',
|
||||||
c0413 = 0 # Wrong import position
|
'Classes',
|
||||||
c2100 = 0 # Missing author variable
|
'Override',
|
||||||
c2101 = 0 # Malformed author variable
|
'Syntax'
|
||||||
c2102 = 0 # Incorrectly assigned author variable
|
]
|
||||||
e0001 = 0 # Syntax error
|
# {violation_name: [amount of the violation, description, violation_group]}
|
||||||
e0102 = 0 # Function redefined
|
violations = {'W0104': [0, 'Pointless statement', 0],
|
||||||
e231 = 0 # Missing whitespace after ','
|
'W0201': [0, 'Attribute defined outside init', 7],
|
||||||
e251 = 0 # Unexpected spaces around keyword / parameter equals
|
'W0231': [0, 'Super init not called', 7],
|
||||||
e261 = 0 # At least two spaces before inline comment
|
'W0232': [0, 'No init', 7],
|
||||||
e265 = 0 # Block comment should start with '# '
|
'W0301': [0, 'Unnecessary semicolon', 0],
|
||||||
e271 = 0 # Multiple space after keyword
|
'W0311': [0, 'Bad indention', 6],
|
||||||
e302 = 0 # Expected 2 blank lines
|
'W0401': [0, 'Wildcard import', 2],
|
||||||
e501 = 0 # Line too long > 99
|
'W0404': [0, 'Reimported', 2],
|
||||||
style_check = ''
|
'W0603': [0, 'Global statement', 1],
|
||||||
|
'W0622': [0, 'Redefined builtin', 8],
|
||||||
|
'W0702': [0, 'Bare except', 0],
|
||||||
|
'W0705': [0, 'Duplicate except', 0],
|
||||||
|
'W0706': [0, 'Try except raise', 0],
|
||||||
|
|
||||||
|
'C0102': [0, 'Blacklisted name', 4],
|
||||||
|
'C0103': [0, 'Invalid name', 4],
|
||||||
|
'C0112': [0, 'Empty docstring', 5],
|
||||||
|
'C0114': [0, 'Missing module docstring', 5],
|
||||||
|
'C0115': [0, 'Missing class docstring', 5],
|
||||||
|
'C0116': [0, 'Missing function or method docstring', 5],
|
||||||
|
'C0121': [0, 'Singleton-comparison', 0],
|
||||||
|
'C0144': [0, 'Non ascii name', 4],
|
||||||
|
'C0321': [0, 'Multiple statements', 0],
|
||||||
|
'C0325': [0, 'Superfluous-parens', 0],
|
||||||
|
'C0410': [0, 'Multiple imports', 2],
|
||||||
|
'C0411': [0, 'Wrong import order', 2],
|
||||||
|
'C0412': [0, 'Ungrouped imports', 2],
|
||||||
|
'C0413': [0, 'Wrong import position', 2],
|
||||||
|
'C2100': [0, 'Missing author variable', 3],
|
||||||
|
'C2101': [0, 'Malformed author variable', 3],
|
||||||
|
'C2102': [0, 'Incorrectly assigned author variable', 3],
|
||||||
|
|
||||||
|
'E0001': [0, 'Syntax error', 9],
|
||||||
|
'E0102': [0, 'Function redefined', 8],
|
||||||
|
'E0211': [0, 'No Method argument', 7],
|
||||||
|
'E201': [0, 'Whitespace after \'(\'', 6],
|
||||||
|
'E202': [0, 'Whitespace before \')\'', 6],
|
||||||
|
'E203': [0, 'Whitespace before \':\'', 6],
|
||||||
|
'E211': [0, 'Whitespace before \'(\'', 6],
|
||||||
|
'E221': [0, 'Multiple spaces before operator', 6],
|
||||||
|
'E222': [0, 'Multiple spaces after operator', 6],
|
||||||
|
'E223': [0, 'Tab before operator', 6],
|
||||||
|
'E224': [0, 'Tab after operator', 6],
|
||||||
|
'E225': [0, 'Missing whitespace around operator', 6],
|
||||||
|
'E231': [0, 'Missing whitespace after \',\', \';\', or \':\'', 6],
|
||||||
|
'E251': [0, 'Unexpected spaces around keyword / parameter equals', 6],
|
||||||
|
'E261': [0, 'At least two spaces before inline comment', 6],
|
||||||
|
'E262': [0, 'Inline comment should start with \'# \'', 6],
|
||||||
|
'E265': [0, 'Block comment should start with \'# \'', 6],
|
||||||
|
'E271': [0, 'Multiple space after keyword', 6],
|
||||||
|
'E302': [0, 'Expected 2 blank lines', 6],
|
||||||
|
'E501': [0, 'Line too long > 99', 6],
|
||||||
|
'E502': [0, 'Backslash redundant between brackets', 0],
|
||||||
|
'E713': [0, 'Negative membership test should use \'not in\'', 0],
|
||||||
|
'E714': [0, 'Negative identity test should use \'is not\'', 0],
|
||||||
|
'E721': [0, 'Use \'isinstance\' instead of comparing types', 0]}
|
||||||
|
style_check = ''
|
||||||
|
|
||||||
def __init__(self, style_check):
|
def __init__(self, style_check):
|
||||||
self.style_check = style_check
|
self.style_check = style_check
|
||||||
|
|
||||||
|
|
||||||
def check_violations(self):
|
def check_violations(self):
|
||||||
w0311_violations = re.findall(r',*W0311.*', self.style_check)
|
"""Method to search for all violations"""
|
||||||
self.w0311 = len(w0311_violations)
|
for violation_name, _ in self.violations.items():
|
||||||
|
all_violations = re.findall(rf',*{violation_name}.*', self.style_check)
|
||||||
w0401_violations = re.findall(r',*W0401.*', self.style_check)
|
self.violations[violation_name][0] = len(all_violations)
|
||||||
self.w0401 = len(w0401_violations)
|
|
||||||
|
|
||||||
w0622_violations = re.findall(r',*W0622.*', self.style_check)
|
|
||||||
self.w0622 = len(w0622_violations)
|
|
||||||
|
|
||||||
c0103_violations = re.findall(r',*C0103.*', self.style_check)
|
|
||||||
self.c0103 = len(c0103_violations)
|
|
||||||
|
|
||||||
c0114_violations = re.findall(r',*C0114.*', self.style_check)
|
|
||||||
self.c0114 = len(c0114_violations)
|
|
||||||
|
|
||||||
c0116_violations = re.findall(r',*C0116.*', self.style_check)
|
|
||||||
self.c0116 = len(c0116_violations)
|
|
||||||
|
|
||||||
c0121_violations = re.findall(r',*C0121.*', self.style_check)
|
|
||||||
self.c0121 = len(c0121_violations)
|
|
||||||
|
|
||||||
c0325_violations = re.findall(r',*C0325.*', self.style_check)
|
|
||||||
self.c0325 = len(c0325_violations)
|
|
||||||
|
|
||||||
c0413_violations = re.findall(r',*C0413.*', self.style_check)
|
|
||||||
self.c0413 = len(c0413_violations)
|
|
||||||
|
|
||||||
c2100_violations = re.findall(r',*C2100.*', self.style_check)
|
|
||||||
self.c2100 = len(c2100_violations)
|
|
||||||
|
|
||||||
c2101_violations = re.findall(r',*C2101.*', self.style_check)
|
|
||||||
self.c2101 = len(c2101_violations)
|
|
||||||
|
|
||||||
c2102_violations = re.findall(r',*C2102.*', self.style_check)
|
|
||||||
self.c2102 = len(c2102_violations)
|
|
||||||
|
|
||||||
e0001_violations = re.findall(r',*E0001.*', self.style_check)
|
|
||||||
self.e0001 = len(e0001_violations)
|
|
||||||
|
|
||||||
e0102_violations = re.findall(r',*E0102.*', self.style_check)
|
|
||||||
self.e0102 = len(e0102_violations)
|
|
||||||
|
|
||||||
e265_violations = re.findall(r',*E265.*', self.style_check)
|
|
||||||
self.e265 = len(e265_violations)
|
|
||||||
|
|
||||||
e501_violations = re.findall(r',*E501.*', self.style_check)
|
|
||||||
self.e501 = len(e501_violations)
|
|
||||||
|
|
||||||
e302_violations = re.findall(r',*E302.*', self.style_check)
|
|
||||||
self.e302 = len(e302_violations)
|
|
||||||
|
|
||||||
e231_violations = re.findall(r',*E231.*', self.style_check)
|
|
||||||
self.e231 = len(e231_violations)
|
|
||||||
|
|
||||||
e261_violations = re.findall(r',*E261.*', self.style_check)
|
|
||||||
self.e261 = len(e261_violations)
|
|
||||||
|
|
||||||
e271_violations = re.findall(r',*E271.*', self.style_check)
|
|
||||||
self.e271 = len(e271_violations)
|
|
||||||
|
|
||||||
e251_violations = re.findall(r',*E251.*', self.style_check)
|
|
||||||
self.e251 = len(e251_violations)
|
|
||||||
|
|
||||||
|
|
||||||
def list_violation(self):
|
def list_violation(self):
|
||||||
violations = ''
|
"""Method to return a list with all violations and the amount of the violations sort by
|
||||||
violations += (f'W03111 (Bad indention): {self.w0311}'
|
groups"""
|
||||||
f'\nW0401 (Wildcard import): {self.w0401}'
|
violation_string = ''
|
||||||
f'\nW0622 (Redefined builtin): {self.w0622}'
|
violation_groups_strings = []
|
||||||
f'\nC0103 (Invalid name): {self.c0103}'
|
for i in range(10):
|
||||||
f'\nC0114 (Missing module docstring): {self.c0114}'
|
violation_groups_strings.append('')
|
||||||
f'\nC0116 (Missing function or method docstring): {self.c0116}'
|
for violation_name, value in self.violations.items():
|
||||||
f'\nC0121 (Singleton-comparison): {self.c0121}'
|
violation_groups_strings[value[2]] += f'{violation_name} ({value[1]}): {value[0]}\n'
|
||||||
f'\nC0325 (Superfluous-parens): {self.c0325}'
|
for i, violation_group in enumerate(self.violation_groups):
|
||||||
f'\nC0413 (Wrong import position): {self.c0413}'
|
if i == 0: continue
|
||||||
f'\nC2100 (Missing author variable): {self.c2100}'
|
violation_string += f'-----{violation_group}-----\n{violation_groups_strings[i]}'
|
||||||
f'\nC2101 (Missing author variable): {self.c2101}'
|
violation_amount = self.count_violations(i)
|
||||||
f'\nC2102 (Incorrectly assigned author variable): {self.c2102}'
|
violation_string += (f'\nFehler Insgesamt: {violation_amount} Abzug: '
|
||||||
f'\nE0001 (Syntax error): {self.e0001}'
|
f'{self.count_deduction(i, violation_amount)} Punkt(e)\n\n')
|
||||||
f'\nE0102 (Function redefined): {self.e0102}'
|
if i == 9:
|
||||||
f'\nE231 (Missing whitespace after \',\'): {self.e231}'
|
violation_string += f'-----No deduction-----\n{violation_groups_strings[0]}'
|
||||||
f'\nE251 (Unexpected spaces around keyword / parameter equals): {self.e251}'
|
violation_string += f'\nFehler Insgesamt: {violation_amount} Abzug: 0 Punkte'
|
||||||
f'\nE261 (At least two spaces before inline comment): {self.e261}'
|
return violation_string
|
||||||
f'\nE265 (Block comment should start with \'# \'): {self.e265}'
|
|
||||||
f'\nE271 (Multiple space after keyword): {self.e271}'
|
|
||||||
f'\nE302 (Expected 2 blank lines): {self.e302}'
|
|
||||||
f'\nE501 (Line too long > 99): {self.e501}')
|
|
||||||
return violations
|
|
||||||
|
|
||||||
|
def count_violations(self, violation_group):
|
||||||
|
"""Method to count all violations"""
|
||||||
|
all_violations = 0
|
||||||
|
if violation_group == -1:
|
||||||
|
for _, value in self.violations.items():
|
||||||
|
all_violations += value[0]
|
||||||
|
else:
|
||||||
|
for _, value in self.violations.items():
|
||||||
|
if value[2] == violation_group:
|
||||||
|
all_violations += value[0]
|
||||||
|
return all_violations
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def count_deduction(self, violation_group, violation_amount):
|
||||||
with open('G02_Voll/abgaben/Cynthia Celoudis_691452_assignsubmission_file/stylecheck.txt', 'r',
|
"""Method to count the deduction based on the group and amount"""
|
||||||
encoding='utf-8') as file:
|
if violation_group == 0:
|
||||||
file_content = file.read()
|
return 0
|
||||||
violation_checker = ViolationChecker(file_content)
|
elif violation_group == 3:
|
||||||
violation_checker.check_violations()
|
if violation_amount > 0:
|
||||||
print(violation_checker.list_violation())
|
# The deduction for the author variable
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
elif violation_group == 5:
|
||||||
|
# Violation for docstrings with a max deduction
|
||||||
|
return min(violation_amount*0.5, 2)
|
||||||
|
else:
|
||||||
|
# Cause group 6 is bigger it can get a higher deduction than 0.5
|
||||||
|
if violation_group == 6 and violation_amount > 30:
|
||||||
|
return 1
|
||||||
|
elif violation_group == 6 and violation_amount > 20:
|
||||||
|
return 0.75
|
||||||
|
elif violation_amount > 9:
|
||||||
|
return 0.5
|
||||||
|
elif violation_amount > 1:
|
||||||
|
return 0.25
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue