mirror of
https://github.com/JonasunderscoreJones/epr_grader.git
synced 2025-10-25 09:19:18 +02:00
Readme update
This commit is contained in:
parent
e9c9c6ae49
commit
5497926df1
3 changed files with 135 additions and 97 deletions
38
README.md
38
README.md
|
|
@ -4,31 +4,36 @@ Ein Tool, um die Bewertung von EPR-Abgaben (und auch GPR-Abgaben) zu beschleunig
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Legt `eprgrader.py` (das eigentliche Programm) und `eprcheck_2019.py` (das pylint-Plugin für die
|
1. Legt `eprgrader.py` (das eigentliche Programm), `eprcheck_2019.py` (das pylint-Plugin für die
|
||||||
Author-Variable) im selben Verzeichnis ab.
|
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`
|
2. Wenn ihr den automatischen Style-Check benutzen wollt, installiert `pylint`, `pycodestyle`
|
||||||
und `astroid` via `pip`:
|
und `astroid` via `pip`:
|
||||||
`pip install pylint==2.15.0 pycodestyle==2.8.0 astroid==2.13.5 openpyxl pandas`
|
`pip install pylint==2.15.0 pycodestyle==2.8.0 astroid==2.13.5 openpyxl pandas`
|
||||||
|
|
||||||
## Zu Beginn
|
## 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 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
|
blatt0
|
||||||
|-- Bewertungstabelle_EPR_0.xlsx
|
|-- Bewertungstabelle_EPR_0.xlsx
|
||||||
|-- EPR01
|
|-- EPR01
|
||||||
| `-- EPR-2021-Abgabe zu EPR_00-EPR 01 - H 7 - Adrian-88422.zip
|
| `-- 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
|
`-- EPR02
|
||||||
`-- EPR-2021-Abgabe zu EPR_00-EPR 02 - H 6 - Adrian-88422.zip
|
`-- 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:
|
Führt jetzt den Startbefehl aus:
|
||||||
```cmd
|
```cmd
|
||||||
cd ...\Tutorium\blatt0
|
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:
|
Zusätzliche Optionen:
|
||||||
|
|
@ -37,12 +42,26 @@ Zusätzliche Optionen:
|
||||||
* `--no-deduction`: Wenn es noch keinen Abzug für Stylefehler gibt.
|
* `--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
|
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
|
## Style-Prüfung erneut ausführen
|
||||||
|
|
||||||
Bei Bedarf kann die Style-Prüfung erneut ausgeführt werden. Dabei werden alle bestehenden
|
Bei Bedarf kann die Style-Prüfung erneut ausgeführt werden. Dabei werden alle bestehenden
|
||||||
`stylecheck.txt`-Dateien überschrieben.
|
`stylecheck.txt`-Dateien überschrieben.
|
||||||
|
Die Punkte werden aber nicht mehr in die Bewertungstabellen eingetragen
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
cd ...\Tutorium\blatt0
|
cd ...\Tutorium\blatt0
|
||||||
|
|
@ -51,6 +70,7 @@ python eprgrader.py relint
|
||||||
|
|
||||||
Zusätzliche Optionen:
|
Zusätzliche Optionen:
|
||||||
* `--pairs`: Überprüft die `__author__`-Variable nach dem Format für Paaraufgaben.
|
* `--pairs`: Überprüft die `__author__`-Variable nach dem Format für Paaraufgaben.
|
||||||
|
* `--no-deduction`: Wenn es noch keinen Abzug für Stylefehler gibt.
|
||||||
|
|
||||||
## Abschluss
|
## 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
|
für jeden Teilnehmer zusammengesammelt und für den Upload als Feedback-Datei wieder zusammengepackt
|
||||||
werden.
|
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!
|
Achtung: das funktioniert nur für die Einzelabgaben sinnvoll!
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
|
|
@ -68,6 +91,9 @@ python eprgrader.py finalise
|
||||||
Nun sollte sich in jedem Tutoriums-Unterordner eine neue Zip-Datei finden, die den Namen
|
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
|
des Tutoriums trägt (z. B. `EPR02.zip`). Diese kann über die Moodle-Option "Mehrere Feedbackdateien
|
||||||
in einer Zip-Datei hochladen" hochgeladen werden.
|
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
|
## Änderung der Style-Einstellungen
|
||||||
|
|
||||||
|
|
|
||||||
107
eprgrader.py
107
eprgrader.py
|
|
@ -8,11 +8,13 @@ Later on, collecting all the stylecheck files and grading tables into
|
||||||
neat little archives for upload.
|
neat little archives for upload.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = "Adrian Welcker, Lukas Horst"
|
__author__ = "Adrian Welcker"
|
||||||
|
__credits__ = "Adjustments from Lukas Horst"
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
|
import csv
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
|
@ -20,18 +22,19 @@ import pathlib
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from itertools import count
|
|
||||||
|
|
||||||
|
import openpyxl
|
||||||
|
import pandas as pd
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import zipfile
|
import zipfile
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from openpyxl.styles import Font
|
||||||
from datetime import datetime
|
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 update_rating, update_style_deduction
|
|
||||||
from violation_checker import ViolationChecker
|
from violation_checker import ViolationChecker
|
||||||
|
|
||||||
PYLINT_ARGS = [
|
PYLINT_ARGS = [
|
||||||
|
|
@ -189,7 +192,10 @@ def lint_files(folders, author_pairs, deduction: bool):
|
||||||
|
|
||||||
|
|
||||||
def remove_unnecessary_violations(style_check: str):
|
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()
|
lines = style_check.splitlines()
|
||||||
|
|
||||||
filtered_lines = []
|
filtered_lines = []
|
||||||
|
|
@ -336,6 +342,99 @@ def finalise_grading(folder: pathlib.Path):
|
||||||
outfile.write(file, pathlib.PurePath(person.name) / file.name)
|
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():
|
def main():
|
||||||
"""The function main is where execution begins."""
|
"""The function main is where execution begins."""
|
||||||
print('EPRgrader v3/221031 running on ', datetime.now(), ' [', platform.platform(terse=True), ' ',
|
print('EPRgrader v3/221031 running on ', datetime.now(), ' [', platform.platform(terse=True), ' ',
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue