6 Technische Umsetzung

Die nachfolgenden Abschnitte erweitern die Nutzerhilfe um die Beschreibung der technischen Umsetzung ausgewählter Elemente der Web-Applikation. Die ausgewählten Elemente sollen dabei insbesondere eine Vielzahl des verwendeteten Methodenspektrums darlegen, sodass eine nachhaltige Nutzung über die Laufzeit des Projektes hinaus erleichtert wird. Darüber hinaus sollen interessante Ansätze vorgestellt werden.

6.1 Struktur der Web-Applikation

Strukturell handelt es sich bei der Web-Applikation um eine Sammlung verschiedener Dateien innerhalb eines Ordners. Der Einstiegspunkt in die Web-Applikation ist die Datei app.R. Alle anderen Dateien werden zur Laufzeit der Web-Applikation eingebunden. Die Web-Applikation wird gestartet, indem der Shiny Server die Datei app.R ausführt. In app.R werden Initialisierungen durchgeführt und die Funktionen ui und server definiert. Die Datei endet mit dem Aufruf shinyApp(ui, server), wobei die ui-Funktion das visuelle Layout der Web-Applikation beschreibt und die server-Funktion Eingaben verarbeitet. Mithilfe von sogenannten Modulen kann der Code strukturiert werden. Jedes Modul setzt sich dabei wiederum aus einer ui- und einer server-Funktion zusammen. Module bilden Teilaspekte, die unter Umständen wiederverwendet werden können, ab. [36]

6.1.1 Struktur von app.R

Wie bereits beschrieben, ist die Datei app.R der Einstiegspunkt der Web-Applikation. Da sie Einfluss auf das Verhalten vieler weiterer Module hat, ist ihr Aufbau näher zu betrachten. Zur Übersicht werden die Inhalte verkürzt dargestellt.

  • Laden benötigter Packages mit library(). Die Mehrzahl aller Packages wird über den ::-Operator referenziert. Nur die Packages, die zwangsläufig mit library() geladen werden müssen, also Packages, die funktionsrelevanten Code mit dem Aufruf von library() verbinden, werden hier geladen.
library(shiny)
library(shinyjs)
library(dplyr)
library(qrcode)
  • Die Funktion source_directory() wird manuell geladen und danach dazu verwendet, alle .R-Dateien in den Ordnern modules und db/func einzubinden.
source("init/source_directory.R")

source_directory(
    path = "modules",
    encoding = "UTF-8",
    modifiedOnly = FALSE,
    chdir = TRUE,
    recursive = TRUE,
    envir = if (source_to_globalenv) globalenv() else environment()
)
  • Die ui-Funktion bindet alle CSS- und JavaScript-Dateien ein und aktiviert spezielle Packages. Die Funktion container_ui(), die das Layout des Dashboardes festlegt, wird aufgerufen. In container_ui() wird für jeden Reiter eine eigene ui-Funktion aufgerufen.
    # UI -----------------------------------------------------------------------
    ui <- htmltools::tagList(
        htmltools::includeScript("www/js/dark-mode.js"),
        htmltools::includeCSS("www/css/styles.css")
        container_ui(
            id = "container"
        ),
        rclipboard::rclipboardSetup(),
        shinyjs::useShinyjs(),
        waiter::use_waiter()
    )
  • In der server-Funktion wird die .values-Environment angelegt. Diese enthält die in Tabelle 6.1 aufgelisteten Elemente.
Tabelle 6.1: Die .values-Environment
Name Beschreibung
query$type Aus Query-String ausgelesene Typ-ID
settings Liste, die Namenslängen festlegt und Dictionaries enthält
update Liste, die reactiveVal()s enthält, die bei Datenaktualisierung getriggert werden
user Liste, die Informationen zum angemeldeten Nutzer enthält
yaml Inhalt von app.yml

Diese Environment wird jeder server-Funktion übergeben, sodass die Werte in jeder server-Funktion verfügbar sind.

  • In der server-Funktion von app.R wird die server-Funktion container_server() aufgerufen.

6.1.2 Ordnerstruktur

  • .gitignore Index aller Dateien, die nicht der Versionskontrolle unterliegen
  • app.R Einstiegspunkt in die Web-Applikation
  • app.yml Konfigurationsdatei
  • db/
    • db.sqlite Datenbank
    • func Funktionssammlung zur Interaktion mit der Datenbank
  • files/ Dateiensammlung für Dateiverwaltung. Enthält drei Unterordner für Gruppen, Typen und Untertypen. Jeder dieser Unterordner enthält für jedes Element der jeweiligen Kategorie einen Ordner mit der Identifikationsnummer des jeweiligen Elementes als Namen
  • init/source_directory.R Hilfsfunktion, die beim Start der Web-Applikation alle .R-Dateien einliest
  • modules/ Shiny-Module und sonstige Funktionalitäten
  • renv, renv.lock Package-Management
  • www/
    • css/ CSS-Dateien
    • favicon.ico Sensotheka-Icon
    • js/ JavaScript-Dateien

6.2 Datenbank

Die Datenbank ist eine SQLite-Datenbank. Sie liegt in db/db.sqlite. Der Zugriff erfolgt über das Package {DBI} [19].

6.2.1 Struktur

Alle Tabellen enthalten die Spalte rowid, die den Primärschlüssel der jeweiligen Tabelle enthält. Diese kann somit zur eindeutigen Identifikation einer Zeile verwendet werden. Die Ablage der zu speichernden Werte erfolgt in Ergänzung dazu über weitere Spalten, die in den folgenden Unterkapiteln näher beschrieben werden. Die Beschränkung Einzigartig bezieht sich immer nur auf die nicht gelöschten Elemente (removed = 0).

6.2.1.1 circulation

Tabelle 6.2: Datenbank: Tabelle circulation
Spalte Einschränkungen Beschreibung
user_id Fremdschlüssel Identifikationsnummer des ausführenden Nutzers
subtype_id Fremdschlüssel Identifikationsnummer des bearbeiteten Untertyps
quantity Nicht-negativ Bearbeitete Menge
time Zeitpunkt der Transaktion
op_type 1 oder 2 Transaktionsart: 1 - Ausleihen und Zurückgeben, 2 - Bestandsänderungen

6.2.1.2 groups

Tabelle 6.3: Datenbank: Tabelle groups
Spalte Einschränkungen Beschreibung
group_name Einzigartig Gruppenname
removed 0 oder 1 0 - existent, 1 - gelöscht

6.2.1.3 group_type

Tabelle 6.4: Datenbank: Tabelle group_type
Spalte Einschränkungen Beschreibung
group_id Fremdschlüssel Identifikationsnummer der Gruppe
type_id Fremdschlüssel Identifikationsnummer des Typs

6.2.1.4 subtype

Tabelle 6.5: Datenbank: Tabelle subtype
Spalte Einschränkungen Beschreibung
type_id Fremdschlüssel Identifikationsnummer des Typs
subtype_name Einzigartig innerhalb eines Typs Untertypenname
quantity Nicht-negativ Bestandsmenge
removed 0 oder 1 0 - existent, 1 - gelöscht

6.2.1.5 type

Tabelle 6.6: Datenbank: Tabelle type
Spalte Einschränkungen Beschreibung
type_name Einzigartig Typname
removed 0 oder 1 0 - existent, 1 - gelöscht

6.2.1.6 user

Tabelle 6.7: Datenbank: Tabelle user
Spalte Einschränkungen Beschreibung
hash Verhashter Nutzername (notwendig für Cookies)
name Einzigartig Nutzername
status admin, mod oder user Nutzerrolle
password Verhashtes Passwort
added_from Nutzeridentifikationsnummer des Hinzufügenden
time_added Zeitpunkt der Kontoeröffnung
time_current_logged Zeitpunkt der letzten Anmeldung
time_previous_logged Zeitpunkt der vorletzten Anmeldung
times_logged Anzahl der Anmeldungen
removed 0 oder 1 0 - existent, 1 - gelöscht

6.2.2 Zugriff

Der Zugriff auf die Datenbank erfolgt, wie beschrieben, über das Package {DBI}[19] und soll hier exemplarisch vorgeführt werden. Die Verbindung der Datenbank und R könnte dabei folgendermaßen aussehen:

library(DBI)
library(RSQLite)

# Verbinde die Datenbank mit R
db <- dbConnect(SQLite(), "db/db.sqlite")

# Tabellennamen
dbListTables(db)
## [1] "circulation" "group_type"  "groups"      "subtype"     "type"        "user"

Beispielhaft wird die Tabelle circulation betrachtet. Diese Tabelle speichert alle Transaktionen, die von Nutzern durchgeführt worden sind, also zum Beispiel Ausleihen, Rückgaben und Mengenänderungen.

dbReadTable(db, "circulation")
Tabelle 6.8: Beispielhafter Inhalt der Tabelle circulation
user_id subtype_id quantity time op_type
1 2 24 2021-02-18 18:38:00 2
1 3 12 2021-02-18 18:38:04 2
1 4 5 2021-02-18 18:38:07 2
1 5 8 2021-02-18 18:38:12 2
1 6 14 2021-02-18 18:38:16 2
1 11 4 2021-02-18 18:40:14 2
1 12 3 2021-02-18 18:41:09 2
1 17 112 2021-02-18 18:41:40 2
1 1 45 2021-02-18 19:38:35 2
4 7 6 2021-02-18 19:40:53 1

Der Ordner db/func/ enthält eine Sammlung von über 80 Hilfsfunktionen, die zum Erstellen, Abfragen und Modifizieren der Datenbank verwendet werden. Die Interaktion mit der Datenbank erfolgt ausschließlich über diese Hilfsfunktionen. Dadurch werden Redundanzen ausgeschlossen und es ist darüber hinaus einfacher, die korrekte Funktionalität zu gewährleisten.

Beispielhaft wird die Funktion db_get_borrowed_quantity(db, subtype_id) betrachtet. Unter Angabe der Datenbank db und einer oder mehrerer Untertypenidentifikationsnummern subtype_id wird die ausgeliehene Menge dieser Untertypen zurückgegeben. In {DBI} werden Abfragen an die Datenbank mithilfe von dbGetQuery() gestellt. Das Resultat der Abfrage wird anschließend weiterverarbeitet. Der Nutzen der Hilfsfunktion wird zusätzlich dadurch verdeutlicht, dass sowohl die abzufragende Tabelle sowie die beteiligten Zeilen und Spalten nicht gesondert angegeben werden müssen.

# Funktionsdefinition
db_get_borrowed_quantity <- function(db, subtype_id) {
  borrowed <- DBI::dbGetQuery(
    db,
    "SELECT SUM(quantity) AS borrowed FROM circulation
    WHERE subtype_id = ? AND op_type = 1",
    params = list(subtype_id)
  )$borrowed

  ifelse(is.na(borrowed), 0, borrowed)
}

# Abfrage der ausgeliehenen Menge für mehrere Untertypen
db_get_borrowed_quantity(db, 1:5)
## [1]  0 13  0  0  2

6.3 Cookies

Cookies sind Textdaten, die der Client mit jeder Anfrage an den Server mitsendet [37]. Die Web-Applikation setzt zwei Cookies. Die Cookies sind jeweils für einen Zeitraum von sieben Tagen gültig.

6.3.1 dark-mode

Dieses Cookie kann die Werte "true" oder "false" enthalten. Das Cookie wird gesetzt, wenn der Nutzer den Schalter für den Nachtmodus verwendet. Beim Start der Web-Applikation wird das Cookie verwendet, um den initialen Status für den Nachtmodus zu bestimmen. Falls das Cookie nicht vorhanden ist, wird die Einstellung des Browsers (window.matchMedia('(prefers-color-scheme: dark)')) ausgelesen und im Cookie gesetzt.

6.3.2 user

Dieses Cookie enthält einen verhashten Nutzernamen und dient zur Identifikation eines angemeldeten Nutzers. Das Cookie wird gesetzt, wenn sich ein Nutzer anmeldet. Beim Start der Web-Applikation wird das Cookie verwendet, um den Nutzer automatisch anzumelden. Falls das Cookie nicht vorhanden ist, muss der Nutzer sich manuell anmelden.

6.3.3 Implementierung

Zur Verwaltung der Cookies wird die Bibliothek js-cookie [38] verwendet. Die Datei www/js/cookies.js enthält Hilfsfunktionen zum Lesen und Schreiben von Cookies und verbindet diese mit Inputwerten in Shiny. Die Hilfsfunktionen werden in R mithilfe von shinyjs::extendShinyjs() eingebunden.

6.4 Deployment

Der Shiny Server liegt auf einer virtuellen Maschine (VirtualBox). Auf der virtuellen Maschine ist das Betriebssystem Ubuntu 20.04.2 LTS ohne grafische Benutzeroberfläche installiert. Änderungen am Shiny Server werden erst wirksam, nachdem der Shiny Server neugestartet wurde. Dazu kann folgender Befehl verwendet werden: sudo systemctl restart shiny-server.

Nachfolgend wird auf Ordner verwiesen, in denen für die Web-Applikation relevante Dateien liegen.

  • /etc/shiny-server

    Dieser Ordner enthält die Datei shiny-server.conf, mit der der Shiny Server konfiguriert werden kann [39]. Hier kann der Port eingestellt werden, unter dem die Web-Applikation verfügbar gemacht wird. Der Port 3838 ist voreingestellt.


  • /srv/shiny-server/Sensotheka

    Dieser Ordner enthält das Git-Repository, das die tatsächliche Web-Applikation darstellt. Es verweist auf das GitHub-Repository https://github.com/PFA-WebApp/App.git als Remote mit Namen origin. In der Datei app.yml müssen die in Tabelle 6.9 aufgelisteten Einstellungen vorgenommen werden:

    Tabelle 6.9: Einstellungen in app.yml
    Einstellung Beschreibung
    showcase yes für Showcasemodus (Passwörter für Standardnutzer werden eingeblendet, Standardnutzer können nicht gelöscht werden), no (voreingestellt) für Betriebsmodus
    url Basis-URL auf welche QR-Codes verweisen (hier sind Anpassungen zwingend notwendig)


  • /etc/rstudio

    Dieser Ordner enthält die Datei rserver.conf, mit der der RStudio Server konfiguriert werden kann [40]. Hier kann der Port eingestellt werden, unter dem der RStudio Server verfügbar gemacht wird. Der Port 8787 ist voreingestellt. Der RStudio Server kann zur nachhaltigen Pflege und Weiterentwicklung der Web-Applikation genutzt werden. Dazu muss der Port aufgerufen werden. In der Login-Maske kann sich mithilfe des Nutzers pfa und eines zugehörigen Passworts eingeloggt werden. Danach öffnet sich die Online-Version der Entwicklungsumgebung RStudio, deren Funktionsumfang mit dem der Desktopversion identisch ist.

    Für weitere Informationen siehe https://docs.rstudio.com/ide/server-pro/. Es ist zu beachten, dass es sich bei der installierten Version um die Open-Source-Version und nicht um die Pro-Version handelt.


  • /var/log/shiny-server/Sensotheka

    Dieser Ordner enthält Log-Dateien, die vom Shiny Server erzeugt werden, wenn Komplikationen im Betrieb auftreten. Sollte das der Fall sein, ist es ratsam, diese zunächst zu konsultieren, bevor mit der Fehlerbehandlung begonnen wird.