Home
Mert Usul editou esta página há 2 anos atrás

Dokumentation des aspern.mobil LAB DATA.hub

Allgemeine Beschreibung des DATA.hub

Der data.HUB ist eine Datenplattform, die aktuell grob zwei Ziele verfolgt und großteils erfüllt:

  1. Der data.HUB ist ein Server, der geografisch verortete Daten von Sensoren entgegennimmt, speichert und verwaltet. Weiters sind aktuell ein paar Möglichkeiten implementiert um diese Daten in Folge nutzbar zu machen:
  2. Der data.HUB kann Open Data Quelldaten (soweit sie in GeoJSON verfügbar sind) auf das Areal der Seestadt Aspern filtern und damit verkleinern. Diese Daten werden automatisch alle 7 Tage aktualisiert und können ebenfalls für weitere Anwendungen verfügbar gemacht werden.

Der data.HUB besteht darüber hinaus grob gesehen drei Komponenten:

  1. Ein Administrations-Interface, das die gesamte Datenbank-Struktur abbildet und es AML-Mitarbeiter:innen erlaubt, Sensoren, Projekte, Benutzer:innen, etc. zu verwalten (Projekte, etc. anlegen, modifizieren und löschen, Nutzer:innen freischalten).
  2. Eine Reihe an Schnittstellen, sogenannten REST-APIs, über die per (Geo)JSON Daten an die Plattform geschickt bzw. abgerufen werden können.
  3. Use-case-getriebene User Interfaces unter Nutzung der REST-APIs, z.B. das Sensoren-Dashboard, das die Daten eines Sensors auf einer Karte, im Zeitverlauf und Sensoren-Infos (Batteriestand, GPS-Empfang) darstellt.

Aktuelle Limitierungen

Einige Funktionen sind aktuell noch prototypisch implementiert und müssen u.U. im Zuge einer Weiterentwicklung, v.a. in einem User:innenzentriertem Prozess, noch weiter angepasst werden.

Eine gravierende Einschränkung stellt die aktuelle Implementierung ohne Verschlüsselung der Datenübertragung (kein HTTPS) dar. Das ist dem Umstand geschuldet, dass die aktuellen Sensoren keine Verschlüsselung unterstützen. Es bedeutet aber auch, dass aktuell ein Betrieb mit externen Nutzer:innen nicht empfehlenswert ist.

Technologie-Stack

  • Backend-seitige Programmierung in python und dem Webentwicklungs-Framework Flask und einiger Erweiterungen (Flask-Admin, Flask-Restful, Flask-User)
  • Die Datenbank ist PostGIS
  • Das Backend (Flask) wird per Gunicorn geserved
  • Allem ist ein nginx Webserver vorgeschalten
  • Die drei Komponenten (nginx, PostGIS, Gunicorn/Flask) sind je per Docker-containern realisiert und per docker-compose konfiguriert
  • Datenaustausch wird per JSON bzw. für lokalisierte Daten GeoJSON ermöglicht

Die Konfigurationen für die Docker container und docker-compose sind Teil dieses Repositorys.

Administrations-Interface

User:innen-Management

Registrierung

User:innen können sich unter /user/register registrieren. Die User:innen müssen aber im Anschluss erst freigeschalten werden, bevor sie sich einloggen können. Eine Möglichkeit, per Email das passwort zurückzusetzen gibt es nicht.

Rollen

Es können zwei User:innen-Rollen für den Administrationsbereich zugewiesen werden:

  • Ein Editor kann alles verwalten und einsehen (inkl. aller Datenquellen/Sensoren und deren Reading Points), aber weder Rollen noch User:innen betrachten. Das könnte z.B. für Studierende geeignet sein.
  • Ein Admin kann sämtliche Daten einsehen und somit auch Nutzer:innen freischalten und Rollen zuweisen
  • Alle anderen Nutzer:innen haben keine Möglichkeiten, Daten über das Administrations-Interface zu betrachten oder zu ändern. Sie müssen die Daten über REST-APIs bzw. auf diesen aufbauenden UIs einsehen (wo die unten genannten Zugriffsrechte greifen).

Zugriffsrechte der REST-APIs

Es gibt drei Modi, wie Daten für Nutzer:innen oder auch die Öffentlichkeit über Schnittstellen zur Verfügung gestellt werden können. Diese Einstellungen werden über Projekte gesetzt. Jedes Projekt hat außerdem eine:n Projektleiter:in, diese:r Nutzer:in kann immer alle Daten einsehen.

  1. Own bedeutet, dass jede Nutzer:in nur die Daten von "eigenen" Sensoren einsehen kann. Das kann z.B. Praktisch sein, wenn der Standort eines Sensors im Privatbereich ist oder Bewegungen aufgezeichnet werden und über die Identität Aufschluss geben könnte.
  2. Project bedeutet, dass alle Nutzer_innen in einem Projekt die Daten aller anderen Nutzer:innen im Projekt einsehen könne, aber andere Nutzer:innen oder die Öffentlichkeit nicht.
  3. Public bedeutet, dass alle Daten für alle einsehbar sind, inklusive nicht authentifizierte Nutzer:innen.

Diese Einschränkungen gelten für alle aktuell implementierte API-Endpunkte, die auf DataSources (Sensoren) oder Datenpunkte (Reading Points) Zugriff bieten. Für z.B. Open Government Data ist dies nicht relevant.

Datenstrukturen und Modelle

In Ergänzung zur dokumentierten Datenbankstruktur hier noch Ausführungen zur Anwendung der Datenstrukturen

Sensorik

Der Sensorik-Teil des data.HUB beinhaltet eine Reihe an Modellen:

  • Projekte organisieren auf oberster Ebene die Geräte. Z.B. könnte eine Workshop-Reihe mit Seestadt-Bewohner:innen ein Projekt sein. Jedes Projekt hat eine:n User:in als Administrator:in. Diese:r User:in kann immer alle Daten dieses Projekts sehen (und muss dazu zumindest die Editor-Rolle bekommen).
  • DeviceGroup ist einem Projekt zugeordnet und kann mehrere Geräte umfassen. Die Idee dahinter ist, Nutzer:innen mehrere Geräten im Paket mitgeben zu können. Jede DeviceGroup hat eine Nutzer:in als Besitzer:in.
  • SourceType ist ein Geräte-Typ. Die erste Sensorbox ist z.B. ein SourceType, Pocobo wäre ein anderer SourceType.
  • DataSource ist dann ein konkretes identifizierbares Gerät und muss Teil einer DeviceGroup sein. Eine DataSource kann sich gegenüber dem Server per JSON Web Token (JWT) authentifizieren.
  • ReadingPoint ist eine Messung eines Geräts. Dabei kann ein Gerät auch mehrere Sensoren besitzen und damit Messungen verschiedener Arten senden (z.B. Lautstärke und Temperatur im Fall der ersten AML-Sensorboxen). Jeder ReadingPoint hat einen Zeitpunkt, eine GPS-basierte Geolocation (Punkt, Linie, Rechteck oder Polygon) und einen Namen der Messung. Dabei kann eine Messung sowohl einen numerischen Wert (Fließkomma-Zahl) oder einen textuellen Wert haben (für qualitative Erhebungen). ReadingPoints können auch über das Admin-Interface manuell erstellt werden, d.h. hier können auch analoge Daten digitalisiert werden.

Obwohl aktuell bei DataSource von Geräten/Sensoren die Rede ist: Es ist auch möglich, z.B. Daten des Mobilitätspanels als GeoJSON per REST-API an den data.HUB zu senden. Daher auch der generische Name "Datenquelle".

Open (Government) Data

  • Unter OGD können externe Open (Government) Data Quellen eingebunden werden. Das ist aber darauf angewiesen, dass diese Daten als GeoJSON zugänglich sind. Die Daten werden dann automatisch auf die Region Seestadt Aspern gefiltert. Jeder Datensatz braucht zwingend einen Namen und kann optional weiter beschrieben werden.

API-Endpunkte

JSON Web Token Authentifizierung für Sensoren

Die Authentifizierung der REST API für u.a. Sensoren erfolgt über JSON Web Tokens (JWT). Diese Tokens erlauben, ein paar Attribute mit einem "Secret" des Servers zu signieren:

  • sub ist das subject des JWT, in unserem Fall bietet sich die Sensor-ID an.
  • iss ist der issuer, hier könnten wir den_die User_in des Webservices verwenden, der_die den Token erstellt hat.
  • exp erlaubt, ein Ablaufdatum festzulegen. Das wird aktuell nicht gesetzt, da die Sensorboxen möglichst lange ohne Intervention senden können sollen.
  • iat ist der Ausstellungszeitpunkt. Diesen zu nutzen macht wahrscheinlich zur Nachvollziehbarkeit sinn (wann wurde der Token erstellt?)

Mit dem Secret der REST-API signiert und base64-codiert entsteht ein Token der wie folgt aussieht. Ein entsprechendes Interface wird es am Server geben.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Dieser Token muss auf jedem Sensor gespeichert werden, bevor er Daten an die REST API senden kann. Für jede Anfrage an die REST-API muss dann dieser Token in den Header als Authorization: Bearer geschrieben werden und der Conten

curl 
  -X POST 
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
  -d '{"type": "Feature", "geometry": {"type": "Point", "coordinates": [100.0, 0.0]}, "properties": {"Temperature": [24.5, "°C"], "Noise": [100.0, "dB"], "timestamp": "1617140798" }' 
  https://aml.media.tuwien.ac.at:11312/api/sensordata

Alternativ, da die aktuellen Sensorboxen den Header nicht setzen können, kann der Token auch als Teil der URL gesendet werden:

curl 
  -X POST  
  -d '{"type": "Feature", "geometry": {"type": "Point", "coordinates": [100.0, 0.0]}, "properties": {"Temperature": [24.5, "°C"], "Noise": [100.0, "dB"], "timestamp": "1617140798" }' 
  https://aml.media.tuwien.ac.at:11312/api/sensordata/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Der Server überprüft dann den Token, und liest aus dem Token die Sensor-ID aus und kann somit die Nachricht dem DataSource zuordnen.

User:innen-Authentifizierung (Login)

Die meisten Endpunkte geben für eingeloggte User:innen mehr Daten Preis. Um das Einloggen zu ermöglichen reicht es, die Person an folgende URL zu schicken: http://aml.media.tuwien.ac.at:11312/user/sign-in?next=https://www.mobillab.wien/path/to/dashboard. Nach dem Login wird die Person zu https://www.mobillab.wien/path/to/dashboard redirected. Wer schon über das Admin-Interface eingeloggt ist, sollte automatisch die sichtbaren Daten angezeigt bekommen.

Wichtig: Aus Sicherheitsgründen werden nur Redirects auf http://www.mobillab.wien/... und https://www.mobillab.wien/... akzeptiert!

/api/sensordata/<token>

Dieser Endpunkt nimmt ReadingPoint Daten entgegen. Der Token kann wie oben beschrieben als Teil der URL oder im Header mitgegeben werden. Dabei muss der GeoJSON-Body der Nachricht folgendem Schema folgen:

{
  "type": "Feature",
  "geometry": {
    "type": "<Geometrie-Typ>",
    "coordinates": [<Longitude>, <Latitude>]
  },
  "properties": {
    "<Reading name>": [<Reading value>, "<Reading unit>"],
    ...,
    "timestamp": "<POSIX timestamp>"
  }
}

D.h. es kann z.B. so aussehen:

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [100.0, 0.0]
  },
  "properties": {
    "Temperature": [24.5, "°C"],
    "Noise": [100.0, "dB"],
    "Geruch": ["Blumig", ""],
    "timestamp": "1617142473"
  }
}

Falls ein Gerät keine POSIX timestamp erstellen kann, ist es auch möglich, einen nicht parsbaren dummy-wert (oder emtpy string) zu senden, dann wird die Zeit des Emfpangs am Server als timestamp verwendet.

/api/sensorstatus/<token>

Dieser Endpunkt erlaubt es Geräten, Informationen über ihren Status zu senden: Batteriestand in Prozent und Millivolt, ob ein GPS fix erfolgen konnte und ob das Gerät sich herunterfährt und damit offline geht. Der Token kann wie oben beschrieben als Teil der URL oder im Header mitgegeben werden.

Default, wenn alles gut ist, reicht folgende Nachricht aus:

{
  "battery": 0.85,
  "millivolt": 123.5
}

wenn die batterie niedrig ist als signal, dass die box offline geht (0 fuer false)

{
  "battery": 0.05,
  "millivolt": 3.5,
  "online": 0
}

gleichsam das, falls es kein GPS-signal gibt:

{
  "battery": 0.85,
  "millivolt": 123.5,
  "gps_fix": 0
}

Wenn, wie in der ersten nachricht, keine Einträge fuer gps_fix oder online mitgegeben werden, dann wird das so interpretiert, dass gps da ist und die box auch online ist.

/api/reading_points/

Über diesen Endpunkt können alle Datenpunkte/Messungen die zugänglich sind als GeoJSON abgerufen werden (d.h. entweder für den:die User:in oder öffentlich wenn nicht eingeloggt). Ein Beispiel-Resultat ist folgendes:

{
    "type": "FeatureCollection",
    "features": [{
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [16.392097, 48.142757]
        },
        "properties": {
            "Temperature (\u00b0C)": 1.88,
            "Timestamp": "2020-12-01 12:52:59.115530"
        }
    }, {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [16.391411, 48.142502]
        },
        "properties": {
            "Noise (dB)": 35.0,
            "Timestamp": "2021-01-20 03:07:14.858159"
        }
    }, {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [16.391375, 48.142567]
        },
        "properties": {
            "Noise (dB)": 35.0,
            "Timestamp": "2020-10-08 00:21:24.705461"
        }
    }, {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [16.344713, 48.223713]
        },
        "properties": {
            "Temperature (\u00b0C)": 1.56,
            "Timestamp": "2020-12-18 21:26:05.633834"
        }
    }]
}

/api/reading_points/<reading name>

Über diesen Pfad können die Datenpunkte nach Reading Name gefiltert werden, d.h. z.B. nur Temperature oder Noise.

/api/datasource/

Über diesen Endpunkt kann eine Liste sichtbarer DataSources abgerufen werden. Die Antwort sieht so aus und ist ein Dictionary von DataSource ID und Name:

{
  "1": "Sensorbox 5",
  "3": "Sensorbox 6",
  "5": "Sensorbox 7",
  "7": "Sensorbox 1",
  "10": "Sensorbox 3",
  "11": "Sensorbox 8",
  "14": "Sensorbox 4",
  "15": "Sensorbox 2",
  "16": "Sensorbox 9"
}

/api/datasource/<source_id>

Mit der DataSource ID können über diesen Endpunkt alle Daten zu dieser DataSource abgerufen werden, um z.B. ein Dashboard damit zu betreiben:

{
  "source_id": 3,
  "source_name": "Sensorbox 6",
  "description": "+436765163405; CK; v1.0",
  "battery_level": null,
  "battery_millivolt": null,
  "gps_fix": false,
  "online": false,
  "reading_points": {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [
            16.344734,
            48.223747
          ]
        },
        "properties": {
          "Temperature (°C)": 15.74,
          "Timestamp": "2021-03-31 00:04:25.600797"
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [
            16.344734,
            48.223747
          ]
        },
        "properties": {
          "Noise (dB)": 46,
          "Timestamp": "2021-03-31 00:04:25.600797"
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [
            16.344734,
            48.223747
          ]
        },
        "properties": {
          "Temperature (°C)": 14.78,
          "Timestamp": "2021-03-31 00:24:25.738378"
        }
      },
      {
        "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [
            16.344734,
            48.223747
          ]
        },
        "properties": {
          "Noise (dB)": 48,
          "Timestamp": "2021-03-31 00:24:25.738378"
        }
      }
    ]
  }
}

/api/datasource/<source_id>?from=<from_date>&to=<to_date>

Da die Messpunkte/Reading Points schnell sehr viele werden können ist es auch möglich, per URL-Paramtern die Reading Points der DataSource zu filtern, z.B. http://aml.media.tuwien.ac.at:11312/api/datasource/3?from=2021-03-31&to=2021-04-01. Dabei kann auch nur to oder from gesetzt sein - es müssen nicht unbedingt beide sein.

/api/ogd

/api/ogd/<name>

Datenweitergabe