Alexey Schröder

immer bereit!

Google Kalender

Im Fach "Projekt" beim Prof. Dr. rer. nat. Hans-Christian Rodrian haben wir für eine Firma ein Konzept für Synchronisation einer Datenbank bzw. MS Outlook mit Google Kalender entwiekelt. In diesem Projekt war ich für Arbeit mit dem Kalender zuständig. Im folgendem ist Information zu finden, die für Arbeit mit Google kalender benötig ist.

Der Projekt wurde in C# für Platform .NET geschrieben, alle folgende Quelltextbeispile sind in C#.

Weboberflache.

Die Felde des Kalenders(Weboberfläche):

Webobefläche

API.

Jeder Termin im Calender wird durch Instanz der Klasse EventEntry representiert.
Folgende Tabelle  zeigt Zusammengang zwischen der Klasse und Oberfläche des Kalenders:

Feld: Bedeutung:
Title.Text Entspricht dem Feld “Was” im Termin
Times[0].StartTime Der Anfang des Termins
Times[0].EndTime Das Ende des Termins
Content.Content Entspricht dem Feld „Beschreibung“ im Termin
Locations[0].ValueString Entspricht dem Feld „Wo“ im Termin
Times[0].AllDay Ganztägig

Außerdem sind folgende Felder der Klasse von Bedeutung:

Feld: Bedeutung:
Published Die Zeit der Erstellung des Termins
Updated Die Zeit der letzten Änderung des Termins
EventId Eindeutiger ID des Termins im Kalender

 

Datenbank.

Um eine Synchronisation durchzufuhren braucht man entfernte, neue und geänderte Termine zu ermitteln. Dafür ist ein altes Zustand (eine Kopie) des Kalenders nötig. Diese Kopie speichern wir in einer Datenbank. Die Tabelle, wo die Kopie gespeichert wird, kann z.B. so aussehen:

Tabelle "GOOGLEKOPIE":

Column Name Data Type Nullable Primary Key
TERMINIDINDB VARCHAR No Yes
TERMINIDINGOOGLE VARCHAR No No
BESCHREIBUNG VARCHAR Yes No
ANFANGDATUM DATE Yes No
ENDEDATUM DATE Yes No
TITL VARCHAR Yes No

Die Felder der Tabelle:
TERMINIDINDB: besteht aus Datum der Erzeugung des Termins und ID des Termins:
terminIdInDB = termin.Published + " " + termin.EventId;
MANDANTID: ID eines Klients in Datenbank, der Besitzer des Kalenders ist.
TERMINIDINGOOGLE: ID eines Termins im Kalender:  termin.EventId.
BESCHREIBUNG: Entspricht dem Feld „Beschreibung“ im Termin: termin.Content.Content.
ANFANGDATUM: Der Anfang des Termins: Times[0].StartTime.
ENDEDATUM: Das Ende des Termins: termin.Times[0].EndTime
TITLE: Entspricht dem Feld “Was” im Termin: termin.Title.Text

Um geänderte Termine zu finden, braucht man noch die Zeit der letzten Synchronisation. Dafür ist eine Tabelle mit einem einziegem Feld vorgesehen.

Tabelle "SYNCINFOTABLE":

Column Name Data Type Nullable Primary Key
LASTSYNC DATE No Yes

 

Besonderheit.

Um Kopie eines Kalenders mit Kalender zu vergleichen, braucht man die Daten aus Datenbank in ein Objekt umzuwandeln, der mit einem Objekt der Klasse "EventEntry" verglichen werden kann. Die einfachste Lösung wäre eine neue Instanz der Klasse "EventEntry" zu machen und entsprechende Felder zu initialisieren, z.B. mit Hilfe der Methode:

  1. public EventEntry erzeugeTermin(String title, DateTime anfang, DateTime ende)
  2.     {
  3.  
  4.       EventEntry res = new EventEntry (title);
  5.       When eventTime = new When();
  6.       eventTime.StartTime = anfang;
  7.       eventTime.EndTime = ende;
  8.       res.Times.Add(eventTime);
  9.       return res;
  10.    

Das Problem ist, dass der Feld "EventId" read-only ist und dementsprechend "per Hand" nicht initialisirt werden kann. Deswegen braucht man eine Klasse, die die Klasse   "EventEntry" erweitert und den Feld "EventId" irgendwie ersetzt , z.B. die Klasse "EventEntryInner":

  1.  class EventEntryInner:EventEntry
  2.   {
  3.     private new String EventId;   
  4.  
  5.     public String GetEventId()
  6.     {
  7.       return EventId;
  8.     }
  9.  
  10.     public void SetEventId(string value)
  11.     {
  12.       this.EventId = value;
  13.     }
  14.    
  15.   }

Arbeit mit Kalender.

Um Zugang zum Google Calender zu bekommen, braucht man eine Instanz von der Klasse CalendarService, die z.B. so zu erhalten ist:
CalendarService service = new CalendarService("GoogleCalender");


Der Objekt "servise" erstellt Verknupfung mit einem Calender dürch:
service.setUserCredentials(userName, password);
wo "username" und "password" die Zugangdaten sind, die Benutzer beim Erstellen des Calenders angibt.

Der Objekt "service" stellt zur Verfugung folgende wichtige Methoden:
Get(Uri uri) und insert(Uri uri,EventEntry event).
Methode Get(Uri uri) gibt zurück Link auf Objekt der Klasse EventEntry, der unter Uri "uri" im Kalender gespeichert ist.
Methode insert(Uri uri,EventEntry event)  fugt in Kalender einen Termin mit Eigenschaften des Objekts "event"

Falls aber eine Menge von Terminen eingetragen soll, bring die Methode "insert" kein gutes Ergebniss, da es zu grosser Verzögerung kommt. Als eine Alternative kann man folgende Methode benutzen:

  1.  public List<EventEntry> setTermine(List<EventEntry> terminListe)
  2.    {
  3.      List<EventEntry> resultat = new List<EventEntry>();
  4.      
  5.      var feedUri = "https://www.google.com/calendar/feeds/default/private/full";
  6.      EventQuery query = new EventQuery(feedUri);
  7.      query.ExtraParameters = "orderby=starttime&max-results=100000";// maksimale anzahl von terminen, die bearbeiten werden
  8.      EventFeed feed = service.Query(query);
  9.      AtomFeed batchFeed = new AtomFeed(feed);
  10.  
  11.      foreach (var termin in terminListe)
  12.      {
  13.        batchFeed.Entries.Add(termin);
  14.      }
  15.      EventFeed batchResultFeed = (EventFeed)service.Batch(batchFeed, new Uri(feed.Batch));
  16.      foreach (EventEntry entry in batchResultFeed.Entries)
  17.      {
  18.        if (entry.BatchData.Status.Code != 200 && entry.BatchData.Status.Code != 201)// wird geprüft, ob alles ok ist
  19.        {
  20.          return null;// falls nicht, null ubergeben
  21.        }
  22.        resultat.Add(entry);
  23.      }
  24.      return resultat;
  25.    }

Die Liste "terminListe" in dieser Methode enthält Objekte, mit deren eine Action in Kalender dürchgefurt werden soll. Welche genau bestimmt der Feld "BatchData". Z.B:

termin.BatchData = new GDataBatchEntryData("A", GDataBatchOperationType.update)

Die Methode "setTermine" liefert eine Liste mit geänderten Terminen.

Einen Termin löschen:

Angenommen will man einen Termin aus Kalender löschen. Die ID des Termins in Kalender ist bekannt(in Datenbank ist die unter "TERMINIDINGOOGLE" gespeichert): "TERMINIDINGOOGLE"="12345"

  1. uri = new Uri("https://www.google.com/calendar/feeds/default/private/full/" + 12345);
  2. termin=service.Get(uri.ToString()) as EventEntry;
  3. termin.Delete()

Termine aus Kalender holen:

  1.   public EventFeed GetTermine(DateTime von, DateTime bis)
  2.     {
  3.       EventQuery myQuery = new EventQuery("https://www.google.com/calendar/feeds/default/private/full");
  4.       myQuery.ExtraParameters = "orderby=starttime&max-results=100000";
  5.       myQuery.StartTime = von;
  6.       myQuery.EndTime = bis;
  7.       EventFeed myResultsFeed = service.Query(myQuery) as EventFeed;
  8.       return myResultsFeed;
  9.     }

Die Zeile: myQuery.ExtraParameters = "orderby=starttime&max-results=100000"; ist sehr wichtig. Wenn man die Zeile weglässt, bekommt man nur die erste 25 Termine. Die Zeile setzt die Anzahl den geliferten Terminen auf 100000.

Erfindung entferneten,neuen und geänderten Terminen.

Neue Termine.

Termine, die im Kalender zu finden sind und in der Tabelle "GOOGLEKOPIE" dagegen nicht, gelten als neue Termine.
Beispiel:  in der Tabelle "GOOGLEKOPIE" sind Termine "A" und "B" gespeichert.  Kalender hat im Moment Termine "A","B" und "C". Der Termin "C" befindet sich im Kalender aber nicht in der Tabelle "GOOGLEKOPIE", d.h. zur Zeit der letzten Synchronisation hat der Termin noch nicht existiert. Der Termin gilt als ein neuer. Als Bezeichner von Terminen, der für Vergleich verwendet wird(im Beispiel sind das "A","B" und "C") benutz man den Feld  "TERMINIDINGOOGLE" aus Tabelle "GOOGLEKOPIE" und den Feld "EventId" eines Termins aus Kalender.

Entfernte Termine.

Für entfernte Termine ist die gleiche Vorgehensweise gültig wie für neue Termine mit dem Unterschied, dass ein entfernter Termin in Datenbank zu finden ist, aber nicht im Kalender.

Geänderte Termine.

Als geänderte Termine gelten die Termine, deren Datum im Feld "Updated" später ist als Datum im Feld "LASTSYNC" in Tabelle "SYNCINFOTABLE".
Beispiel: letzte Synchronisation wurde am 01.01.2010 um 01:00 Uhr durchgeführt, d.h. der Feld "LASTSYNC"  in Tabelle  "SYNCINFOTABLE" enthält : "01.01.2010 01:00:00". Ein Termin im Kalender wurde am 01.01.2010 um 01:15 Uhr geändert, d.h. sein Feld  "Updated" enthält: "01.01.2010 01:15:00". Der Termin gilt als geäderter, da das Datum seines Feldes "Updated" später ist als Datum im Feld  "LASTSYNC" in Tabelle "SYNCINFOTABLE".

Methode:

Um neue und entfernte Termine zu finden, kann man folgende Methode benutzen:

  1. public static EventEntry[] GetFehlendeTermine(EventEntry[] a, EventEntry[] b)
  2.     {
  3.       EventEntryIdComparator eeic = new EventEntryIdComparator();
  4.       IEnumerable<EventEntry> res = a.Except(b, eeic);
  5.       return res.ToArray();
  6.     }

Falls Array "a" die Termine aus Datenbank enthält und Array "b" die Termine aus Kalender, dann sind im Ergebniss entfernte Termine. Falls umgekert- neue Termine.

Die Klasse "EventIdComparator" kann so aussehen:

  1.   class EventEntryIdComparator : IEqualityComparer<EventEntry>
  2.   {
  3.     private String getId(EventEntry entry)
  4.     {
  5.       if (entry is EventEntryInner)
  6.         return ((EventEntryInner)entry).GetEventId().Trim();
  7.       else
  8.       {
  9.         return entry.EventId.Trim();
  10.       }
  11.     }
  12.  
  13.     public bool Equals(EventEntry x, EventEntry y)
  14.     {
  15.       string id1 = getId(x);
  16.       string id2 = getId(y);     
  17.       return id1.Equals(id2);
  18.  
  19.  
  20.     }
  21.  
  22.     public int GetHashCode(EventEntry obj)
  23.     {
  24.       return getId(obj).GetHashCode();
  25.  
  26.     }
  27.   }