mirror of
https://github.com/JonasunderscoreJones/epr_grader.git
synced 2025-10-25 01:09:19 +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
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
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.
|
||||
"""
|
||||
|
||||
__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), ' ',
|
||||
|
|
|
|||
|
|
@ -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