XML Dokument for-Schleife

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.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Ich habe es leider immer noch nicht geschafft, eine anständige Schleife zu erstellen, die mir die Informationen aus dem XML-Dokument holt. Ich brauche eine Schleife, die zuerst schaut, wie oft der tag "Tag" vorhanden ist. Pro Tag muss sie schauen, wie viele Einträge mit dem taginhalt "10" vorhanden sind (also wie oft der tag "klasse" vorhanden ist). Die Informationen in den tags müssen gespeichert werden.

Leider schaffe ich es nicht, dies zu realisieren.
BlackJack

@_nohtyp_: Warum musst Du überhaupt erst die Anzahl ermitteln? Wäre es nicht einfacher über als <Tag>-Elemente zu iterieren, dort das <klasse>-Element zu suchen und falls dessen Text '10' ist, die Daten daraus zu extrahieren und zum Beispiel an eine Liste anzuhängen?

Zeig doch mal ein tatsächliches Beispiel-Dokument, also die tatsächliche Struktur. Bisher hast Du immer nur gekürzte Ausschnitte gezeigt bei denen die Dir wichtigen Tags zu sehen waren, aber wo man nie die Dokumentstruktur als ganzes sehen konnte.
BlackJack

Okay, ich habe jetzt von _nohtyp_ ein Beispiel per PN bekommen weil die Daten nicht öffentlich sind. Es scheint sich um eine Datei zu handeln die mit der Software svPlan erstellt wurde. Davon findet man im Netz zwar einige die öffentlich einsehbar sind, aber die decken immer nur *einen* Tag ab. In der Datei von _nohtyp_ ist das was in denen die man dort findet direkt im <vp>-Element steht mehrfach vorhanden und in <tag>-Tags verpackt.

Was zu folgendem Quelltext führt um die Tage in eine Datenstruktur zu überführen:

Code: Alles auswählen

#!/usr/bin/env python
from datetime import datetime as DateTime
from pprint import pprint
from lxml import etree


def parse_action(action_node):
    return dict((node.tag, node.text) for node in action_node)


def parse_day(day_node):
    date = DateTime.strptime(
        day_node.find('kopf/datum').text, '%d.%m.%Y, %H:%M'
    )
    return (date, map(parse_action, day_node.findall('haupt/aktion')))


def parse_change_document(filename_or_url):
    root_node = etree.parse(filename_or_url).getroot()
    return map(parse_day, root_node.findall('tag'))


def main():
    data = parse_change_document('test.xml')
    pprint(data)


if __name__ == '__main__':
    main()
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Jetzt hatte ich schon an sowas gearbeitet:

Code: Alles auswählen

for x in vp.findall("./tag/haupt/aktion"):
        if x.findtext("klasse") == '10rft':
            infos[x] = None
            klasse = []
            for i in infos:
                stunde = i.findtext("stunde")
                klasse.append(stunde)
                fach = i.findtext("fach")
                klasse.append(fach)
                lehrer = i.findtext("lehrer")
                klasse.append(lehrer)
                raum = i.findtext("raum")
                klasse.append(raum)
                info = i.findtext("info")
                klasse.append(info)
                infos[i] = klasse
Das wäre mir am liebsten: infos = {tag1 : [stunde, fach, ...], [stunde, fach, ...], tag2: [stunde, fach ...}
Wie mach ich das?
Und wie kann ich in deinem Code nach einer bestimmten Klasse suchen?
BlackJack

@_nohtyp_: Das ist eigentlich recht einfach nach einer Klasse zu filtern. Man muss aus der vorhandenen Liste einfach nur eine neue erstellen in der das Datum übernommen wird und für die Aktionen eine neue Liste erstellt wird nur mit denen wo der Klassenname passt. Einfachste Variante:

Code: Alles auswählen

def days_filtered_by_class(days, class_name):
    return [
        (date, [action for action in actions if action['klasse'] == class_name])
        for date, actions in days
    ]
Man kann es natürlich etwas aufwändiger machen wenn man in der Funktion auch gleich Tage aussortieren möchte bei denen dabei keine Aktionen mehr übrig bleiben.

Aus der äusseren Liste ein Wörterbuch zu machen ist nicht weiter schwer, `dict()` nimmt auch ein iterierbares Objekt das (Schlüssel, Wert)-Paare liefert als Argument. Aber von Listen in denen der Index bestimmt welche Bedeutung der Wert hat, würde ich abraten. Das wird schnell unübersichtlich und man hat den Quelltext voller magischer Zahlen statt sprechender Bezeichner. Da würde ich bei Wörterbüchern bleiben oder einen Typ mit `collections.namedtuple()` erstellen.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Wie muss man den Paramter days übergeben?
BlackJack

@_nohtyp_: Beim Aufruf. ;-) Da übergibt man das Ergebnis von `parse_change_document()`. Also noch mal die dazugehörige `main()` von meinem Testprogramm:

Code: Alles auswählen

def main():
    data = parse_change_document('test.xml')
    pprint(data)
    filtered_data = days_filtered_by_class(data, '10rft')
    pprint(filtered_data)
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

GENIAL!
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Jetzt müsste ich nur noch wissen, wie ich aus dem Endergebnis meine Kalendereinräge erstelle.

Mit:

Code: Alles auswählen

def neuesEreignisErstellen(calendar_service, title, content, where, start_time, end_time=None):
    event = gdata.calendar.CalendarEventEntry()
    event.title = atom.Title(text=title)
    event.content = atom.Content(text=content)
    event.where.append(gdata.calendar.Where(value_string=where))

    # if end_time is None:
    #	end_time = time.strptime(start_time) + datetime.timedelta(minutes=45)

    event.when.append(gdata.calendar.When(
        start_time=start_time, end_time=end_time))

    neues_ereignis = calendar_service.InsertEvent(
        event, '/calendar/feeds/default/private/full')
Wie kann ich die Parameter jetzt am sinnvollsten übergeben? Und wie sage ich, dass z.B. heute mehrere neue Ereignisse eingetragen werden müssen?
BlackJack

@_nohtyp_: Programmieren besteht zu einen grossen Anteil darin Probleme in kleinere Teilprobleme zu zerlegen und die dann wieder zu zerlegen, solange bis man eine Funktion oder Methode schreiben kann, die so ein Teilproblem löst. Und aus den ganzen Teillösungen setzt sich am Ende das Programm zusammen.

Wenn man also zum Beispiel eine Lösung (Funktion) hat die *eine* Aktion in den Kalender überträgt, dann benatwortet sich Deine letzte Frage ganz einfach damit, dass man diese Funktion in einer Schleife für alle Aktionen eines Tages aufruft. Und so einen kompletter Vertretungsplan in den Kalender einzutragen ist eine Funktion welche die Funktion zum Eintragen eines Tages in einer Schleife aufruft und jeweils den Tag, also das Datum und alle Aktionen für den Tag, übergibt.

Die beiden höheren Ebenen, Vertretungsplan eintragen und einzelnen Tag eintragen sind also relativ trivial, weil sie einfach nur in einer Schleife jeweils eine speziellere Funktion aufrufen. Um eine Aktion einzutragen musst Du jetzt überlegen wie Du aus einem Datum und einem Wörterbuch mit den Daten aus einer Aktion einen Kalendereintrag erstellen möchtest. Da musst Du entscheiden wie aus zum Beispiel folgenden Daten:

Code: Alles auswählen

{'fach': 'KU',
 'info': 'statt Di (28.01.) St.7, EN NEUM gehalten am Mi (29.01.) St.6',
 'klasse': '10rft',
 'lehrer': 'SCHS',
 'raum': '502',
 'stunde': '2'}
Der Titel und der Inhalt des Kalendereintrags erstellt werden soll. Das „wo” ist relativ einfach — da kann man wahrscheinlich den 'raum' einfach übernehmen.

Für Start- und Endzeit musst Du die 'stunde' irgendwie auf tatsächliche Uhrzeiten (`datetime.time`) abbilden und die mit dem Datum, das der Funktion auch übergeben werden muss, kombinieren.

Eventuell muss man sich Gedanken machen ob und wie man ausgefallene Stunden anders behandeln möchte als Vertretungsstunden.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Ich weiß nicht weiter. Ich habe:

Code: Alles auswählen

        for i in vertretung:
            if i == "fach":
                title = i
            if i == "raum":
                where = i
            if i == "stunde":
                stunde = i
                zeit = kalenderzeitenErstellen(int(stunde))
                stunde, minute = zeit
            start_time = datetime.datetime(2014, 2, 4, stunde, minute).isoformat()
            neuesEreignisErstellen(account, title, content, where, start_time, end_time=None)
und

Code: Alles auswählen

def kalenderzeitenErstellen(stunde):
    zeiten = {1: (7, 45), 2: (8, 40), 3: (9, 45), 4:
              (10, 40), 5: (11, 35), 6: (12, 50), 7: (13, 45)}

    if stunde in zeiten:
        return zeiten[stunde]

def monatUndTagSuchen(datum):
    wochentag, tag, monat, jahr, woche = datum.split()

    monate = {'Januar': 1, 'Februar': 2, 'Maerz': 3, 'April': 4, 'Mai': 5}
    wochentage = {'Montag,': 1, 'Dienstag,': 2,
                  'Mittwoch,': 3, 'Donnerstag,': 4, 'Freitag,': 5}

    if monat in monate and wochentag in wochentage:
        monat = monate[monat]
        wochentag = wochentage[wochentag]

    datum = [monat, tag]
    return datum
Wie kann ich das machen? Ich kann nicht auf die Informationen zugreifen.
BlackJack

@_nohtyp_: Was ist denn `vertretung`? Ein Wörterbuch was den Inhalt von einem <aktion>-Element enthält? Was hast Du Dir denn bei der Schleife gedacht? Soll wirklich für *jedes* Element in `vertretung` ein Eintrag im Kalender erstellt werden? Das macht die Schleife ja. Irgendwie hakt es an absoluten Grundlagen und der Code sieht nach wild raten aus.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Ich bin auch Anfänger. Ich wollte aber gleich mit einem Projekt beginnen und nicht erst tagelang lesen. :D
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

An den Grundlagen führt kein Weg vorbei.
BlackJack

@_nohtyp_: Man kommt halt mit einem Projekt nicht weiter wenn man die Grunddatentypen nicht kennt. Ein Grundlagentutorial sollte man also schon durchgearbeitet haben bevor man mit einem Projekt anfängt, denn sonst weiss man ja gar nicht wie man die Probleme die einem im Projekt über den Weg laufen, lösen soll. Ohne Listen und Wörterbücher (`dict`) zu kennen und welche Operationen und Methoden diese besitzen kann man kein Programm schreiben was auch nur etwas komplexer ist. Die kommen in so ziemlich jedem Programm vor.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Es wäre einfacher, wenn ich das hier erstmal auf das Dictionary reduzieren könnte:

Code: Alles auswählen

[(datetime.datetime(2014, 2, 4, 7, 20), [{'lehrer': 'SCHS', 'info': u'verlegt von St.7, GEO H\xdcBI verlegt nach St.7', 'fach': 'KU', 'stunde': '2', 'klasse': '10rft', 'raum': '502'}, {'lehrer': u'H\xdcBI', 'info': 'verlegt von St.2, KU SCHS verlegt nach St.2', 'fach': 'GEO', 'stunde': '7', 'klasse': '10rft', 'raum': '703'}]), (datetime.datetime(2014, 2, 4, 7, 35), [])]
BlackJack

@_nohtyp_: *Das* Dictionary? Da sind zwei in der Datenstruktur. Du kannst zum testen der Funktion ja erst einmal eines der beiden manuell an die Funktion zum Eintragen von einer Aktion übergeben.

Ansonsten sind das wieder ganz einfache Grundlagen:

Code: Alles auswählen

def add_days_to_google_calendar(days):
    for date, actions in days:
        for action in actions:
            add_action_to_google_calendar(date, action)
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

was ist days?
BlackJack

@_nohtyp_: Immer noch die Daten die beim Parsen von meinen Funktionen erstellt werden.
_nohtyp_
User
Beiträge: 127
Registriert: Mittwoch 8. Januar 2014, 15:38

Klappt bei mir nicht.
Antworten