Ordner Pfad mit csv File abgleichen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

``…/Berichte/2019/Automarke/Bericht_....pdf`` ohne weitere Zwischenverzeichnisse?
Ja genau so sieht es aus. Die Dateistrukturen sind fest vorgegeben.
Da der reports Pfad "C:\daten\Berichte" ist, ist der relative Pfad: PATH_TO_CSV = "..\csv\reports.csv" Ein PDF welches in der selben Ebene liegt wie die Ordner mit der Jahreszahl, sollten nicht mitgenommen werden. Also ist der Code vom Sirius3 nicht optimal, das habe ich Verstanden. Da ich hier an meine Grenzen stoße weiß ich echt nicht wie ich das bewerkstelligen soll? Gibt es eine Möglichkeit in Python mehr auszulesen, also auch in welchen Ordner das File liegt oder wann es dort abgelegt worden sein soll? Warnungen zu Protokollieren ist nie Verkehrt! Da du schreibst dass das einlesen der csv Datei als erstes passieren sollte werde ich deinen Ratschlag folgen.

Code: Alles auswählen

csv_inhalt = pd.read_csv(PATH_TO_CSV, encoding = 'iso8859_15')
    print(csv_inhalt)
Jetzt noch Überprüfen ob eine Zeile doppelt vorkommt und Dokumentieren und in eine passende Datenstruktur speichern.
Für einen ”selbsterklärenden” Quelltext würde ich für einen Datensatz in der CSV-Datei ja einen Datentyp mit `collections.namedtuple()` definieren…
Hier habe ich das gefunden, werde aber daraus nicht schlau http://www.deekras.com/namedtuple.html
Sirius3
User
Beiträge: 18217
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du weißt, dass die PDF-Dateien immer in zwei Ebenen tiefe liegen, dann durchsuch doch einfach nur die. Und natürlich ist dann vielleicht der gesamte Pfad interessant, so dass Du nicht nur den Namen der Datei speichern darfst:

Code: Alles auswählen

report_filenames = [path for path in PATH_TO_REPORTS.glob("*/*/*.pdf")]
bzw. wenn Du Dich nur für Jahreszahl, Automarke, Name interessierst:

Code: Alles auswählen

report_filenames = [path.parts[-3:] for path in PATH_TO_REPORTS.glob("*/*/*.pdf")]
Benutzeravatar
__blackjack__
User
Beiträge: 13925
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@JohannX: Es geht um relative Pfade zu einem Bericht, da hat der Pfad der CSV-Datei nichts mit zu tun. Aber wenn die Struktur so wie beschrieben ist, dann ist der relative Pfadteil ja immer nur ein Verzeichnisname der für eine Automarke steht.

Natürlich kann man auch den gesamten Pfad zu einer Datei ermitteln. Das hast Du ja bereits in dem Code mit `os.walk()` gemacht – um dann den Pfadanteil wieder weg zu werfen, und das macht `Path.rglob()` standardmässig – aber auch da wirft der Code von Sirius3 diese Information dann wieder weg, damit der effektiv das gleiche macht was Dein `os.walk()`-Code gemacht hat (+ einschränken auf PDF-Dateien).

Wenn man sich an der Verzeichnisstruktur orientiert, dann wird das aber ein bisschen mehr Code wenn man die Ebenen selbst explizit verarbeitet. Wie immer beim Programmieren zerlegt man das Problem in kleinere Teilprobleme. Also als erstes geht man die Einträge im Basisverzeichnis für Berichte durch und filtert nach Verzeichnissen deren Name vier Zeichen lang ist und nur aus Ziffern besteht. Für jeden dieser Pfade sucht man dann alle Pfade ab dort der Form Verzeichnisname gefolgt von Namen die mit 'Bericht_' anfangen und mit '.pdf' enden. Das kann man mit der `Path.glob()`-Methode machen. Wenn man paranoid ist, filtert man die noch nach Dateien, nicht das jemand ein Verzeichnis mit so einem Namen angelegt hat und damit das Programm in Probleme stürzt.

Die Metadaten einer Datei aus dem Dateisystem wie das Datum kann man natürlich auch abfragen wenn man einen Pfad zu einer Datei hat.

Pandas würde ich hier ja nicht mit hineinnehmen. Ich sehe da keinen Vorteil den das hat, insbesondere wenn das am Ende sowieso nicht die Datenstruktur ist mit der man arbeiten will/wird.

`namedtupel` hatten wir ja schon mal: Das ist in der Python-Dokumentation beschrieben. Genauso wie `pathlib` und `Path`-Objekte dort beschrieben sind.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Hi zusammen, ein neuer Tag im Paradies :D

Punkto namedtuple hab ich folgendes gefunden:
https://riptutorial.com/de/python/examp ... namedtuple
https://docs.python.org/3/library/collections.html
Demnach müsste das namedtuple für die csv Datei wie gefolgt lauten:

Code: Alles auswählen

csv_liste = namedtuple("csv_liste", "Datum, AutoMarke, ID, Irgendwas, NamePDF, True_False")
Aber er mekert leider beim Code:

Code: Alles auswählen

def read_Csv_File():
    csv_liste = namedtuple("csv_liste", "Datum, AutoMarke, ID, Irgendwas, NamePDF, True_False")
    with open(PATH_TO_CSV, "r", newline = '') as file:
        reader = csv.reader(file, delimiter = ',', quotechar = '"')
        for line in reader:
            csv_liste.append(line)
weil es anscheinend kein append beim csv_liste gibt, laut dem zweiten Link aber sehr wohl?
Sirius3
User
Beiträge: 18217
Registriert: Sonntag 21. Oktober 2012, 17:20

und wo genau hast Du dieses `append` gefunden? Listen haben eine append-Methode, und Listen solltest Du hier auch benutzen. Die einzelnen Einträge der Liste können dann csv_liste-Instanzen sein (besserer Name: CsvEntry).
Und es gilt immer noch, nur weil man eine Variable `csv_liste` nennt, ist es noch lange keine Liste.
Zuletzt geändert von Sirius3 am Montag 12. August 2019, 09:48, insgesamt 1-mal geändert.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Sirius3 hat geschrieben: Montag 12. August 2019, 09:45 und wo genau hast Du dieses `append` gefunden? Listen haben eine append-Methode, und Listen solltest Du hier auch benutzen. Die einzelnen Einträge der Liste können dann namedtuple-Instanzen sein.
Und es gilt immer noch, nur weil man eine Variable `csv_liste` nennt, ist es noch lange keine Liste.
Bitte vergiss meine Frage, es war eine Blöde Frage. :?

Mit diesem Code läuft es nun durch:

Code: Alles auswählen

def read_Csv_File():
    csv_liste = namedtuple("csv_liste", "Datum, AutoMarke, ID, Irgendwas, NamePDF, True_False")
    with open(PATH_TO_CSV, "r", newline = '') as file:
        reader = csv.reader(file, delimiter = ',', quotechar = '"')
        for line in reader:
        print(line)
        new_data = csv_liste(Datum = line[0], AutoMarke = line[1], ID = line[2], Irgendwas = [3], NamePDF = [4], True_False = [5])

        print(new_data)
Aber mir fehlt hier etwas, das ist die Ausgabe:

Code: Alles auswählen

['2019-07-29', 'GM', '0281Y09EEE', 'ISO_EL', 'Testreport_0281Y09EEE.pdf', 'False']
csv_liste(Datum='2019-07-29', AutoMarke='GM', ID='0281Y09EEE', Irgendwas=[3], NamePDF=[4], True_False=[5])
Warum nimmt er 3, 4 und 5 nicht?
Benutzeravatar
sparrow
User
Beiträge: 4506
Registriert: Freitag 17. April 2009, 10:28

Nimmt er doch. Wie du in der Zeile geschrieben hast. [3], [4] und [5].
Wolltest du die Elemente aus "line", wi ebei den ersten 3 Einträgen, hättest du das doch geschrieben ;)
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Bei dem print(line) gibt wird alles ausgegeben, das funktioniert auch, aber bei print(new_data) fehlen jedoch die 3 Einträge.
Oder nicht?
Sirius3
User
Beiträge: 18217
Registriert: Sonntag 21. Oktober 2012, 17:20

Da fehlt nichts, das wird so ausgegeben, wie DU das beim Erzeugen des NamedTuple geschrieben hast.
Um solche Fehler zu vermeiden, wäre die einfachere Schreibweise fehlerunanfälliger:

Code: Alles auswählen

new_data = csv_liste(*line)
Aaarg, dieser Klassenname ist einfach schrecklich falsch.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Aaarg, dieser Klassenname ist einfach schrecklich falsch.
:shock:

Jetzt muss ich noch kontrollieren ob es gleiche Zeilen gibt, was eigentlich nicht passieren sollte.
Dann die PDF´s samt Pfad in ein Namedzuple speichern und anschließend diese beiden kontrollieren ob in der csv Datei ein Eintrag fehlt laut den PDF´s und speichern
Sollte so Richtig sein oder?
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

Jetzt habe ich 2 Namedtuples

Code: Alles auswählen

csv_nt = namedtuple("csv_nt", "Datum, AutoMarke, ID, Irgendwas, NamePDF, True_False")
reports_nt = namedtuple("reports_nt", "Jahr, Automarke, NamePDF")
Ich möchte kontrollieren ob die NamePDF´s welche in reports_nt sind auch in csv_nt vorhanden sind.

Code: Alles auswählen

fehlt = set(reports_nt[2]) - set(csv_nt[4])
    print(fehlt)
Das will er aber nicht so haben, dachte mit set kann man dies kontrollieren?

Code: Alles auswählen

Traceback (most recent call last):
  File "reporter.py", line 42, in <module>
    matching()
  File "reporter.py", line 36, in matching
    fehlt = set(reports_nt[2]) - set(csv_nt[4])
TypeError: 'type' object is not subscriptable
Sirius3
User
Beiträge: 18217
Registriert: Sonntag 21. Oktober 2012, 17:20

Jetzt verwechselst Du wieder die Klassendefinitionen csv_nt/reports_nt (die man deshalb besser wie Klassennamen schreibt: CsvEntry, ReportsEntry) mit Instanzen davon.
Außerdem macht es wenig Sinn, aus einem Eintrag ein Set zu erstellen. Wie schon öfters hier geschrieben, mußt Du Listen benutzen.
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

So funktioniert das:

Code: Alles auswählen

CsvEntry = []
ReportsEntry = []

def read_csv_File():
    with open(PATH_TO_CSV, "r", newline = '') as file:
        reader = csv.reader(file, delimiter = ',')
        for line in reader:
            CsvEntry.append(line)

def read_path_File():
    report_filenames = [path.parts[-3:] for path in PATH_TO_REPORTS.glob("*/*/*.pdf")]
    for line in report_filenames:
        ReportsEntry.append(line)
    
def matching():
    fehlt = set(ReportsEntry[2]) - set(CsvEntry[4])
    print(fehlt)
:D
Benutzeravatar
snafu
User
Beiträge: 6831
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mit globalen veränderbaren Objekten muss man extrem aufpassen. Und du hast gleich zwei davon. Ganz ungünstig ist es, wenn Funktionen, die auf solchen Objekten arbeiten, einen bestimmten Zustand erwarten. Bei dir muss man vorher zwei Funktionen aufrufen, damit eine Dritte überhaupt funktioniert. Mag in deinem Fall ausreichen, ist aber kein guter Programmierstil. Und wenn du in 3 Monaten vergessen hast, welche Voraussetzungen erfüllt sein müssen (Aufruf besagter Funktionen), dann wirst du auch erstmal dumm gucken...
Benutzeravatar
sparrow
User
Beiträge: 4506
Registriert: Freitag 17. April 2009, 10:28

Vor allem, weil es total unnötig ist. Da wird rigoros alles ignoriert, was Funktionen aus macht.
Das Schlimme: Nicht er er wird sich in 3 Monaten am Kopf kratzen, weil er er ja Praktikant ist.
Benutzeravatar
snafu
User
Beiträge: 6831
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das Zauberwort heißt return und ist essentiell beim Programmieren. Davon gehen die Voraussetzungen zwar nicht weg, aber man sieht viel deutlicher, was zu tun ist und kann das Verhalten besser nachvollziehen, wenn man als "Fremder" den Code ausführt.
Sirius3
User
Beiträge: 18217
Registriert: Sonntag 21. Oktober 2012, 17:20

Jetzt hast Du nicht die NamedTuple-Klasse CsvEntry genannt, sondern eine globale Liste. Da wäre der alte Name csv_liste doch besser, oder?
Man macht aber keine globalen Variablen, hier kannst Du doch ganz einfach eine Lokale Liste erzeugen, die Du per `return` zurückgibst und an matching als Parameter übergibst.
Du mußt übrigens in jedem Element der Liste den zwieten Eintrag in ein Set stecken. So macht matching im Moment nicht das, was Du möchtest.
Benutzeravatar
kbr
User
Beiträge: 1501
Registriert: Mittwoch 15. Oktober 2008, 09:27

@JohannX: was befindet sich denn in ReportsEntry[2] und in CsvEntry[4]? Und was sagt die anschließend erstellte Differenzmenge aus?
Benutzeravatar
__blackjack__
User
Beiträge: 13925
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Besonders erheiternd ist ja bei den globalen Listen, dass das im ersten Beitrag mal mit Threads angefangen hat. 😎
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
JohannX
User
Beiträge: 110
Registriert: Mittwoch 27. März 2019, 17:07

__blackjack__ hat geschrieben: Montag 12. August 2019, 22:20 Besonders erheiternd ist ja bei den globalen Listen, dass das im ersten Beitrag mal mit Threads angefangen hat. 😎
:roll:
@JohannX: was befindet sich denn in ReportsEntry[2] und in CsvEntry[4]? Und was sagt die anschließend erstellte Differenzmenge aus?
CsvEntry[4] -> ['Datum', 'Automarke', 'InterneID', 'Emission', 'Prüfbericht_InterneID.pdf', 'False']
ReportsEntry[2] -> ('Datum', 'Automarke', 'Prüfbericht_InterneID.pdf')

Code: Alles auswählen

def matching():
        fehlt = set(ReportsEntry[2]) - set(CsvEntry[4])
        print(fehlt)
Damit gebe ich aus, welche Einträge in der CSV fehlen, jedoch nur das erste Element. Müsste ich dann quasi in einer for-schleife machen oder? damit er zeile für zeile durchgeht?
Antworten