From beb8a4a45d17039a969ebf72aba0adc216dfab0e Mon Sep 17 00:00:00 2001 From: Malte Schokolowski <baw8441@uni-hamburg.de> Date: Mon, 13 Dec 2021 20:25:08 +0100 Subject: [PATCH] changed READMEs and added gitignore --- verarbeitung/.gitignore | 57 +++++++++++++++++ verarbeitung/README.md | 61 +++++++++++++++++++ verarbeitung/construct_new_graph/README.md | 29 +++++++++ .../{Processing.py => initialize_graph.py} | 2 +- verarbeitung/construct_new_graph/readme.txt | 11 ---- .../dev_files/{readme.txt => README.md} | 0 verarbeitung/dev_files/print_graph_test.py | 6 +- verarbeitung/process_main.py | 4 ++ verarbeitung/readme.txt | 19 ------ verarbeitung/test/README.md | 27 ++++++++ ...nittest.py => construct_graph_unittest.py} | 49 +++++---------- verarbeitung/test/readme.txt | 6 -- verarbeitung/test/update_graph_unittest.py | 47 ++++++++++++++ verarbeitung/update_graph/README.md | 37 +++++++++++ .../update_graph/connect_new_input.py | 2 +- verarbeitung/update_graph/readme.txt | 15 ----- 16 files changed, 281 insertions(+), 91 deletions(-) create mode 100644 verarbeitung/.gitignore create mode 100644 verarbeitung/README.md create mode 100644 verarbeitung/construct_new_graph/README.md rename verarbeitung/construct_new_graph/{Processing.py => initialize_graph.py} (98%) delete mode 100644 verarbeitung/construct_new_graph/readme.txt rename verarbeitung/dev_files/{readme.txt => README.md} (100%) delete mode 100644 verarbeitung/readme.txt create mode 100644 verarbeitung/test/README.md rename verarbeitung/test/{Processing_unittest.py => construct_graph_unittest.py} (57%) delete mode 100644 verarbeitung/test/readme.txt create mode 100644 verarbeitung/test/update_graph_unittest.py create mode 100644 verarbeitung/update_graph/README.md delete mode 100644 verarbeitung/update_graph/readme.txt diff --git a/verarbeitung/.gitignore b/verarbeitung/.gitignore new file mode 100644 index 0000000..d167d87 --- /dev/null +++ b/verarbeitung/.gitignore @@ -0,0 +1,57 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ \ No newline at end of file diff --git a/verarbeitung/README.md b/verarbeitung/README.md new file mode 100644 index 0000000..10640cb --- /dev/null +++ b/verarbeitung/README.md @@ -0,0 +1,61 @@ +# Projekt CiS-Projekt 2021/22 + +Processing-Package to generate theoretical graph for citations and references of given input publications. + +## Usage/Examples + +```python +from verarbeitung.process_main import Processing + + +def main(url_list): + Processing(url_list) +``` + +Grundlegender Prozess: +Es wird von der UI eine Liste an DOIs an die Verarbeitung übergeben und +diese wird dann umgewandelt in eine Knoten-und Kantenmenge, welche die Zitierungen darstellen. +Die Informationen über die Paper und die Zitierungen kommen von der Input Gruppe über den Aufruf +von der Funktion Publication. Die Knoten- und Kantenmengen werden in Form einer +Json Datei an den Output übergeben. + +## Files and functions in directory + + +get_pub_from_input.py: + +```python +def get_pub(pub_doi, test_var) +``` +- Gibt für eine DOI ein Klassenobjekt zurück, in dem alle nötigen Informationen gespeichert sind. + + +process_main.py: + +```python +def Processing(url_list) +``` +- Überprüft, ob bereits eine Json Datei existiert und ruft dann entweder die Funktion auf, um + einen neuen Graphen zu erstellen oder die Funktion um einen Vorhandenen zu updaten. + + +start.script.py: + + - Wird benötigt, um die Dateien ordnerübergreifend aufzurufen. Nur fürs interne Testen der + Funktionalität + + +<name>.json: + +- sind momentan Beispiele, die an den Output übergeben werden könnten. + +## Testing + +python -m unittest discover verarbeitung/test -v + +## Authors +- Donna Löding +- Alina Molkentin +- Xinyi Tang +- Judith Große +- Malte Schokolowski \ No newline at end of file diff --git a/verarbeitung/construct_new_graph/README.md b/verarbeitung/construct_new_graph/README.md new file mode 100644 index 0000000..d73f4eb --- /dev/null +++ b/verarbeitung/construct_new_graph/README.md @@ -0,0 +1,29 @@ +# Projekt CiS-Projekt 2021/22 + +Directory for functions to create the fundamental graph structure at first time call of programm. + +## Files in directory + +initialize_graph.py + +- Führt den grundlegendem Graphbauprozess aus. Die Input-DOIs werden + als Klassenobjekt zur Knotenmenge hinzugefügt und über einen rekursiven Aufruf + wird die angegene Zitierungstiefe in beide Richtungen zu den Kanten hinzugefügt. + + +add_citations_rec.py + +- Die DOIs, die in den Zitierungen des Inputs zu finden sind, werden ebenfalls zu Knoten + und je nach angegebener Höhe oder Tiefe wird dies für weitere Tiefen erneut ausgeführt. + + +export_to_json.py + +- Wandelt die berechnete Knoten- und Kantenmenge in eine Json Datei um. + +## Authors +- Donna Löding +- Alina Molkentin +- Xinyi Tang +- Judith Große +- Malte Schokolowski \ No newline at end of file diff --git a/verarbeitung/construct_new_graph/Processing.py b/verarbeitung/construct_new_graph/initialize_graph.py similarity index 98% rename from verarbeitung/construct_new_graph/Processing.py rename to verarbeitung/construct_new_graph/initialize_graph.py index 0f3dc51..c54880f 100644 --- a/verarbeitung/construct_new_graph/Processing.py +++ b/verarbeitung/construct_new_graph/initialize_graph.py @@ -96,7 +96,7 @@ def complete_inner_edges(): edges.append([node.doi_url,reference.doi_url]) -def process_main(doi_input_list, search_height, search_depth, test_var = False): +def init_graph_construction(doi_input_list, search_height, search_depth, test_var = False): ''' :param doi_input_list: input list of doi from UI :type doi_input_list: List[String] diff --git a/verarbeitung/construct_new_graph/readme.txt b/verarbeitung/construct_new_graph/readme.txt deleted file mode 100644 index 2bc7c23..0000000 --- a/verarbeitung/construct_new_graph/readme.txt +++ /dev/null @@ -1,11 +0,0 @@ -Hier wird die grundlegende Grapfstruktur erstellt bei einem ersten Ausführen unseres gesamten Programms. - -Processing.py - Führt den grundlegendem Graphbauprozess aus. Die Input-DOIs werden - als Klassennobjekt zur Knotenmenge hinzugefügt und über einen rekursiven Aufruf - wird die angegene Anzahl an Zitierungen in beide Richtungen zu den Kanten hinzugefügt. - -add_citations_rec.py - Die DOIs, die in den Zitierungen des Inputs zu finden sind, werden ebenfalls zu Knoten - und je nach angegebener Höhe oder Tiefe wird dies für die Zitierungen der Zitierungen - erneut ausgeführt. - -export_to_json.py - Wandelt die berechnete Knoten- und Kantenmenge in eine Json Datei um. \ No newline at end of file diff --git a/verarbeitung/dev_files/readme.txt b/verarbeitung/dev_files/README.md similarity index 100% rename from verarbeitung/dev_files/readme.txt rename to verarbeitung/dev_files/README.md diff --git a/verarbeitung/dev_files/print_graph_test.py b/verarbeitung/dev_files/print_graph_test.py index 5884a6a..0ce8cd9 100644 --- a/verarbeitung/dev_files/print_graph_test.py +++ b/verarbeitung/dev_files/print_graph_test.py @@ -18,7 +18,7 @@ import sys #sys.path.insert(1, 'C:\Users\Malte\Git\CiS-Projekt\ci-s-projekt-verarbeitung\input') sys.path.append("../../") -from verarbeitung.construct_new_graph.Processing import process_main +from verarbeitung.construct_new_graph.initialize_graph import init_graph_construction from verarbeitung.update_graph.import_from_json import input_from_json from verarbeitung.update_graph.update_graph import check_graph_updates @@ -77,7 +77,7 @@ def try_known_publications(): #arr.append[url] - nodes, edges = process_main(doi_list,2,2) + nodes, edges = init_graph_construction(doi_list,2,2) print_graph(nodes, edges) @@ -87,7 +87,7 @@ def try_delete_nodes(): doi_list = [] doi_list.append('https://pubs.acs.org/doi/10.1021/acs.jcim.9b00249') #doi_list.append('https://pubs.acs.org/doi/10.1021/acs.jcim.1c00203') - nodes, edges = process_main(doi_list,1,1) + nodes, edges = init_graph_construction(doi_list,1,1) #print_simple(nodes, edges) # list_of_nodes_py, list_of_edges_py = input_from_json('json_text.json') diff --git a/verarbeitung/process_main.py b/verarbeitung/process_main.py index e69de29..d1654c9 100644 --- a/verarbeitung/process_main.py +++ b/verarbeitung/process_main.py @@ -0,0 +1,4 @@ + + +def Processing(url): + print(url) \ No newline at end of file diff --git a/verarbeitung/readme.txt b/verarbeitung/readme.txt deleted file mode 100644 index a84da7f..0000000 --- a/verarbeitung/readme.txt +++ /dev/null @@ -1,19 +0,0 @@ -Hauptordner der Verarbeitung. - -Grundlegender Prozess: -Es wird von der UI eine Liste an DOIs an die Verarbeitung übergeben und -diese wird dann umgewandelt in eine Knoten-und Kantenmenge, welche die Zitierungen darstellen. -Die Informationen über die Paper und die Zitierungen kommen von der Input Gruppe über den Aufruf -von der Funktion Publication. Die Knoten- und Kantenmengen werden in Form einer -Json Datei an den Output übergeben. - -get_pub_from_input - Erstellt aus einer DOI ein Klassenobjekt in dem alle nötigen Informationen - gespeichert sind. - -process_main.py - Überprüft, ob bereits eine Json Datei existiert und ruft dann entweder die Funktion auf, um - einen neuen Graphen zu erstellen oder die Funktion um ihn zu updaten. - -start.script.py - Wird benötigt, um die Dateien ordnerübergreifend aufzurufen. - - -Alle Dateien mit mit der Endung .json sind momentan Beispiele, die an den Output übergeben werden könnten. \ No newline at end of file diff --git a/verarbeitung/test/README.md b/verarbeitung/test/README.md new file mode 100644 index 0000000..79afed6 --- /dev/null +++ b/verarbeitung/test/README.md @@ -0,0 +1,27 @@ +# Projekt CiS-Projekt 2021/22 + +Directory to contain unittests for construction and update of publication graph + +## Files in directory + +input_test.py + +- Immitiert die Arbeit der Input Gruppe auf eine sehr einfache Weise. + Beispielhafte Informationen werden aus Strings herausgelesen und als Klassenobjekt gespeichert. + +construct_graph_unittest.py + +- Führt diverse Tests zur Konstruktion des Graphen ohne Vorkenntnisse mit eigenen Beispielen und + unserer Input_test Funktion aus. + +update_graph_unittest.py + +- Führt diverse Tests zum Updaten eines alten Graphs mit aktualisierter Input Liste mit eigenen + Beispielen und unserer Input_test Funktion aus. + +## Authors +- Donna Löding +- Alina Molkentin +- Xinyi Tang +- Judith Große +- Malte Schokolowski \ No newline at end of file diff --git a/verarbeitung/test/Processing_unittest.py b/verarbeitung/test/construct_graph_unittest.py similarity index 57% rename from verarbeitung/test/Processing_unittest.py rename to verarbeitung/test/construct_graph_unittest.py index 35cade7..13f201c 100644 --- a/verarbeitung/test/Processing_unittest.py +++ b/verarbeitung/test/construct_graph_unittest.py @@ -4,21 +4,19 @@ import sys from pathlib import Path sys.path.append("../") -from verarbeitung.construct_new_graph.Processing import process_main -from verarbeitung.update_graph.import_from_json import input_from_json -from verarbeitung.update_graph.update_graph import check_graph_updates +from verarbeitung.construct_new_graph.initialize_graph import init_graph_construction -class ProcessingTest(unittest.TestCase): +class ConstructionTest(unittest.TestCase): maxDiff = None def testCycle(self): - nodes, edges = process_main(['doiz1'],1,1,True) + nodes, edges = init_graph_construction(['doiz1'],1,1,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes, ['doiz1', 'doiz2']) self.assertCountEqual(edges, [['doiz1', 'doiz2'], ['doiz2', 'doiz1']]) - nodes, edges = process_main(['doiz1'],2,2,True) + nodes, edges = init_graph_construction(['doiz1'],2,2,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes, ['doiz1', 'doiz2']) self.assertCountEqual(edges, [['doiz2', 'doiz1'], ['doiz1', 'doiz2']]) @@ -30,79 +28,60 @@ class ProcessingTest(unittest.TestCase): #def testEmptyDepth(self): def testEmptyDepthHeight(self): - nodes, edges = process_main(['doi1'],0,0,True) + nodes, edges = init_graph_construction(['doi1'],0,0,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi1']) self.assertCountEqual(edges, []) - nodes, edges = process_main(['doi1', 'doi2'],0,0,True) + nodes, edges = init_graph_construction(['doi1', 'doi2'],0,0,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes, ['doi1','doi2']) self.assertCountEqual(edges, [['doi1', 'doi2']]) - nodes, edges = process_main(['doi1', 'doi2', 'doi3'],0,0,True) + nodes, edges = init_graph_construction(['doi1', 'doi2', 'doi3'],0,0,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes, ['doi1','doi2', 'doi3']) self.assertCountEqual(edges, [['doi3', 'doi1'], ['doi1', 'doi2']]) def testInnerEdges(self): - nodes, edges = process_main(['doi_ie1'],1,1,True) + nodes, edges = init_graph_construction(['doi_ie1'],1,1,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_ie1','doi_ie2','doi_ie3']) self.assertCountEqual(edges,[['doi_ie1','doi_ie2'],['doi_ie3','doi_ie1'],['doi_ie3','doi_ie2']]) def testRightHeight(self): - nodes, edges = process_main(['doi_h01'],1,0,True) + nodes, edges = init_graph_construction(['doi_h01'],1,0,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_h01']) self.assertCountEqual(edges, []) - nodes, edges = process_main(['doi_h02'],1,0,True) + nodes, edges = init_graph_construction(['doi_h02'],1,0,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_h02','doi_h1']) self.assertCountEqual(edges, [['doi_h1','doi_h02']]) - nodes, edges = process_main(['doi_h02'],2,0,True) + nodes, edges = init_graph_construction(['doi_h02'],2,0,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_h02','doi_h1','doi_h2']) self.assertCountEqual(edges, [['doi_h1','doi_h02'], ['doi_h2','doi_h1']]) def testRightDepth(self): - nodes, edges = process_main(['doi_d01'],0,1,True) + nodes, edges = init_graph_construction(['doi_d01'],0,1,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_d01']) self.assertCountEqual(edges, []) - nodes, edges = process_main(['doi_d02'],0,1,True) + nodes, edges = init_graph_construction(['doi_d02'],0,1,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_d02','doi_d1']) self.assertCountEqual(edges, [['doi_d02','doi_d1']]) - nodes, edges = process_main(['doi_d02'],0,2,True) + nodes, edges = init_graph_construction(['doi_d02'],0,2,True) doi_nodes = keep_only_dois(nodes) self.assertCountEqual(doi_nodes,['doi_d02','doi_d1','doi_d2']) self.assertCountEqual(edges, [['doi_d02','doi_d1'], ['doi_d1','doi_d2']]) - def test_import_from_json(self): - nodes_old, edges_old = process_main(['doi_lg_1_i'],2,2,True) - nodes_new, edges_new = input_from_json('test_output.json') - self.assertCountEqual(nodes_old,nodes_new) - self.assertCountEqual(edges_old, edges_new) - - def test_deleted_input_dois(self): - nodes_old_single, edges_old_single = process_main(['doi_lg_1_i'],2,2,True) - nodes_old_both, edges_old_both = process_main(['doi_lg_1_i','doi_lg_2_i'],2,2,True) - nodes_new_both, edges_new_both = input_from_json('test_output.json') - nodes_new_single, edges_new_single = check_graph_updates(['doi_lg_1_i'], nodes_old_both, edges_old_both, 'test_output.json', 2, 2, True) - self.assertCountEqual(nodes_old_single,nodes_new_single) - self.assertCountEqual(edges_old_single, edges_new_single) - - nodes_old_single, edges_old_single = process_main(['doi_cg_i'],3,3,True) - nodes_old_two, edges_old_two = process_main(['doi_lg_1_i','doi_cg_i'],3,3,True) - nodes_old_three, edges_old_three = process_main(['doi_lg_1_i','doi_lg_2_i','doi_cg_i'],3,3,True) - - diff --git a/verarbeitung/test/readme.txt b/verarbeitung/test/readme.txt deleted file mode 100644 index 7931309..0000000 --- a/verarbeitung/test/readme.txt +++ /dev/null @@ -1,6 +0,0 @@ -In diesem Ordner befinden sich die Unittests. - -input_test.py - Immitiert die Arbeit der Input Gruppe auf eine sehr einfache Weise. - Beispielhafte Informationen werden aus Strings herausgelesen und als Klassenobjekt gespeichert. - -Processing_unittest.py - Führt diverse Tests mit eigenen Beispielen und unserer Input_test Funktion aus. \ No newline at end of file diff --git a/verarbeitung/test/update_graph_unittest.py b/verarbeitung/test/update_graph_unittest.py new file mode 100644 index 0000000..c91efe4 --- /dev/null +++ b/verarbeitung/test/update_graph_unittest.py @@ -0,0 +1,47 @@ +import unittest + +import sys +from pathlib import Path +sys.path.append("../") + +from verarbeitung.construct_new_graph.initialize_graph import init_graph_construction +from verarbeitung.update_graph.import_from_json import input_from_json +from verarbeitung.update_graph.update_graph import check_graph_updates + +class UpdatingTest(unittest.TestCase): + maxDiff = None + + def test_import_from_json(self): + nodes_old, edges_old = init_graph_construction(['doi_lg_1_i'],2,2,True) + nodes_new, edges_new = input_from_json('test_output.json') + self.assertCountEqual(nodes_old,nodes_new) + self.assertCountEqual(edges_old, edges_new) + + def test_deleted_input_dois(self): + nodes_old_single, edges_old_single = init_graph_construction(['doi_lg_1_i'],2,2,True) + nodes_old_both, edges_old_both = init_graph_construction(['doi_lg_1_i','doi_lg_2_i'],2,2,True) + nodes_new_both, edges_new_both = input_from_json('test_output.json') + nodes_new_single, edges_new_single = check_graph_updates(['doi_lg_1_i'], nodes_old_both, edges_old_both, 'test_output.json', 2, 2, True) + self.assertCountEqual(nodes_old_single,nodes_new_single) + self.assertCountEqual(edges_old_single, edges_new_single) + + nodes_old_single, edges_old_single = init_graph_construction(['doi_cg_i'],3,3,True) + nodes_old_two, edges_old_two = init_graph_construction(['doi_lg_1_i','doi_cg_i'],3,3,True) + nodes_old_three, edges_old_three = init_graph_construction(['doi_lg_1_i','doi_lg_2_i','doi_cg_i'],3,3,True) + + +def keep_only_dois(nodes): + ''' + :param nodes: input list of nodes of type Publication + :type nodes: List[Publication] + + gets nodes of type pub and return only their doi + ''' + doi_list = [] + for node in nodes: + doi_list.append(node.doi_url) + return doi_list + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/verarbeitung/update_graph/README.md b/verarbeitung/update_graph/README.md new file mode 100644 index 0000000..f680e99 --- /dev/null +++ b/verarbeitung/update_graph/README.md @@ -0,0 +1,37 @@ +# Projekt CiS-Projekt 2021/22 + +Directory for functions to adjust a publication graph to updated input lists and changed citation/reference depths. For minimal use of the time consuming Input function, a reinterpretation of the exported json file is implemented. + +## Files in directory + +import_from_json.py + +- Stellt die alte Knoten-und Kantenmenge aus der Json Datei wieder her. + +Knoten_Vergleich.py + +- Überprüft welche Knoten neu hinzugekommen sind und welche enfternt wurden. + +Kanten_Vergleich.py + +- Stellt nach der Löschung eines Knotens wieder eine valide Kantenmenge her. + +update_graph_del.py + +- Führt die Löschung eines Knotens durch + +connect_new_input.py + +- Verbindet den alten Graphen aus der Json Datei mit den neuen DOIs zu dem neuen Graphen. + +update_graph.py + +- Überprüft welche Änderungen der Benutzer vorgenommen hat (Löschen oder hinzufügen von DOIs) + und führt diese aus. + +## Authors +- Donna Löding +- Alina Molkentin +- Xinyi Tang +- Judith Große +- Malte Schokolowski \ No newline at end of file diff --git a/verarbeitung/update_graph/connect_new_input.py b/verarbeitung/update_graph/connect_new_input.py index 7b6a538..3f11e59 100644 --- a/verarbeitung/update_graph/connect_new_input.py +++ b/verarbeitung/update_graph/connect_new_input.py @@ -19,7 +19,7 @@ from os import error sys.path.append("../") from .import_from_json import input_from_json -from verarbeitung.construct_new_graph.Processing import initialize_nodes_list, complete_inner_edges +from verarbeitung.construct_new_graph.initialize_graph import initialize_nodes_list, complete_inner_edges from verarbeitung.construct_new_graph.add_citations_rec import add_citations from verarbeitung.construct_new_graph.export_to_json import output_to_json diff --git a/verarbeitung/update_graph/readme.txt b/verarbeitung/update_graph/readme.txt deleted file mode 100644 index 4481b9c..0000000 --- a/verarbeitung/update_graph/readme.txt +++ /dev/null @@ -1,15 +0,0 @@ -Wenn der Benutzer seine Eingabe im Nachhinein ändert, werden hier die Änderungen angewendet. -Um möglichst selten die Funktion für den Input aufzurufen (lange Laufzeit), reinterpretieren wir die Json Datei. - -import_from_json.py - Stellt die alte Knoten-und Kantenmenge aus der Json Datei wieder her. - -Knoten_Vergleich.py - Überprüft welche Knoten neu hinzugekommen sind und welche enfternt wurden. - -Kanten_Vergleich.py - Stellt nach der Löschung eines Knotens wieder eine valide Kantenmenge her. - -update_graph_del.py - Führt die Löschung eines Knotens durch - -connect_new_input.py - Verbindet den alten Graphen aus der Json Datei mit den neuen DOIs zu dem neuen Graphen. - -update_graph.py - Überprüft welche Änderungen der Benutzer vorgenommen hat (Löschen oder hinzufügen von DOIs) - und führt diese aus. \ No newline at end of file -- GitLab