Die Clemens-Winkler-Sammlung

Metadatenerschließung

Grundlage der Erfassung der Metadaten ist der Metadaten-Standard. Dieser gibt vor, welche Daten in welchem Format für die Objekte als Metadaten erfasst werden. In diesem Projekt wurden die Metadaten anhand des LIDO-Standards (Version 1.0) erfasst und in einer SQLite3-Datenbank gespeichert. Der Standard wurde mit den subject-Feldern um Informationen zur Chemie erweitert. Diese Erweiterung ist standardkonform, sodass valides LIDO aus den Daten erzeugt werden kann.

Datenstruktur

LIDO ist ein XML-Datenformat, das nicht direkt in ein relationales Datenbanksystem übertragen werden kann. Für die Nutzung einer SQLite3-Datenbank für die Metadaten während der Erfassung wurde folgende Datenbankstruktur geschaffen: Es gibt die Tabellen Objects, Events, Actors, Places und Resources mit Daten und die Verknüpfungstabellen isRelated, isActorOf, tookPartInPlace, tookPartInEvent, isPartOfPlace und isResourceOf als Tabellen mit Verknüpfungsinformationen. Im Folgenden ist die Datenbankstruktur mit den Tabellen und Verknüpfungen schematisch dargestellt:

ER-Modell der Datenbankstruktur

Im Projekt hat sich gezeigt, dass vor allem die Tabelle Objects und Resources sowie deren Verknüpfungstablle isResourceOf ausgefüllt wurde. Aufgrund des Fehlens von Laborbüchern bedarf es zum Ausfüllen der weiteren Tabellen chemiehistorischer Recherche, die dieses Projekt übersteigt.

Nomenklatur und Datentypen in der Datenbank

Die Tabellenspalten sind im camelCase benannt. Nur die jeweiligen Primärschlüssel fangen zusätzlich mit einer Majuskel an. Es gibt vier Arten von möglichen Datentypen, die in der Datenbank gespeichert sind:

  1. Ganzzahlen: das sind vor allem die Primärschlüssel und die Identifikatoren für die Verknüpfungstabellen.

  2. Text: wird dort verwendet, wenn der Inhalt direkt im LIDO-Tag ausgegeben wird. Es gibt wenige Ausnahmen, namentlich Resources::resoucreMeasurements und Konzepte (siehe unten), die ein JSON-Objekt darstellen, da sie je nach Dateityp sehr variabel sind.

  3. Konzepte: werden als JSON-String gespeichert. Ein Konzept ist eine nummerierte Liste von möglichen Werten. Der Inhalt des JSON-Elements ist eine Liste aus 2 Elementen, wobei das erste der Konzept-Index ist und der zweite der (zum Zeitpunkt der Speicherung aktuelle) Konzept-Text ist.

  4. Datumsangaben: Das Datum wird im ISO-Format eingegeben, also JJJJ-MM-TT. Dabei können unbekannte Teile weggelassen werden. Das bedeutet 1989-05 ist Mai 1989. Zusätzlich kann noch ein < für die Zeit vor dem eingegebenen Datum und > für die Zeit danach angegeben werden. Für ungenaue Angaben (um/ca./etwa/...) muss eine Tilde (˜) vor das Datum geschrieben werden. Bei Zeitspannen werden zwei Zeiten getrennt durch einen Halbgeviertstrich (-) eingegeben. Für jede der beiden Zeiten können die Zeichen < , > und ˜ vorangestellt werden. Die Speicherung der Datumsangabe erfolgt innerhalb der Datenbank als Text und muss beim Auslesen interpretiert werden.

  5. Maße und Gewichte: Maße und Gewichte werden als Zahl mit einem . oder , als Dezimaltrennzeichen gefolgt von einem Leerzeichen und der Einheit im metrischen System angegeben. Abweichungen von diesem Format wird das Speichern des Formularinhalts verhindern und eine entsprechende Meldung verursachen. Die Speicherung der Daten erfolgt als String, nachdem das Dezimaltrennzeichen auf ein Komma festgelegt, vereinheitlicht wurde.

Datentabellen

Tabelle Objects

Die Tabelle orientiert sich am LIDO-Standard. Spezielle Spalten, die durch die Natur der Objekte benötigt werden, aber nicht im Standard vorgesehen sind, werden als subject-Attribute eingefügt.

| Spalte | Art | Beschreibung | Hinweis |

|--------------|-----------|------------|------------|

| ObjectID* | Ganzzahl |Identifikationszeichen| Primärschlüssel, wird automatisch eingetragen |

| objectWorkType* | Konzept | Art des Objekts ||

| objectClassification | Konzept | Klassifikation des Objektes ||

| objectTitle* | Text | Titel des Objektes ||

| objectTitleSource | Text | Quelle des Titels (z. B. das Etikett) ||

| repositoryName | Text | Sammlungszugehörigkeit ||

| height | Text | Höhe in cm (als Zahlenwert mit Leerzeichen und Einheit) ||

| width | Text | Breite in cm (als Zahlenwert mit Leerzeichen und Einheit) ||

| length | Text | Länge in cm (als Zahlenwert mit Leerzeichen und Einheit) ||

| specialForm | Konzept | Falls das Objekt eine spezielle Form hat ||

| description | Text | Beschreibung ||

| descriptionSource | Text | Woher stammt die Beschreibung (z. B. aus dem DFG-Projekt) ||

| recordType* | Konzept | Art des Datensatzes ||

| recordSource* | Text | Woher stammt der Datensatz? ||

| recordRights* | Text | Unter welcher Lizenz steht der Datensatz und wer ist der Rechteinhaber? | Spezieller Text: JSON (aber kein Konzept) |

| workRights* | Text | Welche Rechte hat wer am Objekt (Achtung: meist gemeinfrei!) ||

| subjectSystematicName | Text | systematischer Name der Substanz ||

| subjectFormular | Text | Formel der Substanz (sofern bekannt) ||

| subjectOrigin | Text | Woher stammt das Objekt? ||

| subjectSynonymes | Text | Synonyme für den Substanznamen, z. B. Trivialnamen ||

| subjectSubstanceClass | Konzept-Liste | Substanzklasse ||

| subjectLabel | Text | Beschreibung des Etiketts ||

| subjectCaption | Text | Weiter Beschriftung (sofern vorhanden) ||

| subjectComment | Text | Kommentare, die zum Objekt vorhanden sind ||

| subjectChemicalElements | Text | Liste der enthaltenen Elemente, wird autotmaisch beim Eintragen der Formel erstellt, kann aber ergänzt werden ||

| subjectFillingLevel | Konzept | Füllhöhe ||

| subjectCASNumber | Text | CAS-Registrierungsnummer ||

| subjectDominantColor | Konzept | dominante Farbe des Objekts ||

| subjectCreationDate | Text | Erstelldatum des Datensatzes ||

| subjectSignalWord | Konzept | Signalwort (GHS) ||

| subjectGHSPictograms | Konzept-Liste | Nummern der GHS-Piktogramme ||

| subjectHSentences | Text | H-Sätze (GHS) ||

| subjectPSentences | Text | P-Sätze (GHS) ||

mit * gekennzeichnete Spalten sind Pflichtfelder

Objekte können zueinander in einer Beziehung stehen. Diese Verknüpfung ist in der Tabelle isRealted enthalten. Eine Eltern-Kind-Beziehung ist möglich, wird aber aktuell nicht vom Formular unterschieden.

Die Verknüpfung zu Ereignissen (Tabelle Events) erfolgt über die Tabelle tookPartInEvent. Typische Events, sind z. B. Schenkung oder Herstellung des Präparats.

Dateiverknüpfungen erfolgen über die Tabelle isResourceOf zur Tabelle Resources. Dadurch kann eine Datei mehreren Objekten zugeordnet werden (z. B. Bilder, die für mehrere Objekte relevant sind).

Tabelle Events

Diese Tabelle beinhaltet alle Ereignisse, die im Kontext der Objekte stehen, wie z. B. die Herstellung des Objekts.

| Spalte | Art | Beschreibung | Hinweis |

|--------------|-----------|------------|------------|

| EventID* | Ganzzahl |Identifikationszeichen| Primärschlüssel, wird automatisch eingetragen |

| eventType* | Konzept | Art des Ereignisses (z. B. die Herstellung eines Objektes) ||

| eventName* | Text | Name des Ereignisses ||

| eventCulture | Konzept | Kann das Ereignis einer bestimmten Kultur zugeordnet werden? ||

| eventDate | Text | Wann war das Ereignis? | Das Datum wird in der Form YYYY-MM-DD angegeben. Bei einem Zeitraum können zwei so geformte Daten mit einem Halbgeviertstrich getrennt werden. DIe Angaben wir "vor", "nach" und "etwa" werden mit den Zeichen <, > und ~ angegeben und können vor jedem Datum stehen. Unbekannte Stellen des Datums dürfen weggelassen werden, also ist z. B. der Mai 1989 mit 1989-05 anzugeben. |

| eventMaterialsTech | Text | Welche Materialien und Techniken wurden angewendet ||

| eventSourceMaterialsTech | Text | Woher sind die Materialien und Techniken bekannt? ||

| eventDescription | Text | Beschreibung des Ereignisses ||

| eventDescriptionSource | Text | Woher stammt die Beschreibung? ||

mit * gekennzeichnete Spalten sind Pflichtfelder

Der Ort des Ereignisses wird durch eine Verknüpfung zur Tabelle Places über die Verknüpfungstabelle tookPartInPlace.

Beteiligte Personen werden als Verknüpfung mit der Tabelle Actors über die Verknüpfungstabelle isActorOf angelegt.

Die Verknüpfung mit einem Objekt erfolgt über die Verknüpfungstabelle tookPartInEvent und kann nur vom Objekt ausgehend eingetragen werden.

Tabelle Actors

Diese Tabelle beinhaltet alle Personen, die an den Ereignissen erwähnenswert sind.

| Spalte | Art | Beschreibung | Hinweis |

|--------------|-----------|------------|------------|

| ActorID* | Ganzzahl |Identifikationszeichen| Primärschlüssel, wird automatisch eingetragen |

| actorName* | Text | der Name der Person ||

| actorNationality | Konzept | die Nationalität der Person ||

| actorLifespan | Text | Die Lebensdaten werden automatisch in eine sinnvolle menschenlesbare Form gebracht. m Formular werden der Geburtstag und Todestag separat angegeben. Dabei gelten die gleichen Regeln, wie bei Events::eventDate ||

| actorGender | Konzept | das Geschlecht der Person ||

mit * gekennzeichnete Spalten sind Pflichtfelder

Personen sind über die Verknüpfungstabelle isActorOf mit der Tabelle Events verknüpft. Die Verknüpfung kann vom Ereignis aus bearbeitet werden.

Tabelle Places

Diese Tabelle beinhaltet alle Orte, die über die Ereignisse mit den Objekten verknüpft sind.

| Spalte | Art | Beschreibung | Hinweis |

|--------------|-----------|------------|------------|

| PlaceID* | Ganzzahl |Identifikationszeichen| Primärschlüssel, wird automatisch eingetragen |

| placeClassification | Konzept | Welcher Kategorie ist der Ort zugewiesen? ||

| placeName* | Text | der Name des Ortes ||

mit * gekennzeichnete Spalten sind Pflichtfelder

Orte können hierarchisch geordnet werden. So ist die Stadt Freiberg im Bundesland Sachsen, das wiederum Teil von Deutschland ist, welches wiederum in der EU ist und zum Kontinent Europa gehört. Der Verknüpfung erfolgt über eine Eltern-Kind-Beziehung in der Tabelle isPartOfPlace. Die Orte sollten nur soweit angelegt werden, wie sie für die Beschreibung der Ereignisse und der Objekte sinnvoll sind.

Die Verknüpfung eines Ortes mit einem Ereignis (Tabelle Events) erfolgt über die Tabelle tookPartInPlace und kann nur von der Seite der Ereignisse bearbeitet werden.

Tabelle Resources

Diese Tabelle enthält alle Informationen zu mit den Objekten verknüpften Dateien.

| Spalte | Art | Beschreibung | Hinweis |

|--------------|-----------|------------|------------|

| ResourceID* | Ganzzahl |Identifikationszeichen| Primärschlüssel, wird automatisch eingetragen |

| resourceType* | Konzept | Welche Art von datei ist es? ||

| resourcePerspective | Konzept | Bei Bildern: Gibt es eine spezielle Perspektive? ||

| resourceDescription | Text | Beschreibung der Datei ||

| resourceDescriptionSource | Text | Woher stammt die Beschreibung? ||

| resourceDate | Text | Wann wurde die datei erstellt? ||

| resourceSource | Text | Wer/Wo/in welchem Zusammenhang hat/wurde die Datei erstellt? ||

| resourceRights | Text | Wer hat welche Rechte an der Datei? ||

| resourceLink* | Text | Pfad zur Datei (relativ) ||

| resourceMeasurements | Konzept | JSON-Feld mit allen Zusatzinformationen. Für Bilder werden diese von dem Programm imagemagick automatisch ermittelt, können aber noch bearbeitet werden. ||

mit * gekennzeichnete Spalten sind Pflichtfelder

Die Verknüpfung der Dateien mit den Objekten erfolgt über die Tabelle isResourceOf und kann nur vom Objekt aus bearbeitet werden.

Verknüpfungstabellen

Diese Tabellen dienen der Verknüpfung der Datentabellen, wobei immer eine n:n-Verknüpfung möglich ist. Teilweise werden für die Verknüpfung weitere Angaben benötigt, die hier kurz beschrieben werden.

isRealted

Diese Tabelle verknüpft zwei Objekte. Die Felder parent und child beinhalten die beiden Objekte. Eine hierarchische Struktur ist möglich, aber aktuell nicht vorgesehen. Zusätzlich wird eine RealtionID vergeben und der relationType als Konzept angegeben.

isActorOf

Diese Tabelle verknüpft Ereignisse mit Personen. Dabei sind die Felder Event und Actor die jeweiligen Primärschlüssel der Tabellen. Zusätzliche Informationen sind im Konzept actorsRoleInEvent grob zusammengefasst und ggf. als Text in extendOfParticipation genauer spezifiziert.

tookPartInPlace

Diese Tabelle verknüpft Orte mit Ereignissen ohne Zusatzinformationen.

tookPartInEvent

Diese Tabelle verknüpft Objekte mit Ereignissen. Dabei sind die Felder Object und Event die jeweiligen Primärschlüssel der Tabellen. Zusätzlich kann die Rolle des Objektes im Ereignis mit dem Konzept roleInEvent angegeben werden.

isPartOfPlace

Diese Tabelle verknüpft zwei Orte in einer Eltern-Kind-Beziehung. Beide Felder sind Primärschlüssel der Places-Tabelle.

isResourceOf

Diese Tabelle verknüpft Dateien und Objekte miteinander. Dabei sind die Felder Object und Resource die jeweiligen Primärschlüssel der Tabellen. Zusätzlich muss das Konzept resourceRealationshipType zur Spezifizierung der Verknüpfung angegeben werden.

Das Dateneingabesystem

Das Standard-Dateneingabesystem ist eine Weboberfläche, die mit einem modernen Browser geöffnet werden kann. Im Folgenden werden sowohl die Programmierung als auch die Anwendung dokumentiert.

Die Anwendung des Dateneingabesystems ist an die Datenarten gekoppelt. Es gibt:

Die Eingabe der Daten erfolgt über ein Formular pro Datenart. Eine Übersicht über alle angelegten Einträge je Datenart. Von dort aus können zu allen Einträgen die Details betrachtet oder die Einträge selbst bearbeitet werden.

Die Struktur der Datenarten untereinander ist vom LIDO-Standard vorgegeben:

Objekte können mit Dateien und Ereignissen oder anderen Objekten verknüpft werden. Ereignisse wiederum mit Personen (den Beteiligten) und Orten. Orte können hierarchisch mit über- und untergeordneten Orten verknüpft sein.

Das LIDO-Format ist ein streng hierarchisches XML-Format. Ein Objekt stellt den Datenstamm dar. Jedes Ereignis, an dem das Objekt beteiligt war, ist ein Ast der Struktur. Die an dem Ereignis beteiligten Personen sind wiederum Zweige am Ereignis-Ast. Diese Struktur wird auch in der Eingabe der Verknüpfungen wiedergegeben: Es ist möglich, einem Ereignis eine Person zuzuweisen (vom Ast zum Zweig), aber nicht, einer Person ein Ereignis (vom Zweig zum Ast). Zur besseren Orientierung werden diese Zweit-zu-Ast-Verknüpfungen dennoch angezeigt, können aber nicht in dieser Richtung geändert werden. Es ist aber möglich, vom Zweig auf den Ast zu wechseln und dort Änderungen zum Zweig vorzunehmen. Vor dem Wechsel auf den Ast sollten alle Änderungen gespeichert werden, weil sie sonst verloren gehen.

Das beste Vorgehen ist es, erst alle benötigten Daten anzulegen und anschließend die Verknüpfungen einzufügen. Während der Digitalisierung werden alle Metadaten eines Objektes zusammen mit Fotos aufgenommen. Dabei werden die Metadaten der Objekte direkt in die Erfassungssoftware eingegeben. Die Bilder müssen anschließend nachbearbeitet werden (Farb- und Weißabgleich). Nach der Bearbeitung werden sie in die Erfassungssoftware eingepflegt und danach das jeweilige Objekt mit den Bildern verknüpft. Diese Eingabe kann zwar in der Weboberfläche erfolgen, wird allerdings effizienter mit einem Python-Skript erledigt (siehe unten).

Anpassung/Festlegung der Konzepte/Thesauri

Der LIDO-Standard legt einige Felder als Konzepte/Thesauri fest. Die Formulare enthalten immer Auswahlfelder, sofern es notwendig ist. Sollte einmal ein Begriff im Thesaurus fehlen, so muss er erst über die Thesauriverwaltung eingegeben und anschließend das Formular neu geladen werden. Beim Neuladen gehen aber alle nicht gespeicherten Änderungen verloren. Ggf. ist es sinnvoll, erst einen falschen Begriff zu speichern (oder keinen, sofern es kein Pflichtfeld ist) und anschließen, diesen Begriff zu ändern.

Programmierung

Die Programmierung ist mit php (Version 8) auf der Serverseite und JavaScript (clientseitig) erfolgt. Dabei ist der built-in-Server von php für die Benutzung ausreichend. Die Speicherung der Metadaten erfolgt in einer SQLite3-Datenbank.

Allgemeine Struktur

Alle php-Skripte bis auf wenige Ausnahmen sind im Wurzelverzeichnis des Servers zu finden. Die Ausnahmen sind der Header (header.php), der Footer (footer.php) und allgemein genutzte Funktionen (globalFunctions.php), die sich im Verzeichnis inc befinden.

Im Verzeichnis inc befinden sich außerdem: die Datenbank (Winklersammlung.sqlite3), die JavaScript-Datei (globalScripts.js) und die CSS3-Styledatei (style.css). Außer für die Thesauri sind alle Javascript-Funktionen in dieser Javascript-Datei. Des Weiteren befinden sich noch die HTML-Formular-Rümpfe für alle Datenarten in diesem Verzeichnis. Sie sind nach dem Prinzip Form.html benannt.

Im Verzeichnis img befinden sich die Bilder, die für die Webseite notwendig sind.

Durch den Benutzer hochgeladene Dateien (auch die Fotos) werden im Verzeichnis upload abgelegt. Das Zuordnen der Fotos zum Objekt über die Weboberfläche hat sich als ineffizient für die zu verwaltende Datenmenge herausgestellt, weshalb diese Funktion nicht genutzt wurde.

Thesauri

Im Wurzelverzeichnis gibt es die Dateien saveConcepts.php, thesauri.php und concepts.json, die für die Verwaltung der Thesauri benutzt werden. Dabei sind alle Konzepte (Thesauri) in der Datei concepts.json im JSON-Format gespeichert. Diese Speicherung ermöglicht eine schnelle Abfrage in verschiedenen Programmiersprachen, ohne die Datenbankschnittstelle zu benötigen. Das ist sinnvoll, weil die Begriffe im Thesaurus keiner großen Änderung unterliegen und nicht umfassend durchsucht werden müssen.

Formulare

Die Formulare sind als HTML-Rümpfe im Verzeichnis inc enthalten. Diese Rümpfe werden von der Datei forms.php je nach übergebenen GET-Parameter (form) geladen. Das Laden der Daten und das Speichern werden durch JavaScript mittels fetch-API durchgeführt.

Für alle die Datenbank betreffenden Aktionen gibt es Dateien mit der Bezeichnung Functions.php im Wurzelverzeichnis. Je nach übergebenen POST-Parameter wird JSON- oder HTML-Code zurückgegeben.

Listen

Die Listen sind in der Datei list.php implementiert. Diese Datei zeigt datenartabhängig (über GET-Variable type) eine Liste mit allen gespeicherten Datensätzen in einer übersichtlichen Tabelle an. Vorn hier aus kann auf die Datensätze für mehr Informationen ℹ (als modales Pop-up) oder zum Bearbeiten ✎ zugegriffen werden. Die Listen werden mit php generiert. Diese Funktion wurde während des Projekts kaum genutzt.

Pop-ups

Innerhalb der Pop-ups steht unveränderlicher Code. Sie dienen nur der Anzeige von Details. Die Pop-ups sind als ein einziger Container in den Dateien list.php und forms.php definiert. Der Inhalt wird jeweils mit JavaScripts fetch-API aus den Functions.php als HTML-Code geladen. Dabei werden intern die aufgerufenen Daten jeweils mit Art und ID gespeichert, sodass eine Navigation innerhalb des Pop-ups zu einem gewählten oder dem vorherigen Eintrag möglich ist. Bei jeder Änderung wird der gesamte Pop-up-Inhalt neu geladen. Auch diese Funktion wurde kaum genutzt.

Zusätzliche Eingabe durch Python-Skripte

Durch die Speicherung der Daten in einer SQLite3-Datenbank und der Konzepte in einer JSON-Datei ist es problemlos möglich, die Daten mit anderen Programmiersprachen zu bearbeiten. Die Weboberfläche ist für die Eingabe der Metadaten hervorragend geeignet. Die Verknüpfung der Objektdatensätze mit den aufgenommenen Fotos ist auf dieser Webseite aufgrund der hohen Menge an Daten ineffizient möglich. Daher wird diese Aufgabe durch ein Python-Skript erledigt. Ein weiterer Vorteil ist die Möglichkeit der Nutzung von Python-Modulen, insbesondere dem Modul minetypes, um die MIME-Type einer Ressource zu ermitteln.


#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

Created on Wed Aug 2 09:21:20 2023


@author: marcus

"""


import sqlite3

import pathlib

from PIL import Image

import mimetypes

import json

import os.path

import shutil

import sys

import spectrum


### Einstellungen


ObjektID = "794" # Weinstein

# Analysenart = '[5, "Infrarotspektroskopie-Rohdaten"]'

# Analysenart = '[6, "Infrarotspektroskopie-Darstellung"]'

# Analysenart = '[7, "Ramanspektroskopie-Rohdaten"]'

# Analysenart = '[8, "Ramanspektroskopie-Darstellung"]'

# Analysenart = '[9, "Röntgenfluoreszenzspektroskopie-Rohdaten"]'

# Analysenart = '[10, "Röntgenfluoreszenzspektroskopie-Darstellung"]'

# Analysenart = '[11, "Röntgendiffraktometrie-Rohdaten"]'

# Analysenart = '[12, "Röntgendiffraktometrie-Darstellung"]'

# Analysenart = '[13, "Massenspektrometrie-Rohdaten"]'

Analysenart = '[14, "Massenspektrometrie-Darstellung"]'


Datei = "/home/marcus/heptazin/Projekte/Winklersammlung_DFG/Analysen/MADAC2022/MS/GALDI_pos_weisserWeinstein.mzML"

Beschreibung = """Daten aus dem Praktikum zum Moul "Moderne Aspekte der Analytischen Chemie" im Sommersemester 2022"""

Datum = None # wird automatisch gelesen oder interaktiv abgefragt.


# Wird bei Bilder automatisch ermittelt, sonst bitte angeben!

if Datei.endswith(".dx"):

mimetype = "chemical/x-jcamp-dx" # Für JCAMP-DX-Dateien

blocks = spectrum.getJCAMPblockFromFile(Datei)

spec = spectrum.Spectrum()

spec.openJCAMPDXfromString(blocks[0])

Datum = spec.metadata["Notes"]["Date Time"].strftime("%Y-%m-%d")

elif Datei.endswith(".mzML"):

mimetype = "application/xml" # für mzml

elif pathlib.Path(Datei).suffix in [".png", ".svg", ".bmp", ".jpg", ".tif"]:

print("Found Image")

else:

print("Could not determine mimetype. Aborting.")

sys.exit(1)


if not Datum:

Datum = input("Bitte gebe das Datum der Datenerstellung an (Format JJJJ-MM-TT): ")


## Standard-Einstellungen

SavePath = "App/analyses/"


### Einstellungen Ende


def getResourceMesaurements(pfad):

Bild = Image.open(pfad)

mime, encoding = mimetypes.guess_type(pfad)

resourceMeasurements = {}

resourceMeasurements['mime-type'] = mime

resourceMeasurements['width'] = Bild.width

resourceMeasurements['height'] = Bild.height

return resourceMeasurements


# erstelle den richtigen Dateinamen

Analyse = json.loads(Analysenart)

Basisname = "TUBAF_CW_" + ObjektID + "_" + Analyse[1]

idx = 0

while os.path.exists(SavePath + Basisname + "_" + str(idx) + pathlib.Path(Datei).suffix.lower()):

idx += 1

fileName = SavePath + Basisname + "_" + str(idx) + pathlib.Path(Datei).suffix.lower()

if pathlib.Path(Datei).suffix in [".png", ".svg", ".bmp", ".jpg", ".tif"]:

resourceMeasurements = getResourceMesaurements(Datei)

else:

resourceMeasurements = {}

resourceMeasurements['mime-type'] = mimetype


shutil.copy2(Datei, fileName)


print(fileName)


con = sqlite3.connect("App/inc/Winklersammlung.sqlite3")

cur = con.cursor()


data = [

(

Analysenart,

"[-1, '']",

Beschreibung,

Datum,

"TU Bergakademie Freiberg - Deutsche Forschungsgemeinschaft (DFG) - Projektnummer 465474202",

"CC0 - TU Bergakademie Freiberg",

"../" + fileName[4:],

json.dumps(resourceMeasurements)

)]


cur.executemany("INSERT INTO Resources (resourceType, resourcePerspective, resourceDescription, resourceDate, resourceSource, resourceRights, resourceLink, resourceMeasurements) VALUES (?,?,?,?,?,?,?,?)", data)

con.commit()


dataResource = []

cur.execute("SELECT ResourceID FROM Resources WHERE resourceLink='../"+fileName[4:]+"'")

resourceID = cur.fetchone()[0]

print(resourceID)

dataResource.append((

ObjektID,

resourceID,

"[1, 'Analyse']"

))


cur.executemany("INSERT INTO isResourceOf(Object, Resource, resourceRelationshipType) VALUES (?,?,?)", dataResource)



con.commit()

con.close()


Ein ähnliches Skript wird auch für den Import von Analysendaten verwendet:


#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

Created on Wed Aug 2 09:21:20 2023


@author: marcus

"""


import sqlite3

import pathlib

from PIL import Image

import mimetypes

import json

import os.path

import shutil

import sys

import spectrum


### Einstellungen


ObjektID = "794" # Weinstein

# Analysenart = '[5, "Infrarotspektroskopie-Rohdaten"]'

# Analysenart = '[6, "Infrarotspektroskopie-Darstellung"]'

# Analysenart = '[7, "Ramanspektroskopie-Rohdaten"]'

# Analysenart = '[8, "Ramanspektroskopie-Darstellung"]'

# Analysenart = '[9, "Röntgenfluoreszenzspektroskopie-Rohdaten"]'

# Analysenart = '[10, "Röntgenfluoreszenzspektroskopie-Darstellung"]'

# Analysenart = '[11, "Röntgendiffraktometrie-Rohdaten"]'

# Analysenart = '[12, "Röntgendiffraktometrie-Darstellung"]'

# Analysenart = '[13, "Massenspektrometrie-Rohdaten"]'

Analysenart = '[14, "Massenspektrometrie-Darstellung"]'


Datei = "/home/marcus/heptazin/Projekte/Winklersammlung_DFG/Analysen/MADAC2022/MS/GALDI_pos_weisserWeinstein.mzML"

Beschreibung = """Daten aus dem Praktikum zum Moul "Moderne Aspekte der Analytischen Chemie" im Sommersemester 2022"""

Datum = None # wird automatisch gelesen oder interaktiv abgefragt.


# Wird bei Bilder automatisch ermittelt, sonst bitte angeben!

if Datei.endswith(".dx"):

mimetype = "chemical/x-jcamp-dx" # Für JCAMP-DX-Dateien

blocks = spectrum.getJCAMPblockFromFile(Datei)

spec = spectrum.Spectrum()

spec.openJCAMPDXfromString(blocks[0])

Datum = spec.metadata["Notes"]["Date Time"].strftime("%Y-%m-%d")

elif Datei.endswith(".mzML"):

mimetype = "application/xml" # für mzml

elif pathlib.Path(Datei).suffix in [".png", ".svg", ".bmp", ".jpg", ".tif"]:

print("Found Image")

else:

print("Could not determine mimetype. Aborting.")

sys.exit(1)


if not Datum:

Datum = input("Bitte gebe das Datum der Datenerstellung an (Format JJJJ-MM-TT): ")


## Standard-Einstellungen

SavePath = "App/analyses/"


### Einstellungen Ende


def getResourceMesaurements(pfad):

Bild = Image.open(pfad)

mime, encoding = mimetypes.guess_type(pfad)

resourceMeasurements = {}

resourceMeasurements['mime-type'] = mime

resourceMeasurements['width'] = Bild.width

resourceMeasurements['height'] = Bild.height

return resourceMeasurements


# erstelle den richtigen Dateinamen

Analyse = json.loads(Analysenart)

Basisname = "TUBAF_CW_" + ObjektID + "_" + Analyse[1]

idx = 0

while os.path.exists(SavePath + Basisname + "_" + str(idx) + pathlib.Path(Datei).suffix.lower()):

idx += 1

fileName = SavePath + Basisname + "_" + str(idx) + pathlib.Path(Datei).suffix.lower()

if pathlib.Path(Datei).suffix in [".png", ".svg", ".bmp", ".jpg", ".tif"]:

resourceMeasurements = getResourceMesaurements(Datei)

else:

resourceMeasurements = {}

resourceMeasurements['mime-type'] = mimetype


shutil.copy2(Datei, fileName)


print(fileName)


con = sqlite3.connect("App/inc/Winklersammlung.sqlite3")

cur = con.cursor()


data = [

(

Analysenart,

"[-1, '']",

Beschreibung,

Datum,

"TU Bergakademie Freiberg - Deutsche Forschungsgemeinschaft (DFG) - Projektnummer 465474202",

"CC0 - TU Bergakademie Freiberg",

"../" + fileName[4:],

json.dumps(resourceMeasurements)

)]


cur.executemany("INSERT INTO Resources (resourceType, resourcePerspective, resourceDescription, resourceDate, resourceSource, resourceRights, resourceLink, resourceMeasurements) VALUES (?,?,?,?,?,?,?,?)", data)

con.commit()


dataResource = []

cur.execute("SELECT ResourceID FROM Resources WHERE resourceLink='../"+fileName[4:]+"'")

resourceID = cur.fetchone()[0]

print(resourceID)

dataResource.append((

ObjektID,

resourceID,

"[1, 'Analyse']"

))


cur.executemany("INSERT INTO isResourceOf(Object, Resource, resourceRelationshipType) VALUES (?,?,?)", dataResource)



con.commit()

con.close()

Metadaten-Kuration

Die Metadaten wurden mind. zweimal von unterschiedlichen Personen überprüft und korrigiert. Dazu wurde eine weitere Web-Anwendung geschrieben, die lokal auf einem dafür eingerichteten Laptop ausgeführt wird. Die Anwendung ist dreispaltig aufgebaut: in der linken Spalte sind die Bilder zu dem Objekt zu sehen, in der rechten Spalte werden die Metadaten angezeigt; die mittlere Spalte ist ein Formular, in dem alle Metadaten geändert werden können. Beim Start des Programms wird der Name des Nutzers abgefragt und nach Abschluss der Bearbeitung eines Eintrags in die Datenbank als Erst- bzw. Zweitkurator eingetragen. In einem Cookie im Browser wird gespeichert, welcher Metadatensatz zuletzt bearbeitet wurde. Dadurch kann beim Programmstart direkt zu dem nächsten zu bearbeitenden Datensatz gesprungen werden. Es ist zu empfehlen, Konventionen zur z.B. Formatierung der mehrzeiligen Textfelder nebenbei z. B. in einer Textdatei zu notieren und möglichst viele Einträge hintereinander zu bearbeiten. Dadurch wird eine möglichst konsequente und einheitliche Metadatenstruktur erhalten.