From 5497926df1c8cfdf549bbe0b36c02f10fee44e13 Mon Sep 17 00:00:00 2001 From: Lukas Horst Date: Mon, 7 Oct 2024 14:29:17 +0200 Subject: [PATCH] Readme update --- README.md | 38 +++++++++++--- eprgrader.py | 107 +++++++++++++++++++++++++++++++++++++-- rating_table_adjuster.py | 87 ------------------------------- 3 files changed, 135 insertions(+), 97 deletions(-) delete mode 100644 rating_table_adjuster.py diff --git a/README.md b/README.md index 350befe..562929b 100644 --- a/README.md +++ b/README.md @@ -4,31 +4,36 @@ Ein Tool, um die Bewertung von EPR-Abgaben (und auch GPR-Abgaben) zu beschleunig ## Installation -1. Legt `eprgrader.py` (das eigentliche Programm) und `eprcheck_2019.py` (das pylint-Plugin für die -Author-Variable) im selben Verzeichnis ab. +1. Legt `eprgrader.py` (das eigentliche Programm), `eprcheck_2019.py` (das pylint-Plugin für die +Author-Variable) und `violation_checker.py` (Klasse um die Stylefehler zusammenzuzählen) im + selben Verzeichnis ab. 2. Wenn ihr den automatischen Style-Check benutzen wollt, installiert `pylint`, `pycodestyle` und `astroid` via `pip`: `pip install pylint==2.15.0 pycodestyle==2.8.0 astroid==2.13.5 openpyxl pandas` ## Zu Beginn -Ladet die ZIP-Datei(en) mit den Abgaben für eure Gruppe(n) sowie die Bewertungstabellle herunter. +Ladet die ZIP-Datei(en) mit den Abgaben für eure Gruppe(n), die Gesamtbewertung (.csv Datei) für +eure Gruppe(n) sowie die Bewertungstabelle herunter. Legt ein Verzeichnis für das Übungsblatt an, und dann einen Unterordner für jedes eurer Tutorien. -Legt die Abgaben-Zips in die jeweiligen Ordner. Das ganze sollte jetzt in etwa so aussehen: +Legt die Abgaben-Zips und die Gesamtbewertungen in die jeweiligen Ordner. Das ganze sollte +jetzt in etwa so aussehen: ``` blatt0 |-- Bewertungstabelle_EPR_0.xlsx |-- EPR01 | `-- EPR-2021-Abgabe zu EPR_00-EPR 01 - H 7 - Adrian-88422.zip +| `-- Bewertungen-EPR 2021-Abgabe zu EPR_00-EPR 01 - H 7 - Adrian-88422.csv `-- EPR02 `-- EPR-2021-Abgabe zu EPR_00-EPR 02 - H 6 - Adrian-88422.zip + `-- Bewertungen-EPR 2021-Abgabe zu EPR_00-EPR 02 - H 6 - Adrian-88422.csv ``` Führt jetzt den Startbefehl aus: ```cmd cd ...\Tutorium\blatt0 -python eprgrader.py begin --table Bewertungstabelle_EPR_4.xlsx --no-stylecheck +python eprgrader.py begin --table Bewertungstabelle_EPR_0.xlsx ``` Zusätzliche Optionen: @@ -37,12 +42,26 @@ Zusätzliche Optionen: * `--no-deduction`: Wenn es noch keinen Abzug für Stylefehler gibt. Hierdurch werden alle zip-Archive entpackt, die Bewertungstabellen kopiert und für jeden Teilnehmer -entsprechend umbenannt, und ggf. der Stylchecker ausgeführt. +entsprechend umbenannt, und ggf. der Stylechecker ausgeführt. + +Wenn der Stylechecker ausgeführt, wird außerdem direkt der Abzug berechnet und in die +Bewertungstabelle eingetragen. Die überprüften Stylefehler werden dabei in Gruppen eingeteilt +und dann innerhalb dieser Gruppen zusammengezählt. Wie die Fehlergruppen eingeteilt sind und wie +viel Abzug es gibt, kann man in der violation_checker.py Datei anpassen. + +Damit der Abzug automatisch eingetragen werden kann, muss es folgende Zellen geben: + - `__author__` falsch (Abzug für Fehler bei der author Variable) + - ...alle `o.g. Fehler` sind gleichbedeutend... (Abzug für Stylefehler) + - `Abzug bei` mangelnden Kommentaren... (Abzug für Docstrings) + +Markiert sind dabei die Stellen, nach welchen explizit gesucht wird. + ## Style-Prüfung erneut ausführen Bei Bedarf kann die Style-Prüfung erneut ausgeführt werden. Dabei werden alle bestehenden `stylecheck.txt`-Dateien überschrieben. +Die Punkte werden aber nicht mehr in die Bewertungstabellen eingetragen ```cmd cd ...\Tutorium\blatt0 @@ -51,6 +70,7 @@ python eprgrader.py relint Zusätzliche Optionen: * `--pairs`: Überprüft die `__author__`-Variable nach dem Format für Paaraufgaben. +* `--no-deduction`: Wenn es noch keinen Abzug für Stylefehler gibt. ## Abschluss @@ -58,6 +78,9 @@ Am Ende können die Bewertungsdateien (Glob-Pattern `Bewertung *`) sowie die `st für jeden Teilnehmer zusammengesammelt und für den Upload als Feedback-Datei wieder zusammengepackt werden. +Wenn die Gesamtbewertungstabellen in den Ordnern sind, wird die Gesamtpunktzahl von allen +Einzelbewertungen ausgelesen und in die csv Datei eingefügt. Dafür muss es die Zelle `Summe` geben. + Achtung: das funktioniert nur für die Einzelabgaben sinnvoll! ```cmd @@ -68,6 +91,9 @@ python eprgrader.py finalise Nun sollte sich in jedem Tutoriums-Unterordner eine neue Zip-Datei finden, die den Namen des Tutoriums trägt (z. B. `EPR02.zip`). Diese kann über die Moodle-Option "Mehrere Feedbackdateien in einer Zip-Datei hochladen" hochgeladen werden. +Die Gesamtbewertungstabelle kann unter "Bewertungstabelle hochladen" hochgeladen werden. Dabei +muss der Haken bei "Update von Datensätzen zulassen, ..." gesetzt werden. + ## Änderung der Style-Einstellungen diff --git a/eprgrader.py b/eprgrader.py index c679ded..bc694c4 100644 --- a/eprgrader.py +++ b/eprgrader.py @@ -8,11 +8,13 @@ Later on, collecting all the stylecheck files and grading tables into neat little archives for upload. """ -__author__ = "Adrian Welcker, Lukas Horst" +__author__ = "Adrian Welcker" +__credits__ = "Adjustments from Lukas Horst" import argparse import contextlib import copy +import csv import io import itertools import os @@ -20,18 +22,19 @@ import pathlib import platform import shutil import sys -from itertools import count +import openpyxl +import pandas as pd import unicodedata import zipfile import re +from openpyxl.styles import Font from datetime import datetime from pylint.lint import Run as RunPylint import pycodestyle -from rating_table_adjuster import update_rating, update_style_deduction from violation_checker import ViolationChecker PYLINT_ARGS = [ @@ -189,7 +192,10 @@ def lint_files(folders, author_pairs, deduction: bool): def remove_unnecessary_violations(style_check: str): - """Function to delete all lines with a violation to ignore""" + """ + Function to delete all lines with a violation to ignore + author: Lukas Horst + """ lines = style_check.splitlines() filtered_lines = [] @@ -336,6 +342,99 @@ def finalise_grading(folder: pathlib.Path): outfile.write(file, pathlib.PurePath(person.name) / file.name) +def get_points(file_path: str): + """ + Returns the total points of the given rating table + author: Lukas Horst + """ + data = pd.read_excel(file_path, usecols='A, C') + total_points = 0 + for i in range(len(data)): + if data.iat[i, 0] == 'Summe': + break + value = data.iat[i, 1] + if pd.notna(value) and type(value) != str: + total_points += value + return max(0, total_points) + + +def update_style_deduction(file_path: str, violation_checker: ViolationChecker, student_name: str): + """ + Function to update the deduction for style violations in the given rating table + author: Lukas Horst + """ + wb = openpyxl.load_workbook(file_path, data_only=True) + ws = wb['Sheet1'] + # Updating the name + ws[f'A1'].value = student_name + rows = ws.iter_rows(min_row=1, max_row=75, min_col=1, max_col=1) + for i, row in enumerate(rows): + cell = row[0] + if cell.value is not None: + # Updating the deduction for the author variable + if '__author__' in cell.value: + ws[f'C{i+1}'].value = -violation_checker.count_deduction(3) + ws[f'C{i + 1}'].font = Font(color='FF0000') + # All deductions except the author variable and docstrings + elif 'o.g. Fehler' in cell.value: + deduction = 0 + for j in range(1, 10): + if j == 3 or j == 5: + continue + deduction -= violation_checker.count_deduction(j) + ws[f'C{i+1}'].value = deduction + ws[f'C{i+1}'].font = Font(color='FF0000') + # Deduction for docstrings + elif 'Abzug bei' in cell.value: + ws[f'C{i+1}'].value = -violation_checker.count_deduction(5) + ws[f'C{i + 1}'].font = Font(color='FF0000') + # Updating the function for the total points + elif 'Summe' in cell.value: + ws[f'C{i+1}'] = f'=MAX(0, SUM(C1:C{i}))' + break + wb.save(file_path) + wb.close() + + +def read_csv_file(file_path: str): + """ + Function to read a csv file and returns a list with each row in a dic + author: Lukas Horst + """ + 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: str, data: list[dict[str, str]]): + """ + Function to (over)write a csv file with the given data + author: Lukas Horst + """ + 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: str, student_rating_path: str, student_name: str): + """ + Function to update the points of the given student + author: Lukas Horst + """ + 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 + + def main(): """The function main is where execution begins.""" print('EPRgrader v3/221031 running on ', datetime.now(), ' [', platform.platform(terse=True), ' ', diff --git a/rating_table_adjuster.py b/rating_table_adjuster.py deleted file mode 100644 index 9bcb400..0000000 --- a/rating_table_adjuster.py +++ /dev/null @@ -1,87 +0,0 @@ -__author__ = 'Lukas Horst' - -import csv - -import openpyxl -import pandas as pd -from openpyxl.styles import Font - -from violation_checker import ViolationChecker - - -def get_points(file_path: str): - """Returns the total points""" - data = pd.read_excel(file_path, usecols='A, C') - total_points = 0 - for i in range(len(data)): - if data.iat[i, 0] == 'Summe': - break - value = data.iat[i, 1] - if pd.notna(value) and type(value) != str: - total_points += value - return max(0, total_points) - - -def update_style_deduction(file_path: str, violation_checker: ViolationChecker, student_name: str): - """Function to update the deduction for style violations""" - wb = openpyxl.load_workbook(file_path, data_only=True) - ws = wb['Sheet1'] - # Updating the name - ws[f'A1'].value = student_name - rows = ws.iter_rows(min_row=1, max_row=75, min_col=1, max_col=1) - for i, row in enumerate(rows): - cell = row[0] - if cell.value is not None: - # Updating the deduction for the author variable - if '__author__' in cell.value: - ws[f'C{i+1}'].value = -violation_checker.count_deduction(3) - ws[f'C{i + 1}'].font = Font(color='FF0000') - # All deductions except the author variable and docstrings - elif 'alle o.g. Fehler' in cell.value: - deduction = 0 - for j in range(1, 10): - if j == 3 or j == 5: - continue - deduction -= violation_checker.count_deduction(j) - ws[f'C{i+1}'].value = deduction - ws[f'C{i+1}'].font = Font(color='FF0000') - # Deduction for docstrings - elif 'Abzug bei mangelden' in cell.value: - ws[f'C{i+1}'].value = -violation_checker.count_deduction(5) - ws[f'C{i + 1}'].font = Font(color='FF0000') - # Updating the function for the total points - elif 'Summe' in cell.value: - ws[f'C{i+1}'] = f'=MAX(0, SUM(C1:C{i}))' - break - wb.save(file_path) - wb.close() - - -def read_csv_file(file_path: str): - """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: str, data: list[dict[str, str]]): - """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: str, student_rating_path: str, student_name: str): - """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