Dictionary um eine Schlüsselebene erweitern

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.
Antworten
rhea90
User
Beiträge: 4
Registriert: Montag 11. September 2017, 14:55

Hallo Forum,

ich arbeite derzeit privat an einem Projekt welches mir die freien Räume an meiner Hochschule anzeigen soll.
Bis vor kurzem war das nämlich gar nicht möglich, mittlerweile, seitdem ich das angefangen habe geht es zwar aber dennoch ist meins etwas besser.
Auch wenn das ganze relativ komplex aussieht bin ich wirklich Python-Anfänger aber nicht komplett unerfahren im Programmieren allgemein, ich hoffe ihr könnt mir bei dem ganzen unter die Arme greifen. Das ganze ist keine(!) Hausaufgabe oder sonst was, ich mache das aus freien Stücken weil mich das genervt hatte wie es bisher war ^^.

Jedenfalls lade ich mir alle ics-Kalenderdateien herunter und bereite diese als JSON Dateien auf. Die brauche ich nämlich damit mein Web-Frontend damit arbeiten kann. Bisher habe ich die Dateien so strukturiert, dass sie diesem Schema folgen: Woche_Raum.json was dann so aussieht:
15_01.01.11.json oder 15_01.01.16.json. Jetzt habe ich aber feststellen müssen, dass das so in meinem Frontend nicht funktioniert und ich eher 15.json brauche. Das bedeutet das jede dieser Dateien nun alle Räume enthält. Aussehen müsste das dann so: https://github.com/TonySpegel/free-room ... er/14.json

Also genauso wie vorher nur dass jetzt als übergeordneter Schlüssel noch der Raum hinzukommt und alle Räume zur Woche zusammengefasst sind.

Das ganze Projekt befindet sich hier: https://github.com/TonySpegel/free-rooms-py

Bisher ist der Ablauf grob dieser:
  • 1. Website mit BS4 parsen und ICS-Dateien herunterladen (Kalendardateien)
    2. Inhalte parsen und nur die Schlüssel speichern die mich interessieren (Beginn, Ende, Beschreibung)
    3. Nach Zeiten aufsteigend sortieren
    4. PRO Datei diese in JSON konvertieren

Ich hatte mir jetzt zwei Ansätze überlegt wie man das lösen könnte:
  • 1. Aus den erzeugten Dateien hier: https://github.com/TonySpegel/free-room ... aster/json die Dateien zu mergen aber selbst dann fehlt mir wieder der übergeordnete Raum als Schlüssel.

    Ein Ansatz den ich hierbei hatte war:

    Code: Alles auswählen

    def merge_files():
        files = [f for f in os.listdir('./json/') if os.path.isfile(os.path.join('./json/', f))]
        print(files)
    
        groups = defaultdict(list)
        for file_name in files:
            week, room = file_name.split('_')
            groups[week].append(file_name)
        return groups
    
    Wobei ich hier wie gesagt nicht weiß wie ich einen weiteren Schlüssel hinzufüge und wie ich mit dem Code weiter mache (das ist ja nur das Mapping dann?)

    Folgender Punkt scheint mir der "richtigere" Weg:

    2. Direkt eine Schlüsselebene hinzufügen aber nicht nach jeder Datei direkt diese verarbeiten
    Und das ist der Punkt an dem ich nicht weiß wie ich vorgehen soll:
    In meiner Funktion build_json_object habe ich jetzt bspw:

    Code: Alles auswählen

    room_top_level_key = '{}.{}.{}'.format(building_number, floor_number, room_number)
    ergänzt und EIGENTLICH brauche ich doch nur ein weiteres append und packe alles da rein? Weiß aber leider nicht wie das an der Stelle geht.
Vielen Dank für eure Hilfe. Wenn ihr wollt können wir auch über andere Messenger schreiben falls es hier nicht ausreicht Dinge zu erklären (die wir dann aber natürlich hier posten).

MfG
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ich verstehe ehrlich gesagt das Problem nicht... Dein JSON besteht doch schon aus verschachtelten Objekten (unter Python: Dicts). Dann kannst du doch nach belieben weitere Ebenen einziehen.

Wenn du alle Rohdaten im Speicher hast und dein Zielstruktur kennst, dann sollte es doch eigentlich so schwierig sein, dass zu erzeugen... Ggf. macht es auch Sinn, die Struktur der Rohdaten im Speicher zu überdenken, damit man daraus besser die Zielstruktur generieren kann.

Gruß, noisefloor
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@rhea90: py-Dateinamen müssen gültige Bezeichner sein, '-' im Namen geht gar nicht. `digest_room_website` sollte kein globalen Variablen verwenden, sondern room_links lokal erzeugen und zurückgeben. Warum lädst Du die Datein als ics herunter und wandelst sie nicht gleich im Speicher in das Format um, wie Du es brauchst? Dann muß man auch nicht / durch - ersetzen und so vielleicht ungewohnte Raumnamen erzeugen.
build_json_object erzeugt doch schon Wörterbücher mit allen Informationen, die Du brauchst. Wo ist da noch das Problem, das Ergebnis nicht sofort in eine Datei zu schreiben, sondern in einem Wörterbuch oder in einer Liste zu sammeln?
In `build_calendar_json`: List-Comprehension ist kein Ersatz für eine normale for-Schleife!
Schreibe richtige Funktionen, die alle Informationen, die sie brauchen, über ihre Argumente bekommen, und nicht über globale Variablen. Die sollten alle weg. Statt in `extract_info_from_ics` die Liste `extracted_info` immer wieder zu leeren, solltest Du für jeden Schleifendurchgang einfach eine neue Liste erstellen.
rhea90
User
Beiträge: 4
Registriert: Montag 11. September 2017, 14:55

Sirius3 hat geschrieben:@rhea90: py-Dateinamen müssen gültige Bezeichner sein, '-' im Namen geht gar nicht. `digest_room_website` sollte kein globalen Variablen verwenden, sondern room_links lokal erzeugen und zurückgeben. Warum lädst Du die Datein als ics herunter und wandelst sie nicht gleich im Speicher in das Format um, wie Du es brauchst? Dann muß man auch nicht / durch - ersetzen und so vielleicht ungewohnte Raumnamen erzeugen.
build_json_object erzeugt doch schon Wörterbücher mit allen Informationen, die Du brauchst. Wo ist da noch das Problem, das Ergebnis nicht sofort in eine Datei zu schreiben, sondern in einem Wörterbuch oder in einer Liste zu sammeln?
In `build_calendar_json`: List-Comprehension ist kein Ersatz für eine normale for-Schleife!
Schreibe richtige Funktionen, die alle Informationen, die sie brauchen, über ihre Argumente bekommen, und nicht über globale Variablen. Die sollten alle weg. Statt in `extract_info_from_ics` die Liste `extracted_info` immer wieder zu leeren, solltest Du für jeden Schleifendurchgang einfach eine neue Liste erstellen.
Danke für die vielen guten Tipps. Ich glaube die Antwort darauf warum ich das meiste so gemacht habe wie ich es tat ist, dass ich keine Ahnung von Python habe und dass das erste ernsthaftere Projekt damit ist. Aber wieso gehen Bindestriche in Dateinamen nicht? Leerzeichen verstehe ich ja noch aber Bindestriche?
rhea90
User
Beiträge: 4
Registriert: Montag 11. September 2017, 14:55

noisefloor hat geschrieben:Hallo,

ich verstehe ehrlich gesagt das Problem nicht... Dein JSON besteht doch schon aus verschachtelten Objekten (unter Python: Dicts). Dann kannst du doch nach belieben weitere Ebenen einziehen.

Wenn du alle Rohdaten im Speicher hast und dein Zielstruktur kennst, dann sollte es doch eigentlich so schwierig sein, dass zu erzeugen... Ggf. macht es auch Sinn, die Struktur der Rohdaten im Speicher zu überdenken, damit man daraus besser die Zielstruktur generieren kann.

Gruß, noisefloor
Hm ich weiß eben nicht so recht, wie ich das ganze was ich habe ein weiteres mal in append nutze, meine Versuche waren von nicht viel Erfolg gekrönt. Eigentlich dachte ich, dass ich den Schlüssel einfach erzeuge und ähnlich wie beim Schlüssels days mittels append anhänge.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Schlüssel kennen kein `append`, `append`ist eine Methode von Listen.

Das schöne bei Python und JSON ist ja, dass die Datenstrukturen sehr ähnlich sind. Eine Python-Liste wird zu einem JSON-Array und ein Python Dict zu einen JSON Objekt. Beides kannst du beliebig verschachteln:

[codebox=pycon file=Unbenannt.txt]>>> my_dict = {}
>>> my_dict['foo'] = 'bar'
>>> my_dict['spam'] = 'egg'
>>> my_dict['sub_dict'] = {'python':'cool', 'php': 'not really...'}
>>> my_dict
{'foo': 'bar', 'sub_dict': {'python': 'cool', 'php': 'not really...'}, 'spam': 'egg'}
>>> my_list = ['foo', 'bar']
>>> my_list.append('spam')
>>> my_list.append(['python', 'php'])
>>> my_list
['foo', 'bar', 'spam', ['python', 'php']]
>>> my_dict['my_list'] = my_list
>>> my_dict
{'my_list': ['foo', 'bar', 'spam', ['python', 'php']], 'foo': 'bar', 'sub_dict': {'python': 'cool', 'php': 'not really...'}, 'spam': 'egg'}
my_list.append(dict([('foo', 'bar')]))
>>> my_list
['foo', 'bar', 'spam', ['python', 'php'], {'foo': 'bar'}]
>>>[/code]

Und das lässt sich dann auch so nach JSON übertragen. Wie du was verschachtelst liegt jetzt an deinen Anforderungen.

Gruß, noisefloor

P.S.: Bitte nicht beim Antworten unsere vollen Post zitieren. Die stehen unmittelbar darüber im Thread, dann ist sowas nicht nötig, sondern macht das ganze nur unübersichtlich.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@rhea90:
Das append() funktioniert in Zusammenhang mit Dicts nur wenn der Wert eine Liste ist. Die Liste wird beim Konstrukt defaultdict(list) automatisch bereitgestellt. Bei einem "normalen" Dict muss man die Liste vorher manuell für den Wert zuweisen.

Um das nochmal deutlicher zu sagen: Bei data['x'].append(y) nutzt man kein spezielles Verhalten des Dicts, sondern man holt sich die Liste mittels data['x'] und weist dieser Liste mit append() dann - sozusagen ohne "Zwischenspeichern" der Liste (d.h. kein Binden an einen Namen) - direkt ein weiteres Element zu. Da Python mit Referenzen arbeitet, handelt es sich bei der Liste, die dur dir so geholt hast, um die selbe Liste, die an dem Dict hängt.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

rhea90 hat geschrieben:wieso gehen Bindestriche in Dateinamen nicht? Leerzeichen verstehe ich ja noch aber Bindestriche?
Weil beim import-Statement der Dateiname ohne das .py am Ende ein gültiger Python-Bezeichner sein muss. Bezeichner müssen mit einem Buchstaben beginnen und dürfen im Weiteren nur Buchstaben, Ziffern und Unterstriche beinhalten.
In specifications, Murphy's Law supersedes Ohm's.
rhea90
User
Beiträge: 4
Registriert: Montag 11. September 2017, 14:55

Ich habe mir die Antwort dass ich richtige Funktionen schreiben und meine Struktur überdenken soll mal zu Herzen genommen und genau das getan. Ich lade die Dateien nicht mehr herunter und arbeite nicht mehr mit deren Dateinamen.

Meine Struktur habe ich jetzt so geändert, dass sie auch den Raumnamen enthält.

Trotzdem habe ich noch Schwierigkeiten es so zu verschachteln wie ich will.

Folgendes ist keine Variable in meinem Code aber das Ergebnis einer Funktion, der Einfachheit halber lasse ich das aber mal weg:

Code: Alles auswählen

WEEK_INDEX = 0
DATE_INDEX = 1
DAY_INDEX = 2
BEGIN_INDEX = 3
END_INDEX = 4
ROOM_NAME_INDEX = 5
SUMMARY_INDEX = 6

parsed_data = [
    [15, '20170413', 'Thursday', '13:30', '16:30', '04.00.15/FT', 'summary text'],
    [16, '20170419', 'Wednesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [17, '20170427', 'Thursday', '13:30', '16:30', '04.00.15/FT', 'summary text'],
    [17, '20170428', 'Friday', '09:30', '12:30', '04.00.15/FT', 'summary text'],
    [18, '20170503', 'Wednesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [19, '20170511', 'Thursday', '13:30', '16:30', '04.00.15/FT', 'summary text'],
    [19, '20170512', 'Friday', '09:30', '12:30', '04.00.15/FT', 'summary text'],
    [20, '20170515', 'Monday', '08:00', '12:00', '01.-1.13(SZB)', 'summary text'],
    [20, '20170515', 'Monday', '15:15', '18:15', '04.00.15/FT', 'summary text'],
    [20, '20170516', 'Tuesday', '15:15', '17:15', '01.-1.13(SZB)', 'summary text'],
    [20, '20170517', 'Wednesday', '08:00', '11:00', '01.-1.13(SZB)', 'summary text'],
    [20, '20170517', 'Wednesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [20, '20170517', 'Wednesday', '13:30', '16:30', '04.00.15/FT', 'summary text'],
    [20, '20170519', 'Friday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [21, '20170522', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [21, '20170522', 'Monday', '12:00', '15:00', '04.00.15/FT', 'summary text'],
    [22, '20170529', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [22, '20170531', 'Wednesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [22, '20170531', 'Wednesday', '15:15', '18:15', '04.00.15/FT', 'summary text'],
    [22, '20170601', 'Thursday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [22, '20170602', 'Friday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [23, '20170608', 'Thursday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [24, '20170612', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [24, '20170612', 'Monday', '15:15', '18:15', '01.-1.13(SZB)', 'summary text'],
    [24, '20170613', 'Tuesday', '08:00', '11:00', '01.-1.13(SZB)', 'summary text'],
    [24, '20170613', 'Tuesday', '15:15', '18:15', '04.00.15/FT', 'summary text'],
    [24, '20170614', 'Wednesday', '08:00', '11:00', '01.-1.13(SZB)', 'summary text'],
    [24, '20170614', 'Wednesday', '15:15', '18:15', '04.00.15/FT', 'summary text'],
    [24, '20170615', 'Thursday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [25, '20170619', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [25, '20170619', 'Monday', '12:00', '15:00', '04.00.15/FT', 'summary text'],
    [25, '20170620', 'Tuesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [25, '20170621', 'Wednesday', '13:30', '16:30', '04.00.15/FT', 'summary text'],
    [25, '20170622', 'Thursday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [26, '20170626', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [26, '20170627', 'Tuesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [26, '20170628', 'Wednesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [26, '20170628', 'Wednesday', '15:15', '18:15', '04.00.15/FT', 'summary text'],
    [26, '20170629', 'Thursday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [27, '20170703', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [27, '20170704', 'Tuesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [28, '20170710', 'Monday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [28, '20170711', 'Tuesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [28, '20170712', 'Wednesday', '08:00', '11:00', '04.00.15/FT', 'summary text'],
    [28, '20170712', 'Wednesday', '13:30', '16:30', '04.00.15/FT', 'summary text'],
    [28, '20170713', 'Thursday', '15:15', '18:15', '04.00.15/FT', 'summary text']
]

Code: Alles auswählen

def create_dictionary(data):
    grouped_by_week = defaultdict(list)
    grouped_by_room = defaultdict(list)

    result = []

    for entry in data:
        grouped_by_week[entry[WEEK_INDEX]].append(entry)

    for room in data:
        grouped_by_room[room[ROOM_NAME_INDEX]].append(room)

    for week, week_entries in grouped_by_week.items():
        grouped_by_day = defaultdict(list)

        for entry in week_entries:
            grouped_by_day[entry[DAY_INDEX]].append({
                'begin': entry[BEGIN_INDEX],
                'end': entry[END_INDEX],
                'summary': entry[SUMMARY_INDEX]
            })

        result.append({
            'cw': str(week),
            'days': grouped_by_day,
        })

    return result


for dx in create_dictionary(parsed_data):
    print(json.dumps(dx, indent=4))
Nach meiner Logik gruppiere ich zunächst nach der Woche in Index 0 dann nach dem Raumnamen in Index 5 und dann nach dem Tag in Index 3.
Aber irgendwie versteh ich das mit dem verschachteln nicht so ganz, was mach ich denn da falsch?
Antworten