CSV-Dateien für Google Kalender schreiben

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Da man sehr einfach Terminlisten als CSV-Datei für den Import nach Google Kalender (Hintergrund) schreiben kann, habe ich mal - für meine Belange - ein entsprechendes Tool geschrieben. Hintergrund ist, dass ich schon mal häufiger Termine bei Google einpflegen muss und dies bislang händisch bzw. - wenn automatisch - mit einem relativ komplizierten Tool erledigt habe.

Das Tool - rudimentär umgesetzt - läuft und versieht seinen Dienst ohne Murren, allerdings werden nur Ganztagsevents geschrieben (ich brauche nicht mehr).

Code: Alles auswählen

# -*- coding: utf-8 -*-
# TerminSchreiber, um CSV-Dateien fuer den
# Import nach Google Kalender zu schreiben
# Autor:      PixeWakb
#
# Version:    0.1.0-0001
#
# First Edit: 2014-12-30
# Last Edit:  2014-12-30


class Kalender(object):

    name   = ""
    events = []
    
    def __init__(self,name):
        ''' Konstruktor
        '''
        self.name = ""
        self.name = name
        events = []


    def add_event(self,subject,start_date="1.8.2015"):
        '''Fuegt dem Kalender ein Event hinzu
        subject    = String
        start_date = MM/DD/YY
        '''
        # Datum formatieren
        d,m,y = start_date.split(".")
        if len(d) < 2:
            d = "0" + d
        if len(m) < 2:
            m = "0" + m
        y = y[-2:]
        start_date = m + "/" + d + "/" + y
        # Subject formatieren
        if "," in subject:
            subject = '"' + subject + '"'
        # Event aufbauen
        event = []
        event.append(subject)
        event.append(start_date)
        # Event in die Eventsliste
        self.events.append(event)
    

    def save(self):
        '''Schreibt den Kalender in eine
        CSV-Datei, die in Google Kalender
        importiert werden kann
        '''
        kopf = "Subject,Start Date"
        data = [kopf]
        for event in self.events:
            data.append(",".join(event))
        file = "\n".join(data)
        title = self.name + ".csv"
        with open(title,"w") as f:
            f.write(file)


if __name__ == "__main__":
    calendar = Kalender("Testdatei")
    calendar.add_event("Testtermin","2.2.2015")
    calendar.save()
Ich habe den Code nicht aufgeräumt und ich habe nicht das CSV-Modul verwendet. Möglicherweise schaue ich mir letzteres noch einmal an.

Falls jemand Verbesserungsvorschläge - primär zum Design und zur Konstruktor-Methode hat - immer her damit...

PS Wahrscheinlich werde ich mal nach und nach weitere Methoden ergänzen bzw. die vorhanden add_event-Methode erweitern, um mehr "Spalten" unterstützen zu können.
BlackJack

@pixewakb: Also erst einmal ein Fehlerreport: `name` und `events` haben auf der *Klasse* nichts zu suchen, denn mindestens `events` ist falsch. Das gilt so für *alle* `Kalender`-Exemplare, man möchte aber pro Kalender unterschiedliche, unabhängige Events speichern.

Einem Attribut einen Wert zuzuweisen nur um ihm gleich in der nächsten Zeile einen anderen Wert zuzuweisen ist unsinnig.

Der lokale Name `events` in der `__init__()` wird definiert aber niemals benutzt. Ausserdem sollte man diese Methode nicht mit 'Konstruktor' dokumentieren. Der Konstruktor wäre in Python eine `__new__()`-Methode. Die `__init__()` *init*ialisiert ein bereits erzeugtes und als Argument übergebenes Objekt. Das muss man dann auch nicht durch ein Wort ”dokumentieren”.

Datumsangaben sollten in einem entsprechenden Datentyp abgebildet werden (`datetime.date`) und nicht als Zeichenketten manupuliert werden. So ein Datum kann man dann auch leicht als Zeichenkette formatieren mit der `format()`-Methode oder -Funktion und entsprechenden Formatierungsanweisungen beim Platzhalter.

Das `csv`-Modul sollte man dem kaputten manuellem zusammenbasteln von so einer Datei vorziehen. Ist ja schön das Du das ',' berücksichtigst, aber was ist mit Zeilenumbrüchen oder doppelten Anführungszeichen im Betreff des Termins?

Wenn man zwei Werte hat und eine Liste damit erstellen möchte dann erstellt man eine Liste mit diesen zwei Werten. Direkt! Und nicht eine leere Liste auf der man dann zweimal `append()` aufruft. Ähnlich umständlich ist es auch erst einen Wert an einen Namen zu binden der dann *nur* dafür benutzt wird um in der nächsten Zeile eine Liste mit diesem Wert zu erstellen.

Das Umwandeln der Werte würde ich auch erst beim schreiben vornehmen, denn *dafür* wandelt man die ja eigenlich um.

Das liesse sich problemlos mit einer Terminliste und einer Funktion zum Schreiben der CSV-Datei lösen. Die Klasse macht so irgendwie wenig Sinn.
BlackJack

Ums mal einfacher zu schreiben:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import csv
from datetime import date as Date


def save_calender(filename, events):
    with open(filename, 'w') as csv_file:
        writer = csv.writer(csv_file)
        writer.writerow(['Subject', 'Start Date'])
        writer.writerows(
            [subject, format(date, '%m/%d/%Y')] for subject, date in events
        )


def main():
    events = [['Testtermin', Date(2015, 2, 2)], ['Xmas', Date(2015, 12, 24)]]
    save_calender('test.csv', events)


if __name__ == '__main__':
    main()
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Danke fürs Feedback. Ich werde die Punkte abarbeiten und noch einmal bei Weigend nachlesen. Die Lektüre liegt schon etwas zurück. Ich versuche momentan alles objektorientiert umzusetzen, weil ich mich damit (immer noch) kaum auskenne.

Mal eine grundsätzliche Rückfrage: Ich hatte immer den Eindruck, dass objektorientiert besser als funktional ist, weil die Wartung später einfacher sein sollte. Im Kopf bei mir ist eigentlich gespeichert, dass funktional schlecht und nur objektorientiert die richtige Art des Programmierens sei. Das siehst du erkennbar anders!? Gibt es einen grobe Regel aus deiner Sicht, ab wann du etwas objektorientiert umsetzen würdest? Die Erweiterbarkeit deiner Funktion erscheint mir schwierig, wenn man alle Spalten berücksichtigen will?

Zu meiner Situation: Aktuell arbeite ich viel mit Python-Skripten, die ich selbst geschrieben habe und die mir Daten zur Verfügung stellen, mit denen dann wieder andere Tools arbeiten. Ich bin da inzwischen dankbar, dass die Tools - relativ weitgehend - objektorientiert umgesetzt sind, weil ich einiges vor Monaten geschrieben habe und nicht mehr genau weiß, wie ich das mal gecodet habe. Ich habe sogar schon eine Framework-Klasse, die die Klassen (aus den Entwicklungsbereichen) lädt, weil ich mich dann ganz auf meine eigentliche Arbeit konzentrieren kann.
BlackJack

@pixewakb: Objektorientierung ist kein Allheilmittel und vor allem ist es nicht objektorientiert nur weil man es in eine Klasse zwängt. Deine Lösung war ja nicht wirklich objektorientiert weil man sie *einfach* ohne Klasse schreiben konnte. OOP muss da schon einen Mehrwert bieten. Die einzige wirkliche Funktionalität war ja in der `save()`-Methode, beziehungsweise hätte da einiges aus der `add()`-Methode mit heineingehört. Und wenn man eine Klasse hat die ausser der `__init__()` nur eine weitere Methode hat, dann ist das in der Regel eine zur Klasse ”aufgeblasene” Funktion.

Klassen fassen Daten und Funktionen die darauf operieren zu einem Objekt zusammen. Insbesondere bei den Funktionen ist die Mehrzahl wichtig, weil wie gesagt eine Klasse die nur eine Methode hat genausogut als einfache Funktion geschrieben werden kann. Andererseits kann man Klassen auch für das Zusammenfassen von verschiedenen Daten verwenden wenn man keine weiteren Methoden hat. Also immer dann wenn die Attribute veränderbar sein sollen und deshalb kein `collections.namedtuple()` funktioniert.

Insgesamt bietet Python eine Menge funktionale Werkzeuge, also kann das an sich nicht schlecht sein. Allerdings sind die Closures nicht so wie man es von ”reineren” funktionalen Sprachen kennt (auch wenn sich das in Python 3 geändert hat), und deshalb ist das die Grenze an der man spätestens Klassen schreiben sollte, eben weil das von den beiden Alternativen die „pythonischere” ist.

Bei der Erweiterbarkeit der Spalten sehe ich gerade keinen grossen Unterschied zwischen den beiden Lösungen. Es kann aber gut sein das man bei mehr Funktionalität eine Klasse gebrauchen könnte. Eben wenn es anfängt Sinn zu machen sich die zusätzliche Komplexität ins Programm zu holen.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Verständnisfragen:
Klassen fassen Daten und Funktionen die darauf operieren zu einem Objekt zusammen.
Wie genau könnten - bei so einem Kalender - Funktionen aussehen, die auf den Daten operieren? Ich versuche diesen Punkt zu verstehen, bislang ist OO für mich primär eine andere Form Programme zu schreiben, d. h. wahrscheinlich arbeite ich primär noch funktional. Dieses "Mehr" bei der OO ("Daten und Funktionen die darauf operieren") erschließt sich mir noch nicht ganz - befürchte ich.
Bei der Erweiterbarkeit der Spalten sehe ich gerade keinen grossen Unterschied zwischen den beiden Lösungen. Es kann aber gut sein das man bei mehr Funktionalität eine Klasse gebrauchen könnte. Eben wenn es anfängt Sinn zu machen sich die zusätzliche Komplexität ins Programm zu holen.
Bislang habe ich wahrgenommen, dass ich durch OO Komplexität reduziere, ich dokumentiere für mich die Arbeit mit der Klasse und blende dann bei der späteren Arbeit die Implementierung komplett aus, bei der Kalender-Klasse könnte ich in einem größeren Skript m. E. den Kalender auch fortwährend bestücken und müsste nicht nur die Terminliste auf dem neusten Stand halten.

Wenn ich Weigend richtig verstand, dann ist es genau das, was OO so interessant macht, d. h. ich kann die Klasse ändern und anpassen, ohne dass sich an meiner Arbeit mit den Methoden und Eigenschaften der Klasse etwas ändern muss. Das ginge wohl auch mit Funktionen in Modulen, gefühlt habe ich bei OO aber schon Arbeit für die Zukunft erledigt (mein Eindruck).
BlackJack

@pixewakb: Wie das bei so einem Kalender aussehen könnte kommt darauf an was an Daten und Operationen so zusammenkommt.

Arbeitest Du wirklich *funktional*? Bist Du sicher das Du das nicht mit prozedural verwechselst?

Das bei dem Beispiel hier durch die Klasse keine Komplexität reduziert wird sieht man IMHO ganz gut daran dass man das mit einer Liste und einer Funktion einfacher lösen kann.

Bei Funktionen kann man die Implementierung doch auch ausblenden, man muss nur wissen welchen Effekt oder welches Ergebnis der Aufruf hat, genau wie man bei Klassen wissen muss welchen Effekt oder welches Ergebnis die Methodenaufrufe haben.

Wo siehst Du denn den Unterschied ob man auf einer Kalenderklasse eine `add()`-Methode aufruft, oder auf einer Liste `append()` oder meinetwegen auch eine Funktion die Liste und Argumente für einen zusätzlichen Termin übergeben bekommt?

Bei rein funktionaler Programmierung erledigt man auch Arbeit für die Zukunft, beziehungsweise wurde die bei den entsprechenden Programmiersprachen auch schon von den Entwicklern in der Standardbibliothek der jeweiligen Sprache erledigt in dem dort möglichst allgemein verwendbare Bausteine zur Verfügung gestellt werden, mit denen man sich neue Funktionen erstellen kann.

Ähm, klar kannst Du Klassen ändern und anpassen, aber das kannst Du doch mit Funktionen auch machen.

Ich denke ich weiss worauf Du beziehungsweise der Weigend hinaus will, aber das ist IMHO zum grossen Teil OOP-Marketing-Geschwätz ohne Substanz und/oder Sachen die in statisch typisierten Programmiersprachen ”Sinn” machen weil man sich da erzwungenermassen lustige und umfangreiche Typhierarchien mit abstrakten Klassen und vielen Interfaces basteln muss, die dann ganz toll erweiterbar sind. Theoretisch, denn in der Praxis macht man das dann auch eher selten. Und in Python kommt so etwas in umfangreichen Bibliotheken oder Rahmenwerken vor das man mal eine Klasse von einer anderen ableitet und etwas (anders) implementiert, aber in der Regel hat man eine sehr flache Vererbungshierarchie weil man wegen dem „duck typing” nicht gezwungen wird so viel zu erben oder überall Interfaces zu verwenden.

Nichts gegen OOP, ich finde das auch eine tolle Möglichkeit Code und Daten zu strukturieren. Ich verwende gerne Klassen in Python und OOP hat meinen Programmierstil auch in prozeduralen Programmiersprachen nachhaltig beeinflusst. Aber das gilt auch für funktionale Programmierung. Und auch prozedurale Programmierung hat ihre Berechtigung. Python bietet Werkzeuge für all diese Ansätze, die man deshalb IMHO auch verwenden sollte wenn sie sich sinnvoll anwenden lassen. Funktionen unnötig zu Klassen aufzublasen ist IMHO nicht sinnvoll.
Antworten