diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/Asyst_code.iml b/.idea/Asyst_code.iml
new file mode 100644
index 0000000000000000000000000000000000000000..c5de4eb4005d5f40bb0f5459cb4e94850bdfa2ff
--- /dev/null
+++ b/.idea/Asyst_code.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..aeb483921323486ab6e4a356a05cafb855de0ee1
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3951b3f93147063370b0ce6c8dcfb523360118a0
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000000000000000000000000000000000..94a25f7f4cb416c083d265558da75d457237d671
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ASYST.exe b/ASYST.exe
index 0255574b41b621abd7ee8468aa36584d87fb1ec6..36b1072d7d271f61261f0821dc19e5941cdf457c 100644
Binary files a/ASYST.exe and b/ASYST.exe differ
diff --git a/DE_Demo_Daten.xlsx b/DE_Demo_Daten.xlsx
index a387752d3800aac3b3ecf4ff22783962f73b583c..a83fecf32dd15ebc4517541cea39408c2ac983c9 100644
Binary files a/DE_Demo_Daten.xlsx and b/DE_Demo_Daten.xlsx differ
diff --git a/README_DE.md b/README_DE.md
index 0be725f115eb6eee677bf08a7a3b12b916a88edf..f1dc9157085c67dfbe9ea6dbda1f87340bc49578 100644
--- a/README_DE.md
+++ b/README_DE.md
@@ -28,11 +28,11 @@ und zum anderen empfehlen wir, bei der Überprüfung gezielt die ASYST-Bewertung
Das Programm ist in Python geschrieben; der Quellcode ist öffentlich zugänglich. Um ASYST einfacher nutzbar zu machen, wurden die Python-Skripte
in eine ausführbare Programmdatei umgewandelt, die in Windows 11 nutzbar ist.
-Die technischen Hintergründe zu ASYST und eine Beispielrechnung zum Einsatz für das Deutsche finden sich in Pado, Eryilmaz und Kirschner, IJAIED 2023.
+Die technischen Hintergründe zu ASYST und eine Beispielrechnung zum Einsatz für das Deutsche finden sich in Pado, Eryilmaz und Kirschner, IJAIED 2023.
Welche Sprachen unterstützt ASYST?
-ASYST wurde für Deutsch und Englisch getestet.
+ASYST wurde für Deutsch und Englisch getestet.
Das Sprachmodell, das Deutsch abdeckt, kann im Prinzip noch weitere Sprachen verarbeiten. Sie können also grundsätzlich "Deutsch" als Spracheinstellung auswählen und Daten in einer der anderen unterstützten Sprachen hochladen. Bitte prüfen Sie die Ergebnisse aber sorgfältig, es liegen keine Erfahrungen vor! (Die Sprachen sind lt. den Modellerstellern: ar, bg, ca, cs, da, de, el, en, es, et, fa, fi, fr, fr-ca, gl, gu, he, hi, hr, hu, hy, id, it, ja, ka, ko, ku, lt, lv, mk, mn, mr, ms, my, nb, nl, pl, pt, pt-br, ro, ru, sk, sl, sq, sr, sv, th, tr, uk, ur, vi, zh-cn, zh-tw.)
@@ -49,7 +49,7 @@ Dabei müssen die folgende Informationen in der **richtigen Reihenfolge** und mi
1) **Question**: Die gestellte Frage
2) **referenceAnswer**: Eine korrekte Antwort / Musterlösung / Referenzantwort
3) **studentAnswer**: Die vom Prüfling gegebene Antwort, die bewertet werden soll.
-5) (optional) **observed grade**: Hier kann die tatsächliche Bewertung durch die Lehrkraft eingetragen werden, um Kennzahlen über die Richtigkeit der Vorhersagen zu bekommen.
+5) **observed grade**: Hier kann die tatsächliche Bewertung durch die Lehrkraft eingetragen werden, um Kennzahlen über die Richtigkeit der Vorhersagen zu bekommen. Wenn keine Bewertungen existieren, tragen Sie NONE ein.
Die Beispieltabelle finden Sie unter DE_Demo_Daten.xlsx. Sie enthält einige Fragen und Antworten aus dem CSSAG-Korpus (Computer Science Short Answers in German) der HFT Stuttgart. Das Korpus is CC-BY-NC lizenziert.
@@ -72,17 +72,14 @@ Anschließend kann über einen Klick auf das Feld "Input File" die zu verarbeite
Hierbei sollten die Daten wie oben beschrieben angeordnet sein.
Nach einem Klick auf das "Start"-Feld beginnt ASYST mit der Verarbeitung der Daten. Dies kann wiederum eine Weile dauern (1-2 Minuten, relativ unabhängig von der Menge der zu verarbeitenden Daten).
-Sobald das Programm alle Einträge verarbeitet und Vorhersagen getroffen hat, öffnet sich eine Tabellenansicht mit der Überschrift "Results" (Ergebnisse).
+Sobald das Programm alle Einträge verarbeitet und Vorhersagen getroffen hat, öffnet sich ein Fenster, das Ihnen erlaubt, die Ergebnisse wiederum im Excel-Format .xlsx zu speichern. Nach dem Speichern öffnet sich die Datei automatisch, so dass Sie die Bewertungsvorschläge bearbeiten können.
-Die Ergebnistabelle enthält alle Spalten der eingelesenen Tabelle, sowie zusätzlich in der Spalte "predicted grade" die von ASYST vorgeschlagene Bewertung der Antworten. Die "incorrect"-Einträge der als falsch eingestuften Antworten sind rot hinterlegt. Sie können in dieser Tabelle allerdings noch keine Bewertungen verändern. Speichern Sie hierzu über einen Klick auf "Save as" die erzeugte Tabelle und öffnen Sie sie dann mit einem Tabellenkalkulationsprogramm.
-
-![table_results.png](images%2Ftable_results.png)
+Die Ergebnistabelle enthält alle Spalten der eingelesenen Tabelle, sowie zusätzlich in der Spalte **predicted grade** die von ASYST vorgeschlagene Bewertung der Antworten. Die "incorrect"-Einträge der als falsch eingestuften Antworten sind rot hinterlegt.
Sobald die Ergebnistabelle angezeigt wird, kann ASYST die nächste Tabelle einlesen und verarbeiten.
-**ACHTUNG: Die Ergebnistabelle wird nicht automatisch gespeichert.** Werden die Ergebnisse nicht gespeichert,
-wird die Erbgebnistabelle im nächsten Durchlauf überschrieben.
-Daher sollte, um die Ergebnisse zu sichern, auf den **"Save as"**- Button geklickt und die Ausgabetabelle am gewünschten Ort gespeichert werden.
+Sie sehen in der Ergebnistabelle auch eine Evaluation der ASYST-Ausgaben, falls Sie in der Spalte **observed grade** die tatsächliche Bewertung eingegeben haben. Hier ist besonders die Ausgabe in Spalte **precision** für die beiden Bewertungen "correct" und "incorrect" wichtig: Eine hoher Wert für "precision" (ca. 0.8 und höher) bedeutet, dass die Vorhersage von ASYST für diese Bewertung voraussichtlich verlässlich ist. S. auch Abschnitt "Wie arbeitet man mit der Ausgabe von ASYST weiter?"
+
Wie arbeitet man mit der Ausgabe von ASYST weiter?
@@ -111,18 +108,18 @@ Wir empfehlen die folgende **Vorgehensweise** beim Einsatz von ASYST:
Hierzu werden die Fragen und Antworten aus dem Testdatensatz von ASYST verarbeitet und anschließend die erhaltene Klassifikation mit der manuellen Bewertung abgeglichen (z.B. in einer Tabellenkalkulation wie Excel oder Libre Office Calc).
- Dabei kann der Anteil der korrekt klassifizierten Antworten im gesamten Datensatz ermittelt werden - dieser sollte 85% oder höher betragen (das entspricht einer Fehlerquote von 15% oder weniger).
+ Dabei kann der Anteil der korrekt klassifizierten Antworten im gesamten Datensatz (Kennzahl "Accurady") ermittelt werden - dieser sollte für rein automaticshe Korrektur 85% oder höher betragen (das entspricht einer Fehlerquote von 15% oder weniger).
Sie können auch für die einzelnen Bewertungen (richtig/falsch) berechnen, wie groß die Präzision für die verschiedenen Bewertungen jeweils ist. Die Präzision misst, wie viele Vorhersagen einer bestimmten Bewertung tatsächlich richtig waren, d.h. wie vertrauenswürdig die Vorhersagen des Bewerters für ein bestimmtes Label sind. So bedeutet eine Präzision von 75% für die Bewertung "korrekt", dass drei Viertel aller Vorhersagen von "korrekt" gestimmt haben, aber in einem Viertel der Fälle die Antwort laut der manuellen Bewertung falsch war.
- _(Die Funktion, diese Kenngrößen der Zuverlässigkeit automatisch in einem Testmodus zu generieren soll in Zukunft dem Programm noch hinzugefügt werden.)_
+ Wie oben unter "Wie verwendet man ASYST?" beschrieben, werden diese Kenngrößen automatisch berechnet, sobald Sie zu den zu bewertenden Fragen **observed grades** liefern.
5) **Entscheidung** wie der Ansatz genutzt werden soll.
Anhand der erhobenen Kenngrößen zur Zuverlässigkeit für die oben genannten Kriterien kann nun eine Entscheidung getroffen werden.
Wie groß ist der Anteil der korrekt vorhergesagten Bewertungen? Beträgt er >85%, können Sie die ASYST-Vorhersagen sogar unverändert übernehmen, falls Sie dies wünschen.
-
Wie ist die Präzision der einzelnen Bewertungsklassen (richtig/falsch)? Wenn eine der Klassen deutlich zuverlässiger vorhergesagt wird, können Sie entscheiden, diese Vorhersagen ungeprüft zu übernehmen und nur die Vorhersagen für die weniger verlässlich erkannte Klasse zu überprüfen. Dies führt in der Praxis zu einer deutlichen Zeitersparnis.
+
Wie ist die Präzision der einzelnen Bewertungsklassen (richtig/falsch)? Wenn eine der Klassen deutlich zuverlässiger vorhergesagt wird, können Sie entscheiden, diese Vorhersagen ungeprüft zu übernehmen und nur die Vorhersagen für die weniger verlässlich erkannte Klasse zu überprüfen. Dies führt in der Praxis zu einer deutlichen Zeitersparnis. Ein Richtwert ist eine Präzision von 0,8 oder höher.
Wie ist der Bewertungsfehler verteilt? Werden übermäßig viele korrekte Antworten als falsch bewertet, oder umgekehrt? Ist dies für Ihre Situation akzeptabel?
Wie viel Bewertungsaufwand hätten Sie für den Beispieldatensatz eingespart, z.B. indem Sie die verlässlichere Bewertungsklasse ungeprüft akzeptieren?
diff --git a/README_EN.md b/README_EN.md
index bb6bced129c249be418e945421d472b9bd407a9b..e7147a2b1b1b3eb6a7a1e18b9643e18909f521b6 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -28,11 +28,12 @@ and second, we recommend reviewing mostly those ASYST grades that are most prone
The program is written in Python; the source code is publicly available. To make ASYST easier to use, the Python scripts have been
converted into an executable that is usable in Windows 11.
-The technical background and development history of ASYST are described in Pado, Eryilmaz and Kirschner, IJAIED 2023 along with a worked example for German data. For English data, a similar example is available in Pado, AIED 2022
+The technical background and development history of ASYST are described in Pado, Eryilmaz and Kirschner, IJAIED 2023 along with a worked example for German data. For English data, a similar example is available in Pado, AIED 2022
+
Which languages are supported by ASYST?
-ASYST has been tested for German and English.
+ASYST has been tested for German and English.
The language model that covers German can in principle handle other languages, as well. So, in principle, you could select "German" as language setting and upload data in one of the other languages covered by the model. If you try this, please check the results carefully, as this is untested! (According to the model developers, the covered languages are: ar, bg, ca, cs, da, de, el, en, es, et, fa, fi, fr, fr-ca, gl, gu, he, hi, hr, hu, hy, id, it, ja, ka, ko, ku, lt, lv, mk, mn, mr, ms, my, nb, nl, pl, pt, pt-br, ro, ru, sk, sl, sq, sr, sv, th, tr, uk, ur, vi, zh-cn, zh-tw.)
@@ -50,7 +51,7 @@ The following information needs to be included in the **correct order** and with
1) **question**: The question that was asked
2) **referenceAnswer**: A correct answer / reference answer
3) **studentAnswer**: The student answer that is to be evaluated
-5) (optional) **observed grade**: The grade given by the teacher can be entered here in order to evaluate the accuracy of the ASYST predictions.
+5) **observed grade**: The grade given by the teacher can be entered here in order to evaluate the accuracy of the ASYST predictions. If no grades exist, enter NONE.
The example table can be found at DE_Demo_Daten.xlsx. It contains some questions and answers from the CSSAG corpus (Computer Science Short Answers in German) of HFT Stuttgart. The corpus is licensed as CC-BY-NC.
@@ -72,17 +73,13 @@ After the program has been started, a window appears. First, select the language
The table to be processed can then be selected by clicking on the “Input File” field. The data should be arranged as described above.
After clicking on the “Start” field, ASYST begins processing the data. Again, this can take a while (1-2 minutes, relatively independent of the amount of data being processed).
-Once the program has processed all answers and made predictions, a table view headed "Results" opens.
-
-The results table contains all columns of the input table, as well as the grades suggested by ASYST -- see the the "predicted grade" column. The grades for answers classified as incorrect are highlighted in red. You cannot make change in this table. Instead, save the data by clicking on “Save as” and then open the resulting .xlsx file with a spreadsheet program.
+Once the program has processed all answers and made predictions, a window opens that allows you to save the results as another Excel file in .xslx format. After saving, the file opens automatically, so the grade predictions can be edited.
-![table_results.png](images%2Ftable_results.png)
+The results table contains all columns of the input table, as well as the grades suggested by ASYST -- see the the "predicted grade" column. The grades for answers classified as incorrect are highlighted in red.
As soon as the result table is displayed, ASYST can read and process the next input table.
-**ATTENTION: The results table is not saved automatically.**
-Therefore, to save the results, the **"Save as"** button should be clicked and the output table should be saved at the desired location.
-
+In the results table, you also find evaluation output for the ASYST predictions if you provided **observed grades**. The most important information is in column **precision** for the grades "correct" and "incorrect". A high precision value (ca. 0.8 and higher) means that the ASYST predictions for this grade are probably reliable. See also seciton "How do I continue with the output from ASYST?"
How do I continue with the output from ASYST?
@@ -110,18 +107,16 @@ We recommend the following **process** when using ASYST:
For this purpose, process the questions and answers from the test data set using ASYST and compare the grade predictions with the manual assessment (e.g. in a spreadsheet such as Excel or Libre Office Calc).
- The proportion of correctly classified answers in the entire data set gives you the system accuracy (which should be at around 85% or higher, which means disagreement between the manual and machine grades of 15% or less).
+ The proportion of correctly classified answers in the entire data set gives you the system accuracy (which should be at around 85% or higher for fully automated grading, which means disagreement between the manual and machine grades of 15% or less).
- You can also calculate the precision for each grade ("correct"/"incorrect"). Precision measures how many predictions of a given grade were actually correct, i.e. how trustworthy the ASYST's predictions are for a given label. A precision of 75% for the rating "correct" means that three quarters of all predictions of "correct" were in fact right, but for a quarter of the cases the answer was actually wrong according to the manual grades.
-
- _(We plan to add functionality to automatically generate these reliability parameters in the future.)_
+ ASYST also calculates the precision for each grade ("correct"/"incorrect") as soon as observed grades are provided. Precision measures how many predictions of a given grade were actually correct, i.e. how trustworthy the ASYST's predictions are for a given label. A precision of 75% for the rating "correct" means that three quarters of all predictions of "correct" were in fact right, but for a quarter of the cases the answer was actually wrong according to the manual grades.
5) **Decide** how to use ASYST's predictions.
A usage decision can now be made based on the reliability parameters collected for the criteria mentioned above.
How large is the proportion of correctly predicted reviews? If it is >85%, you can even adopt the ASYST predictions unchanged if you wish.
-
What is the precision of the grade labels ("correct"/"incorrect")? If one of the grade labels is likely to be significantly more reliable, you can decide to accept these predictions without review and only check the predictions for the less reliable grade predictions. In practice, this leads to significant time savings.
+
What is the precision of the grade labels ("correct"/"incorrect")? If one of the grade labels is likely to be significantly more reliable, you can decide to accept these predictions without review and only check the predictions for the less reliable grade predictions. In practice, this leads to significant time savings. As a rule of thumb, a precision of 0.8 or higher may be chosen.
How is the grading error distributed? Are correct answers frequently predicted to be incorrect, or vice versa? Is this acceptable for your situation?
How much evaluation effort would you have saved for the example data set, e.g. by accepting the more reliable grade label without review?
diff --git a/Source/Skript/.DS_Store b/Source/Skript/.DS_Store
deleted file mode 100644
index 0f429091f95d1dceefbb7f1cd5078c0954bc03c1..0000000000000000000000000000000000000000
Binary files a/Source/Skript/.DS_Store and /dev/null differ
diff --git a/Source/Skript/english/.DS_Store b/Source/Skript/english/.DS_Store
deleted file mode 100644
index 307c9f40bedca21c4143b002310f62a6d57bf046..0000000000000000000000000000000000000000
Binary files a/Source/Skript/english/.DS_Store and /dev/null differ
diff --git a/Source/Skript/english/run_SAG_mnli.py b/Source/Skript/english/run_SAG_mnli.py
index 20c5f7bd3b076d0f097bf0f36d5a82fb55afa624..37e203a4983fb83d4ba9edf1b8108f231abf37ba 100644
--- a/Source/Skript/english/run_SAG_mnli.py
+++ b/Source/Skript/english/run_SAG_mnli.py
@@ -33,6 +33,7 @@ import torch
from torch.utils.data import DataLoader, SequentialSampler, TensorDataset
from sklearn.metrics import f1_score, accuracy_score
+from sklearn.metrics import classification_report
from transformers import (
BertConfig,
@@ -491,10 +492,53 @@ def print_predictions(args, preds):
processor.get_test_examples(args.data_dir)
)
+ # observed grade list created
+ obs_grade = [ex.label for ex in examples]
+
+ # suggested grade list created
+ sugg_grade = ['correct' if pred == 0 else 'incorrect' for pred in preds]
+
+ # flag: do observed grades exist?
+ count=0
+
+ # Check if obs_grade contains "NONE" values or is empty
+ if not obs_grade or all(grade == 'NONE' for grade in obs_grade):
+ count += 1
+
+ else:
+ # classification report
+ classification_rep = classification_report(obs_grade, sugg_grade)
+
+ report_string = classification_rep
+
+ report_lines = report_string.split('\n')
+
+ # print(report_lines)
+
+ # accuracy line
+ formatted_accuracy_line = "\t".join(report_lines[5].split())
+ formatted_acc_line_with_tabs = (formatted_accuracy_line[:formatted_accuracy_line.index('\t',
+ formatted_accuracy_line.index(
+ '\t'))] + '\t\t' +
+ formatted_accuracy_line[
+ formatted_accuracy_line.index('\t', formatted_accuracy_line.index('\t')):])
+
+ # #weighted avg printing
+ #
+ wt_avg_line = "\t".join(report_lines[7].split())
+
+ new_wt_avg_line = wt_avg_line.replace("\t", " ", 1)
+
+ # Join the entire newly formatted list into a single string
+ formatted_output = "\n".join([
+ "\t precision \t recall \t f1-score \t support",
+ "\t".join(report_lines[2].split()),
+ "\t".join(report_lines[3].split()),
+ formatted_acc_line_with_tabs,
+ new_wt_avg_line
+ ])
+
with open(args.data_dir + "/" + dir_name + "/predictions.txt", "w", encoding="utf8") as writer:
- # print("# examples: " + str(len(examples)))
- # print("# labels: " + str(len(labels)))
- # print("# preds: " + str(len(preds)))
writer.write(
"question\treferenceAnswer\tstudentAnswer\tsuggested grade\tobserved grade\n")
@@ -519,7 +563,17 @@ def print_predictions(args, preds):
+ examples[i].label
+ "\n"
)
- # else: print("Labels don't match! "+str(i)+": "+str(examples[i].label)+" "+str(labels[i]))
+
+ if count == 1:
+ writer.write("\nClassification Report cannot be printed as observed grade column is empty or filled "
+ "with 'NONE' or 'none' values\n")
+ else:
+
+ # Write the classification report to the file
+
+ writer.write(
+ "\nClassification Report - high Precision for classes correct or incorrect indicates that the class prediction is reliable:\n")
+ writer.write(formatted_output)
if __name__ == "__main__":
diff --git a/Source/Skript/german/.DS_Store b/Source/Skript/german/.DS_Store
deleted file mode 100644
index d0da439dffcc3253b60c3efbd08401ed8b1d1bf9..0000000000000000000000000000000000000000
Binary files a/Source/Skript/german/.DS_Store and /dev/null differ
diff --git a/Source/Skript/german/run_LR_SBERT.py b/Source/Skript/german/run_LR_SBERT.py
index 645d371709e739f6284f8d66ea5f895deddb573d..f3253ddfcca02a4c37e94a0a5ff4aa29bfe3717d 100644
--- a/Source/Skript/german/run_LR_SBERT.py
+++ b/Source/Skript/german/run_LR_SBERT.py
@@ -11,7 +11,8 @@ import argparse
from sklearn import metrics
from sentence_transformers import models, SentenceTransformer
from sklearn.linear_model import LogisticRegression, Perceptron
-from sklearn.metrics import confusion_matrix
+from sklearn.metrics import f1_score
+from sklearn.metrics import classification_report
from sklearn.model_selection import cross_validate, cross_val_predict
__author__ = "Yunus Eryilmaz"
@@ -20,7 +21,6 @@ __date__ = "21.07.2021"
__source__ = "https://pypi.org/project/sentence-transformers/0.3.0/"
-
def main():
parser = argparse.ArgumentParser()
@@ -33,8 +33,8 @@ def main():
# Required parameters
parser.add_argument(
"--data",
- #default=None,
- default=location+"\\Skript\\outputs\\test.tsv",
+ # default=None,
+ default=location + "\\Skript\\outputs\\test.tsv",
type=str,
# required=True,
required=False,
@@ -43,7 +43,7 @@ def main():
parser.add_argument(
"--output_dir",
# default=None,
- default=location+"\\Skript\\outputs\\",
+ default=location + "\\Skript\\outputs\\",
type=str,
# required=True,
required=False,
@@ -52,7 +52,7 @@ def main():
parser.add_argument(
"--model_dir",
# default=None,
- default=location+"\\Skript\\german\\models",
+ default=location + "\\Skript\\german\\models",
type=str,
# required=True,
required=False,
@@ -70,7 +70,7 @@ def main():
# print("Reading from",args.data, file=out);
- with open(os.path.join(location,args.data)) as ft:
+ with open(os.path.join(location, args.data)) as ft:
dft = pd.read_csv(ft, delimiter='\t')
# Sentences we want sentence embeddings for
@@ -78,6 +78,8 @@ def main():
sentences2_test = dft['studentAnswer'].values.tolist()
# print("Input read:",sentences2_test, file=out);
+ # print(sentences1_test)
+
# Use BERT for mapping tokens to embeddings
word_embedding_model = models.Transformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
# pooling operation can choose by setting true (Apply mean pooling to get one fixed sized sentence vector)
@@ -119,9 +121,83 @@ def main():
# print('--------Evaluate on Testset------- ', file=out)
predictions = clf_log.predict(X_test)
+ # new code inserted here
+
+ count = 0
+
+ # observed grade list created
+ observed_grade_column = dft['observed grade']
+ obs_grade = observed_grade_column.tolist()
+
+ # suggested grade list created
+ temp_sugg_grade = predictions
+ sugg_grade = ['correct' if pred == 1 else 'incorrect' for pred in temp_sugg_grade]
+
+ # Check if obs_grade contains "NONE" values or is empty
+ if not obs_grade or all(grade == 'NONE' for grade in obs_grade):
+
+ # print("obs_grade is empty or contains 'NONE' values. Skipping classification report.")
+ count += 1
+
+ else:
+ # classification report
+ classification_rep = classification_report(obs_grade, sugg_grade)
+
+ report_string = classification_rep
+
+ report_lines = report_string.split('\n')
+
+ # print(report_lines)
+
+ # accuracy line
+ formatted_accuracy_line = "\t".join(report_lines[5].split())
+ formatted_acc_line_with_tabs = (formatted_accuracy_line[:formatted_accuracy_line.index('\t',
+ formatted_accuracy_line.index(
+ '\t'))] + '\t\t' +
+ formatted_accuracy_line[
+ formatted_accuracy_line.index('\t', formatted_accuracy_line.index('\t')):])
+
+ # #weighted avg printing
+ #
+ wt_avg_line = "\t".join(report_lines[7].split())
+ # print(wt_avg_line)
+
+ new_wt_avg_line = wt_avg_line.replace("\t", " ", 1)
+ # print(new_wt_avg_line)
+
+ #
+ # wt_avg_line = report_lines[7].split()
+ #
+ # #wt_avg_line
+ #
+ # wg_element_1 = wt_avg_line[0]
+ # wg_element_2 = wt_avg_line[1]
+ #
+ # print(wg_element_1)
+ # print(wg_element_2)
+ #
+ # new_wt_line_out_1_2 =
+
+ # formatted_wt_with_tabs = (wt_avg_line[:wt_avg_line.index('\t',
+ # wt_avg_line.index('\t')) +1 ] + '\t' +
+ # wt_avg_line[wt_avg_line.index('\t', wt_avg_line.index('\t')):])
+
+ # Join the entire newly formatted list into a single string
+ formatted_output = "\n".join([
+ "\t precision \t recall \t f1-score \t support",
+ "\t".join(report_lines[2].split()),
+ "\t".join(report_lines[3].split()),
+ formatted_acc_line_with_tabs,
+ new_wt_avg_line
+ ])
+
+ # Print the entire formatted output
+ # print("\nFormatted Output:")
+ # print(formatted_output)
+
# UP print results
with open(args.output_dir + "\\predictions.txt", "w") as writer:
- writer.write("question\treferenceAnswer\tstudentAnswer\tsuggested grade\tobserved grade\n")
+ writer.write("question\treferenceAnswer\tstudentAnswer\tsuggested_grade\tobserved_grade\n")
for i in range(len(dft)):
hrpred = "incorrect"
if predictions[i] == 1:
@@ -139,6 +215,16 @@ def main():
+ "\n"
)
+ if count == 1:
+ writer.write("\nClassification Report cannot pe printed as observed grade column is empty or filled "
+ "with 'NONE' or 'none' values\n")
+ else:
+
+ # Write the classification report to the file
+
+ writer.write("\nClassification Report - high Precision for classes correct or incorrect indicates that the class prediction is reliable:\n")
+ writer.write(formatted_output)
+
# print('\nExecution time:', time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time)),file=out)
diff --git a/Source/Skript/outputs/predictions.txt b/Source/Skript/outputs/predictions.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6f43d618f8fbc1be3f94a8aad004af542152714f
--- /dev/null
+++ b/Source/Skript/outputs/predictions.txt
@@ -0,0 +1,17 @@
+question referenceAnswer studentAnswer suggested_grade observed_grade
+Warum braucht man für XML-Tags Namensräume? Da jeder Benutzer frei XML-Tags definieren kann, kann es passieren, dass dasselbe Tag in mehreren Dokumenten vorkommt und jeweils eine unterschiedliche Bedeutung hat. Namensräume sorgen dafür, dass diese unterschiedlichen Bedeutungen eindeutig auseinandergehalten werden können, indem die Tags qualifiziert werden. Namensräume verknüpfen Tags mit einer Anwendung: weil sich die Namen von Tags aus einzelnen XML Dateien überschneiden können.Deshlab qualifizierte Namen das bedeuteet dass die Namen sich auf einen Namenraum beziehen und der Namenraum ist als Attribut an Elemente gebunden. correct correct
+Warum möchten Sie bei der Objektserialisierung mit Hilfe von transient bzw. serialPersistentFields[] die Kontrolle darüber haben, welche Felder serialisiert werden? Wenn das Objekt schützenswerte Information enthält wie z.B. Passwörter, müssen diese bei der Serialisierung ausgeschlossen werden, da sie sonst aus dem serialisierten Objekt problemlos auszulesen sind. Außerdem gibt es Felder, die nicht serialisierbar sind (z.B. Sockets oder Threads). Diese Felder möchte man auch ausschließen. Wenn Attribute mit transient markiert werden, dann werden sie bei der Serialisierung übersprungen. serialPersistentFields[] kann man die zu serialisierenden Attribute aufzählen correct incorrect
+Warum braucht man für XML-Tags Namensräume? Da jeder Benutzer frei XML-Tags definieren kann, kann es passieren, dass dasselbe Tag in mehreren Dokumenten vorkommt und jeweils eine unterschiedliche Bedeutung hat. Namensräume sorgen dafür, dass diese unterschiedlichen Bedeutungen eindeutig auseinandergehalten werden können, indem die Tags qualifiziert werden. Namensräume verknüpfen Tags mit einer Anwendung: Namensräume verknüft ein XML-Element mit einer Anwendung. Der Gültigkeitsbereich gilt für den Bereich mit dem Präfix versehenen Elemente und Attribute. correct correct
+Sie möchten ein protected-Feld und mehrere unimplementierte Methoden vererben. Benutzen Sie eine Schnittstelle oder eine abstrakte Klasse? Warum? abstrakte Klasse. Schnittstellen können nur Konstanten vererben, abstrakte Klassen vererben Felder. alternativ: abstrakte Klassen vererben unimplementierte Methoden ich würde eine abstrakte Klasse benutzen, weil die Schinttstellen können nur Konstanten haben. correct correct
+Warum braucht man bei der Arbeit mit Threads Synchronisation? Man muss vermeiden, dass verschiedene Threads gleichzeitig auf Daten oder Objekte zugreifen, weil es dadurch zur Zerstörung von Werten und zu inkonsistenten Zuständen kommen kann. Wenn zwei oder mehrere parallel laufende Threads dasselbe Objekt ansprechen, kann es zu Problemem kommen, da beide gleichzeitig das Objekt ändern. Deswegen benutzt man Monitore, wobei die dazugehörigen Objekte in ein Monitor eingeschlossen werden. Eingekapselt werden kritische Abschnitte. incorrect incorrect
+Wie wird bei Methodenschablonen ermittelt, welchen konkreten Typ T zur Laufzeit annimmt? Wie wird dies bei Klassenschablonen ermittelt? Bei Methodenschablonen wird T aus den angegebenen Parametern abgeleitet (deshalb muss T als einer der Parametertypen vorliegen). Wenn die Parameter nicht den gleichen Typ haben, wird T als der unterste gemeinsame Muttertyp in der Ableitungshierarchie gewählt. Bei Klassenschablonen wird beim Erstellen eines neuen Objekts die Typinstanziierung angegeben. Bei der instanzierung. Die in der Klammer verwendeten Typen. incorrect correct
+Warum können verschiedene Instanzen von Enumerationstypen in switch-case-Konstrukten genutzt werden? Weil es von jedem Objekt nur eine Instanz gibt, so dass == mit equals() identisch ist. Weil die Enumerationstypen stets public, final und static sind. incorrect incorrect
+Wie bestimmen Sie bei einem XML-Dokument Gültigkeit und Wohlgeformtheit? Kann eines ohne das andere vorkommen? Wohlgeformtheit bestimmt sich aus den Syntaxvorschriften für XML-Dokumente (z.B. müssen alle Tags geschlossen werden, es darf nur ein Wurzelelement geben, reservierte Zeichen müssen vermieden werden). Gültigkeit liegt vor, wenn das XML-Dokument darüber hinaus der Elementdefinition aus der DTD entspricht. Ein XML-Dokument kann wohlgeformt sein, ohne gültig zu sein, aber zur Gültigkeit ist die Wohlgeformtheit Voraussetzung. Alternativ: Dokumente werden mit ""validate"" auf Gültigkeit und Wohlgeformtheit geprüft Ein XML Dokument muss einen internen und externen DTD enthalten. Die DTD-Datei beschreibt die Struktur. Diese muss gut geformt sein. Eine XML Datei kann ohne DTD nicht vorkommen, denn dann ist Struktur nicht wohlgeformt. incorrect incorrect
+In einer Swing-Demo ändert sich beim Klick auf einen Button der Text auf dem Button. Wie sieht der mehrschrittige Ablauf hinter den Kulissen aus? Der Klick auf den Button erzeugt ein ActionEvent. Das ActionEvent wird von einem Listener aufgefangen, der beim Button registriert ist. Dieser Listener führt daraufhin seinen Code aus, der den Text ändert. es wird mit einer actionEvent erzeugt incorrect correct
+
+Classification Report:
+ precision recall f1-score support
+correct 0.75 0.60 0.67 5
+incorrect 0.60 0.75 0.67 4
+accuracy 0.67 9
+weighted avg 0.68 0.67 0.67 9
\ No newline at end of file
diff --git a/Source/Skript/outputs/test.tsv b/Source/Skript/outputs/test.tsv
new file mode 100644
index 0000000000000000000000000000000000000000..70965c74870201cb17f5281a842d5a837e84cf64
--- /dev/null
+++ b/Source/Skript/outputs/test.tsv
@@ -0,0 +1,10 @@
+question referenceAnswer studentAnswer observed grade
+Warum braucht man für XML-Tags Namensräume? Da jeder Benutzer frei XML-Tags definieren kann, kann es passieren, dass dasselbe Tag in mehreren Dokumenten vorkommt und jeweils eine unterschiedliche Bedeutung hat. Namensräume sorgen dafür, dass diese unterschiedlichen Bedeutungen eindeutig auseinandergehalten werden können, indem die Tags qualifiziert werden. Namensräume verknüpfen Tags mit einer Anwendung: weil sich die Namen von Tags aus einzelnen XML Dateien überschneiden können.Deshlab qualifizierte Namen das bedeuteet dass die Namen sich auf einen Namenraum beziehen und der Namenraum ist als Attribut an Elemente gebunden. correct
+Warum möchten Sie bei der Objektserialisierung mit Hilfe von transient bzw. serialPersistentFields[] die Kontrolle darüber haben, welche Felder serialisiert werden? Wenn das Objekt schützenswerte Information enthält wie z.B. Passwörter, müssen diese bei der Serialisierung ausgeschlossen werden, da sie sonst aus dem serialisierten Objekt problemlos auszulesen sind. Außerdem gibt es Felder, die nicht serialisierbar sind (z.B. Sockets oder Threads). Diese Felder möchte man auch ausschließen. Wenn Attribute mit transient markiert werden, dann werden sie bei der Serialisierung übersprungen. serialPersistentFields[] kann man die zu serialisierenden Attribute aufzählen incorrect
+Warum braucht man für XML-Tags Namensräume? Da jeder Benutzer frei XML-Tags definieren kann, kann es passieren, dass dasselbe Tag in mehreren Dokumenten vorkommt und jeweils eine unterschiedliche Bedeutung hat. Namensräume sorgen dafür, dass diese unterschiedlichen Bedeutungen eindeutig auseinandergehalten werden können, indem die Tags qualifiziert werden. Namensräume verknüpfen Tags mit einer Anwendung: Namensräume verknüft ein XML-Element mit einer Anwendung. Der Gültigkeitsbereich gilt für den Bereich mit dem Präfix versehenen Elemente und Attribute. correct
+Sie möchten ein protected-Feld und mehrere unimplementierte Methoden vererben. Benutzen Sie eine Schnittstelle oder eine abstrakte Klasse? Warum? abstrakte Klasse. Schnittstellen können nur Konstanten vererben, abstrakte Klassen vererben Felder. alternativ: abstrakte Klassen vererben unimplementierte Methoden ich würde eine abstrakte Klasse benutzen, weil die Schinttstellen können nur Konstanten haben. correct
+Warum braucht man bei der Arbeit mit Threads Synchronisation? Man muss vermeiden, dass verschiedene Threads gleichzeitig auf Daten oder Objekte zugreifen, weil es dadurch zur Zerstörung von Werten und zu inkonsistenten Zuständen kommen kann. Wenn zwei oder mehrere parallel laufende Threads dasselbe Objekt ansprechen, kann es zu Problemem kommen, da beide gleichzeitig das Objekt ändern. Deswegen benutzt man Monitore, wobei die dazugehörigen Objekte in ein Monitor eingeschlossen werden. Eingekapselt werden kritische Abschnitte. incorrect
+Wie wird bei Methodenschablonen ermittelt, welchen konkreten Typ T zur Laufzeit annimmt? Wie wird dies bei Klassenschablonen ermittelt? Bei Methodenschablonen wird T aus den angegebenen Parametern abgeleitet (deshalb muss T als einer der Parametertypen vorliegen). Wenn die Parameter nicht den gleichen Typ haben, wird T als der unterste gemeinsame Muttertyp in der Ableitungshierarchie gewählt. Bei Klassenschablonen wird beim Erstellen eines neuen Objekts die Typinstanziierung angegeben. Bei der instanzierung. Die in der Klammer verwendeten Typen. correct
+Warum können verschiedene Instanzen von Enumerationstypen in switch-case-Konstrukten genutzt werden? Weil es von jedem Objekt nur eine Instanz gibt, so dass == mit equals() identisch ist. Weil die Enumerationstypen stets public, final und static sind. incorrect
+Wie bestimmen Sie bei einem XML-Dokument Gültigkeit und Wohlgeformtheit? Kann eines ohne das andere vorkommen? Wohlgeformtheit bestimmt sich aus den Syntaxvorschriften für XML-Dokumente (z.B. müssen alle Tags geschlossen werden, es darf nur ein Wurzelelement geben, reservierte Zeichen müssen vermieden werden). Gültigkeit liegt vor, wenn das XML-Dokument darüber hinaus der Elementdefinition aus der DTD entspricht. Ein XML-Dokument kann wohlgeformt sein, ohne gültig zu sein, aber zur Gültigkeit ist die Wohlgeformtheit Voraussetzung. Alternativ: Dokumente werden mit ""validate"" auf Gültigkeit und Wohlgeformtheit geprüft Ein XML Dokument muss einen internen und externen DTD enthalten. Die DTD-Datei beschreibt die Struktur. Diese muss gut geformt sein. Eine XML Datei kann ohne DTD nicht vorkommen, denn dann ist Struktur nicht wohlgeformt. incorrect
+In einer Swing-Demo ändert sich beim Klick auf einen Button der Text auf dem Button. Wie sieht der mehrschrittige Ablauf hinter den Kulissen aus? Der Klick auf den Button erzeugt ein ActionEvent. Das ActionEvent wird von einem Listener aufgefangen, der beim Button registriert ist. Dieser Listener führt daraufhin seinen Code aus, der den Text ändert. es wird mit einer actionEvent erzeugt correct
diff --git a/Source/main.py b/Source/main.py
index 1563403e92e2dc863854bcced996536c48a2224d..fb4df26612af61ef54dc4f317accc62a040da19f 100644
--- a/Source/main.py
+++ b/Source/main.py
@@ -1,4 +1,5 @@
import tkinter as tk
+from tkinter import filedialog
from tkinter import BOTTOM, RIDGE, RIGHT, Label, Menu, Toplevel, ttk
from tkinter.messagebox import showwarning
from tkinter.font import NORMAL
@@ -11,9 +12,14 @@ import xlsxwriter
import tkinter
##import sentence_transformers
+
import pandas as pd
import sys
import os
+from sklearn.metrics import f1_score
+from matplotlib import pyplot as plt
+from sklearn.metrics import confusion_matrix, classification_report
+import seaborn as sns
from Skript.english.run_SAG_mnli import main as english_backend
from Skript.german.run_LR_SBERT import main as german_backend
@@ -74,22 +80,22 @@ class App(ttk.Frame):
output_str = resource_path(output_str)
# Excel files are parsed into csv files
- if (indexIdent.endswith('.xlsx')):
+ if indexIdent.endswith('.xlsx'):
# Check if file exists
- if (os.path.exists(csv_file_path)):
+ if os.path.exists(csv_file_path):
# showinfo(title="debug_info", message=output_str + "\n" + indexIdent)
read_file = pd.read_excel(indexIdent)
- read_file.to_csv(output_str, index=None, header=True, quoting=csv.QUOTE_NONE, escapechar="\t", sep='\t')
+ read_file.to_csv(output_str, index=None, header=True, quoting=csv.QUOTE_NONE, escapechar="\t",
+ sep='\t')
# when the input data has been loaded, highlight the Start button to indicate that
# grading can now commence
self.style = ttk.Style(self)
- self.style.configure('Wild.TButton',font=('Helvetica', 14, 'bold'),
- foreground='green')
-
+ self.style.configure('Wild.TButton', font=('Helvetica', 14, 'bold'),
+ foreground='green')
- self.button1.config(state=NORMAL,style='Wild.TButton')
+ self.button1.config(state=NORMAL, style='Wild.TButton')
print(self.button1.winfo_class())
labelframe.update()
@@ -100,7 +106,7 @@ class App(ttk.Frame):
showinfo(
title="Information",
message="File " + csv_file_path + " could not be found."
- # message="Please choose a file"
+ # message="Please choose a file"
)
@@ -112,76 +118,81 @@ class App(ttk.Frame):
# message="Please choose a file"
)
+ # New output window for showing the content of a csv file
-
- # New outputwinodow for showing the content of a csv file
def open_new_window():
- new_window = Toplevel(root)
- new_window.protocol("WM_DELETE_WINDOW", new_window.destroy)
- new_window.title("ASYST Evaluated Results")
- #new_window.geometry("1920x1080")
- new_window.iconbitmap(resource_path("icon.ico"))
- lab = Label(new_window, text="Results", font=("Segoeui 20 bold"))
- lab.pack(padx=0, pady=35)
- container = ttk.Frame(new_window)
- canvas = tk.Canvas(container)
- scrollbary = ttk.Scrollbar(container, orient='vertical', command=canvas.yview)
- scrollbar = ttk.Scrollbar(container, orient="horizontal", command=canvas.xview)
- scrollable_frame = ttk.Frame(canvas)
- scrollable_frame.bind(
- "",
- lambda e: canvas.configure(
- scrollregion=canvas.bbox("all")
- )
- )
- canvas.configure(scrollregion=canvas.bbox("all"))
- canvas.create_window((4, 4), window=scrollable_frame)
-
- csv_file_path = resource_path("Skript\\outputs\\predictions.txt");
-
- # Read the csv file and parse it into the table
-
- with open((csv_file_path), "r", newline="", encoding="utf8") as f:
-
- reader = csv.reader(f, delimiter="\t")
- r = 0
- for col in reader:
- c = 0
- for row in col:
- # variable column width
- col_width = 65
- anch= 'center'
- match c:
- case 0:
- col_width = 10 # ID is short
- anch='w'
- case 3:
- col_width = 20 # label is short
- anch='w'
- case 4:
- col_width = 20 # label is short
- anch = 'w'
-
- label = Label(scrollable_frame, width=col_width, height=4, text=row, wraplength=500, anchor=anch, relief=RIDGE,
- font=("Segoeui 10"))
- label.grid(row=r + 1, column=c + 1)
- string_c = "incorrect"
- if string_c in row:
- label.config(bg="red")
- c += 1
- r += 1
- canvas.config(height=500, width=1500, xscrollcommand=scrollbar.set, yscrollcommand=scrollbary.set)
- canvas.configure(xscrollcommand=scrollbar.set, yscrollcommand=scrollbary.set)
- scrollbary.pack(side=RIGHT, fill=tk.Y)
- scrollbar.pack(side=BOTTOM, fill=tk.X)
-
- save_button = ttk.Button(new_window, text="Save as ...", command=save_as_excel)
- save_button.pack(side=BOTTOM, expand=True)
-
- container.pack(padx=30, pady=15,fill="both", expand=True)
- canvas.pack(fill="both", expand=True)
-
-
+ new_window = Toplevel(root)
+ new_window.protocol("WM_DELETE_WINDOW", new_window.destroy)
+ new_window.title("ASYST Evaluated Results")
+ new_window.geometry("850x500")
+ new_window.iconbitmap(resource_path("icon.ico"))
+ lab = Label(new_window, text="Results\n\nClick on Save As button below to view the file",
+ font=("Segoeui 20 bold"))
+ lab.pack(padx=0, pady=35)
+
+ # container = ttk.Frame(new_window)
+ # canvas = tk.Canvas(container)
+ # scrollbary = ttk.Scrollbar(container, orient='vertical', command=canvas.yview)
+ # scrollbar = ttk.Scrollbar(container, orient="horizontal", command=canvas.xview)
+ # scrollable_frame = ttk.Frame(canvas)
+ # scrollable_frame.bind(
+ # "",
+ # lambda e: canvas.configure(
+ # scrollregion=canvas.bbox("all")
+ # )
+ # )
+ # canvas.configure(scrollregion=canvas.bbox("all"))
+ # canvas.create_window((4, 4), window=scrollable_frame)
+
+ csv_file_path = resource_path("Skript\\outputs\\predictions.txt");
+
+ #############
+
+ # Read the csv file and parse it into the table
+
+ # with open((csv_file_path), "r", newline="", encoding="utf8") as f:
+ #
+ # reader = csv.reader(f, delimiter="\t")
+ # r = 0
+ # for col in reader:
+ # c = 0
+ # for row in col:
+ # # variable column width
+ # col_width = 65
+ # anch = 'center'
+ # match c:
+ # case 0:
+ # col_width = 10 # ID is short
+ # anch = 'w'
+ # case 3:
+ # col_width = 20 # label is short
+ # anch = 'w'
+ # case 4:
+ # col_width = 20 # label is short
+ # anch = 'w'
+ #
+ # label = Label(scrollable_frame, width=col_width, height=4, text=row, wraplength=500,
+ # anchor=anch, relief=RIDGE,
+ # font=("Segoeui 10"))
+ # label.grid(row=r + 1, column=c + 1)
+ # string_c = "incorrect"
+ # if string_c in row:
+ # label.config(bg="red")
+ # c += 1
+ # r += 1
+ #
+ # canvas.config(height=500, width=1500, xscrollcommand=scrollbar.set, yscrollcommand=scrollbary.set)
+ # canvas.configure(xscrollcommand=scrollbar.set, yscrollcommand=scrollbary.set)
+ # scrollbary.pack(side=RIGHT, fill=tk.Y)
+ # scrollbar.pack(side=BOTTOM, fill=tk.X)
+
+ ################################
+
+ save_button = ttk.Button(new_window, text="Save as ...", command=save_as_excel)
+ save_button.pack(side=BOTTOM, expand=True)
+
+ # container.pack(padx=30, pady=15, fill="both", expand=True)
+ # canvas.pack(fill="both", expand=True)
def save_as_excel():
@@ -194,15 +205,40 @@ class App(ttk.Frame):
t = tkinter.filedialog.asksaveasfile(initialfile=name, mode='w',
filetypes=[("All Files", "*.*"), ("Excel Files", "*.xlsx")],
defaultextension=".xlsx")
+
+ if t is not None: # Check if the user didn't click Cancel
+ file_path = t.name # Save the location of the saved file
+ # print(file_path)
+
workbook = xlsxwriter.Workbook(t.name)
+
+ # changes here for format highlight correct and incorrect
+
+ cell_format1 = workbook.add_format()
+ cell_format1.set_pattern(1) # This is optional when using a solid fill.
+ cell_format1.set_bg_color('green')
+ cell_format1.set_bold()
+
+ cell_format2 = workbook.add_format()
+ cell_format2.set_pattern(1) # This is optional when using a solid fill.
+ cell_format2.set_bg_color('red')
+ cell_format2.set_bold()
+
worksheet = workbook.add_worksheet()
with open(csvfile, 'rt', encoding='utf8') as f:
reader = csv.reader(f, delimiter="\t")
for r, row in enumerate(reader):
for c, col in enumerate(row):
- worksheet.write(r, c, col)
+ if col == "correct":
+ worksheet.write(r, c, col, cell_format1)
+ elif col == "incorrect":
+ worksheet.write(r, c, col, cell_format2)
+ else:
+ worksheet.write(r, c, col)
+
workbook.close()
t.close()
+ os.startfile(file_path)
def start_progress():
@@ -212,15 +248,14 @@ class App(ttk.Frame):
self.button2.config(state=DISABLED)
root.update()
-
# start the correct backend pipeline
- if (self.combobox.get() == 'English'):
+ if self.combobox.get() == 'English':
showinfo(
title="Information",
message="Generating grade suggestions for English student answers. This may take a little while.")
# run English backend
english_backend()
- elif (self.combobox.get() == 'German'):
+ elif self.combobox.get() == 'German':
showinfo(
title="Information",
message="Generating grade suggestions for German student answers. This may take a little while.")
@@ -247,7 +282,7 @@ class App(ttk.Frame):
def set_buttons_state(event):
self.button2.config(state="normal")
# print('State changed')
- if (self.combobox.get() == 'Spanish'):
+ if self.combobox.get() == 'Spanish':
showwarning(title="Information", message="Not available")
self.button1.config(state="disabled")
self.button2.config(state="disabled")
@@ -256,7 +291,7 @@ class App(ttk.Frame):
self.combobox.bind('<>', set_buttons_state)
self.button1 = ttk.Button(self.widgets_frame, text="Start",
- command=start_progress,
+ command=start_progress,
state=tk.DISABLED)
self.button1.grid(row=12, column=0, padx=1, pady=5, sticky="nsew")
@@ -268,6 +303,8 @@ def resource_path(relative_path):
else:
bundle_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(bundle_dir, relative_path)
+
+
# Override stdout and stderr with NullWriter in GUI --noconsole mode
# This allows us to avoid a bug where tqdm try to write on NoneType
# https://github.com/tqdm/tqdm/issues/794
@@ -275,6 +312,7 @@ class NullWriter:
def write(self, data):
pass
+
if __name__ == "__main__":
# Override stdout and stderr with NullWriter in GUI --noconsole mode
@@ -286,7 +324,6 @@ if __name__ == "__main__":
if sys.stderr is None:
sys.stderr = NullWriter()
-
root = tk.Tk()
root.title("ASYST")