# Inventar und Handbücher

# Dinge digital ordnen und einfach wiederfinden

<span lang="de">Dinge digital ordnen und einfach wiederfinden ... das klingt nach einer schweren Aufgabe - muss es aber nicht sein! Teedy (vormals "Sismics Docs") ist ein Open Source Enterprise Content Management-System (ECM) bzw. Dokumenten Management System (DMS) mit vielen Funktionen und einer modernen Benutzeroberfläche. Als Begrifflichkeit klingt das natürlich ziemlich langweilig. Allerdings ist ein solches System umseitig einsetzbar. Teedy ist ein auf Java basierter Webdienst, mit dem sich verschiedene Dateien hochladen und klassifizieren lassen. Durch geschicktes Tagging (Verstichworten) und die Wahl der Titel, sowie eine Volltextsuche mit OCR-Funktion erlauben ein sehr flinkes und einfaches Filtern.  
</span>

<span lang="de"> Wir nutzen so Teedy nicht nur für unseren ganzen Vereinspapierkram, sondern auch zum Verwalten unseres kompletten physischen Inventars. Die Digitialisierung unserer Werkstatt ist sehr hilfreich. Wir haben reichlich Werkzeuge und Maschinen in den einzelnen Bereichen und es ist manchmal einfach unübersichtlich - insbesondere für noch nicht so alteingesessene Stammmitglieder. Damit wir den Überblick nicht verlieren, arbeiten wir an einer dem Inventar gewidmeten Teedy-Instanz, mit der wir unsere Werkzeuge und Maschinen durchsuchen können. Ziel soll es unter anderem sein, dass hierbei nach Bereichen, Tätigkeiten, Herstellern und mehr gefiltert werden kann. Bei guter Pflege des Systems dauert es so nur wenige Sekunden, um sämtliche Maschinen zu finden, mit denen man beispielsweise etwas sägen oder bohren kann. Besonders toll ist, dass sich hierbei auch zugehörige Fotos, Handbücher, Protokolle und andere Dokumente eingefügt werden können. </span>

Ziel ist es zudem, dass jeder wichtige Gegenstand im FabLab seinen festen Platz hat, welcher von allen Mitgliedern stets nachvollziehbar ist. Da jedes Objekt im FabLab seine eigene eindeutige Artikelnummer und zugewiesene Eigenschaften bekommen soll, ist dann auch jedes Objekt nachvollziehbar in seiner kompletten Historie und Beschaffenheit. Als physischer Gegenpart zum Portal steht deshalb als Aufgabe das Labeln der Objekte mit Inventaraufklebern. Auf diesen Aufklebern sollen sich Angaben befinden, die das Objekt identifizieren. Die Aufkleber sind dann über eine Barcode Scanner-App einlesbar und öffnen direkt die spezifische Objekt-URL der Inventarplattplattform.

# Ein paar Screenshots unserer Instanz

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/Smid0HdbhF2MI8br-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/Smid0HdbhF2MI8br-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/UAFcnSrzXQeiGMeh-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/UAFcnSrzXQeiGMeh-grafik.png)

## Dokumentation

Wir nutzen Teedy nicht nur selbst, sondern haben auch jede Menge Dokumentation zur Server-Installation, Konfiguration und Nutzung dazu geschrieben. Siehe [Teedy (DMS) - How To's](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-installation-and-configuration). Die **ausführliche** Dokumentation zum Inventar-System (Konzept) findet sich unter [Werkstattorientierung im FabLab - Digtales Inventar](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/werkstattorientierung-im-fablab-digitales-inventar "Werkstattorientierung im FabLab - Digitales Inventar").

# Handbuchkisten in den Werkstätten

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/scaled-1680-/aYNHVTAT9IupYrlo-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/aYNHVTAT9IupYrlo-grafik.png)

Seit Februar 2026 gibt es in jeder Werkstatt eine Eurobox mit Deckel, die für Handbücheraufbewahrung konzipiert ist. Darin finden sich die Handbücher für die im jeweiligen Raum relevanten Dokumentationen in Papierform. Parallel finden sich alle Handbücher auch digital in unserer Inventarplattform [things.fablabchemmnitz.de](https://things.fablabchemmnitz.de).

## Gedanken zur Handbuchkiste

- **Sichtbarkeit &amp; Handling:**
    - die Kisten sollen in jeder Werkstatt gleich aussehen und somit schnell erkennbar sein
    - die Kisten sollen auffallen: deshalb bekommen sie eine große, farbige Beschriftung
    - zum Schutz vor Staub gibt es einen Klappdeckel
- **Abmessungen**: die Kisten sind so hoch, dass alle Handbücher reinpassen und noch Platz ist, außerdem sollen Dokumente ungeachtet ihres Formats stehen oder liegen können. In der Regel sind Handbücher A4, A5 oder kleiner. Außerdem gibt es für viele Geräte häufig verschiedene Dokumente. Deshalb benötigt es transparente Mappen oder Fächer, um dies sauber ablegen zu können. Dafür gibt es eine simple Einsatzkonstruktion. (**ToDo**)
- **Dubletten und Synchronisierung**: Manche Dinge im Verein haben wir mehrfach. Entsprechend gibt es auch verschiedene Handbücher mehrfach. Entsprechend der jeweiligen Standorte verteilen wir auch die Handbücher auf die korrekten Kisten. Im Falle, dass eine Sache dauerhaft ihren Raum wechselt, muss ggf. auch ein Handbuch von einer Kiste in eine andere Kiste wandern. Wird ein Objekt aus dem Vereinsinventar ausgebucht, z.B. durch Verkauf, dann muss auch das Handbuch entsprechend mitgegeben bzw. aus der Kiste entnommen werden.
- **Beschriften**: 
    - von außen: Jede Kiste bekommt ein Schild mit Beschriftung und QR-Codes, die u.a. auch auf diese Seite hier verlinken.
    - von innen: auf der Deckelinnenseite befindet sich ein Register mit Inhalt. Die Listendaten werden über eine SQL-Abfrage generiert. Das Register enthält die Spalten: `Id`, `Objekt`, `Spitzname`, `Werkstatt(unter)bereich`, eine Überschrift mit Inhalt der Raumnummern, für die die Kiste genutzt wird, sowie ein Datum des Registerauszugs.
    - von innen: Ein Hinweis, dass alle `Id`s über eine URL auch direkt aufgerufen werden können.
- **Kistenumfang**: Wir fassen kleinere Bereiche zusammen, damit keine fast leeren Kisten rumstehen. Die Verteilung der Handbuchkisten auf die Räume: 
    - A000 + A003 + A005, befindlich in der Grafikwerkstatt
    - A001 + A002, befindlich an der Säule im Workshopbereich
    - A004, befindlich in der Elektronikwerkstatt
    - A006 + A007, befindlich in der Metallwerkstatt
    - A008, befindlich in der Holzwerkstatt

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/scaled-1680-/315A8IDc7gXe1l11-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/315A8IDc7gXe1l11-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/scaled-1680-/9VmeQbkrk8IpYwYs-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/9VmeQbkrk8IpYwYs-grafik.png) [![IMG_20260310_111359.jpg](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/scaled-1680-/QaDgFU6abaeqxuAt-img-20260310-111359.jpg)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-03/QaDgFU6abaeqxuAt-img-20260310-111359.jpg)

Wir verwenden [diese 40 x 30 x 32 cm Euroboxen](https://www.hornbach.de/p/proregal-supersparset-5x-eurobox-nextgen-grip-mit-scharnierdeckel-und-blauen-schiebeschnappverschluessen-hxbxt-32x30x40cm-30-liter-schwarz-griffe-offen-eurobehaelter-transportbox-transportbehaelter-stapelbehaelter/12551901/) mit Deckel.

# Teedy API Scripts / database queries

# Auto-delete guest comments

<div class="page-metadata-wrapper" id="bkmrk-"></div><div class="page view" id="bkmrk-in-case-you-have-a-g"><article>In case you have a guest login enabled and don't want to accept guest spamming you can prevent it using the following bash script with cron trigger (running every 10 minutes). Guest comments are even useless because each guest can delete the guest comments from another guest session. So nobody can guarantee that those will exist a longer time. Deleting such stuff helps to keep clean useful documents which were **not created by guests** but regular users who wanted to put them to **public**.

# Comment deletion

<p class="callout info">This script is looking within i time fence of 10 minutes. If the script skipped in the meantime, it's possible that comments were overlooked. They have to be cleaned manually then.</p>

```bash
vim /opt/teedy-clean-comments.sh
```

```bash
#!/bin/bash
#check for commments which have been created the last 10 minutes. if result is not empty we send a new email
DB_USER="db_user"
DB_NAME="db_name"
OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
    SELECT
        t_document.doc_title_c,
        t_comment.com_content_c,
        t_comment.com_createdate_d||'\n'
    FROM t_comment
    JOIN t_user ON t_comment.com_iduser_c = t_user.use_id_c
    JOIN t_document ON t_comment.com_iddoc_c = t_document.doc_id_C
    WHERE
        t_document.doc_deletedate_d IS NULL AND
        t_comment.com_deletedate_d IS NULL AND
        t_user.use_username_c = 'guest' AND
        t_comment.com_createdate_d + interval '10 minute' >= now()
    ;
")
  
if [[ ! -z $OUT ]]; then
    #echo -e -n _${OUT}_
    #first inform about the comment via mail
    echo -e -n " "$OUT | mail -s "dms.yourdomain.de guest comments" post@fix.de
  
    OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
        SELECT
            t_comment.com_id_c
        FROM t_comment
        JOIN t_user ON t_comment.com_iduser_c = t_user.use_id_c
        JOIN t_document ON t_comment.com_iddoc_c = t_document.doc_id_C
        WHERE
            t_document.doc_deletedate_d IS NULL AND
            t_comment.com_deletedate_d IS NULL AND
            t_user.use_username_c = 'guest' AND
            t_comment.com_createdate_d + interval '10 minute' >= now()
        ;
    ")
 
    #echo $OUT
 
    BASE_URL="https://dms.yourdomain.de"
    BASE_URL="http://localhost:8080/dms"
    TEEDY_USER="teedy"
    AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
    if [ -z "$AUTH_TOKEN" ]
    then
        echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
        exit 1
    else
        for VAR in $OUT; do
            curl --silent -X DELETE -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/comment/$VAR" -k
        done
    fi
else
    echo "Nothing to send and nothing to fix ..."
fi
```

cron.d script in `/etc/cron.d/teedy-clean-comments`

```bash
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/10 * * * * root /opt/teedy-clean-comments.sh > /dev/null
```

You can also directly perform a drop statement by SQL instead (but that might hurt the audit log)

```sql
DELETE FROM t_comment WHERE com_id_c IN (
SELECT
com_id_c
FROM t_comment
JOIN t_user ON t_comment.com_iduser_c = t_user.use_id_c
JOIN t_document ON t_comment.com_iddoc_c = t_document.doc_id_c
WHERE
t_document.doc_deletedate_d IS NULL AND
t_comment.com_deletedate_d IS NULL AND
t_user.use_username_c = 'guest' AND
t_comment.com_createdate_d + interval '1 minute' >= now())
;
```

<div class="wiki-content" id="bkmrk--1"></div></article></div>

# Auto-delete guest documents

In case you have a guest login enabled and don't want to accept guest spamming you can prevent it using the following bash script with cron trigger (running every 10 minutes). Guest documents are even useless because each guest can delete the guest documents from another guest session. So nobody can guarantee that those will exist a longer time. Deleting such stuff helps to keep clean useful documents which were **not created by guests** but regular users who wanted to put them to **public**.

# Document deletion

<p class="callout info">This script is looking within i time fence of 10 minutes. If the script skipped in the meantime, it's possible that documents were overlooked. They have to be cleaned manually then.</p>

```bash
vim /opt/teedy-clean-documents.sh
```

```bash
#!/bin/bash
#check for documents which have been created the last 10 minutes. if result is not empty we send a new email
DB_USER="user"
DB_NAME="db"
OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
     SELECT
        D.doc_title_c,
        U.use_username_c,
        D.doc_createdate_d||'\n'
    FROM
        t_document AS D
    JOIN t_user AS U ON U.use_id_c = D.doc_iduser_c
    WHERE
        D.doc_deletedate_d IS NULL AND (
        D.doc_iduser_c IN (
        SELECT
            use_id_c
        FROM t_user AS U
        JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
        JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
        WHERE
            U.use_deletedate_d IS NULL AND
            UG.ugp_deletedate_d IS NULL AND
            G.grp_deletedate_d IS NULL AND
            G.grp_name_c NOT IN ('Editoren','Administratoren') AND
            D.doc_createdate_d + interval '10 minute' >= now()
        ) OR
        D.doc_iduser_c = 'guest') AND /*guest ist in keiner Gruppe, deshalb muss er gesondert aufgeführt werden*/
        D.doc_createdate_d + interval '10 minute' >= now()
    ;
    ")
  
if [[ ! -z $OUT ]]; then
    #echo -e -n _${OUT}_
    #first inform about the document via mail
    echo -e -n " "$OUT | mail -s "your.dms.de guest documents" webmaster@stadtfabrikanten.org
  
    OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
      SELECT
        D.doc_id_c
    FROM
        t_document AS D
    JOIN t_user AS U ON U.use_id_c = D.doc_iduser_c
    WHERE
        D.doc_deletedate_d IS NULL AND (
        D.doc_iduser_c IN (
        SELECT
            use_id_c
        FROM t_user AS U
        JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
        JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
        WHERE
            U.use_deletedate_d IS NULL AND
            UG.ugp_deletedate_d IS NULL AND
            G.grp_deletedate_d IS NULL AND
            G.grp_name_c NOT IN ('Editoren','Administratoren') AND
            D.doc_createdate_d + interval '10 minute' >= now()
        ) OR
        D.doc_iduser_c = 'guest') AND /*guest ist in keiner Gruppe, deshalb muss er gesondert aufgeführt werden*/
        D.doc_createdate_d + interval '10 minute' >= now()
    ;
   ")
 
    #echo $OUT
 
    BASE_URL="https://your.dms.de"
    BASE_URL="http://localhost:8080/dms"
    TEEDY_USER="pass"
    AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
    if [ -z "$AUTH_TOKEN" ]
    then
        echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
        exit 1
    else
        for VAR in $OUT; do
            curl --silent -X DELETE -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document/$VAR" -k
        done
    fi
else
    echo "Nothing to send and nothing to fix ..."
fi
```

cron.d script in `/etc/cron.d/teedy-clean-documents`

```bash
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/10 * * * * root /opt/teedy-clean-documents.sh > /dev/null
```

# Auto-delete guest tags

In case you have a guest login enabled and don't want to accept guest spamming you can prevent it using the following bash script with cron trigger (running every 10 minutes). Guest tags are even useless because each guest can delete the guest tags from another guest session. So nobody can guarantee that those will exist a longer time. Deleting such stuff helps to keep clean useful tags which were **not created by guests** but regular users who wanted to put them to **public**.

# Tag deletion

This script is looking within i time fence of 10 minutes. If the script skipped in the meantime, it's possible that comments were overlooked. They have to be cleaned manually then.

```bash
vim /opt/teedy-clean-tags.sh
```

```bash
#!/bin/bash
#check for tags which have been created the last 10 minutes. if result is not empty we send a new email
DB_USER="user"
DB_NAME="db"
OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
    /*werden Tags gelöscht, wenn der Benutzer gelöscht wird?*/
    SELECT
        T.tag_name_c,
        U.use_username_c,
        T.tag_createdate_d||'\n'
    FROM
        t_tag AS T
    JOIN t_user AS U ON U.use_id_c = T.tag_iduser_c
    WHERE
        T.tag_deletedate_d IS NULL AND (
        T.tag_iduser_c IN (
        SELECT
            use_id_c
        FROM t_user AS U
        JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
        JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
        WHERE
            U.use_deletedate_d IS NULL AND
            UG.ugp_deletedate_d IS NULL AND
            G.grp_deletedate_d IS NULL AND
            G.grp_name_c NOT IN ('Editoren','Administratoren') AND
            T.tag_createdate_d + interval '10 minute' >= now()
        ) OR
        T.tag_iduser_c = 'guest') AND /*guest ist in keiner Gruppe, deshalb muss er gesondert aufgeführt werden*/
        T.tag_createdate_d + interval '10 minute' >= now()
    ;
 
")
  
if [[ ! -z $OUT ]]; then
    #echo -e -n _${OUT}_
    #first inform about the document via mail
    echo -e -n " "$OUT | mail -s "your.dms.de guest tags" post@fix.org
  
    OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
        SELECT
            T.tag_id_c
        FROM
            t_tag AS T
        JOIN t_user AS U ON U.use_id_c = T.tag_iduser_c
        WHERE
            T.tag_deletedate_d IS NULL AND (
            T.tag_iduser_c IN (
            SELECT
                use_id_c
            FROM t_user AS U
            JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
            JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
            WHERE
                U.use_deletedate_d IS NULL AND
                UG.ugp_deletedate_d IS NULL AND
                G.grp_deletedate_d IS NULL AND
                G.grp_name_c NOT IN ('Editoren','Administratoren') AND
                T.tag_createdate_d + interval '10 minute' >= now()
            ) OR
            T.tag_iduser_c = 'guest') AND
            T.tag_createdate_d + interval '10 minute' >= now()
        ;
    ")
 
    #echo $OUT
 
    BASE_URL="https://your.dms.de"
    BASE_URL="http://localhost:8080/dms"
    TEEDY_USER="password"
    AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
    if [ -z "$AUTH_TOKEN" ]
    then
        echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
        exit 1
    else
        for VAR in $OUT; do
            echo
            curl --silent -X DELETE -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/tag/$VAR" -k
        done
    fi
else
    echo "Nothing to send and nothing to fix ..."
fi
```

cron.d script in `/etc/cron.d/teedy-clean-comments`

```bash
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/10 * * * * root /opt/teedy-clean-tags.sh > /dev/null
```

# Change owner of tags/files/documents

Within Teedy, there is no function to change the owner of a document. So we cannot transfer docs from one user to another. In case we want to keep documents in the system, we may not delete the author of the document. We can only deactivate the user. Under some circumstances this is going to make the system untidy.

<p class="callout warning">The following steps should be only be done in convenience with DSGVO.</p>

Warning: all files in Teedy are encrypted by the users `use_privatekey_c` value from `t_user` table. In case we change the owner, we have to decrypt and encrypt the file again. We need to know the correct mapping between file and user each. So please do not try to migrate multiple users at once. At the moment we have no routine to do the re-encryption by console commands.

The belonging files are:

<div class="confluence-information-macro-body" id="bkmrk-https%3A%2F%2Fgithub.com%2Fs">- [https://github.com/sismics/docs/blob/master/docs-core/src/main/java/com/sismics/docs/core/listener/async/FileProcessingAsyncListener.java](https://github.com/sismics/docs/blob/master/docs-core/src/main/java/com/sismics/docs/core/listener/async/FileProcessingAsyncListener.java)
- [https://github.com/sismics/docs/blob/master/docs-core/src/main/java/com/sismics/docs/core/util/EncryptionUtil.java](https://github.com/sismics/docs/blob/master/docs-core/src/main/java/com/sismics/docs/core/util/EncryptionUtil.java)

</div>**So do the following steps at your own risk!**

 Stop Jetty

```
sudo systemctl stop jetty11
```

**Take care** to make a backup of `/var/docs` and the database before doing the following steps:

```sql
--get some info
select * from t_document where doc_title_c =  '<some title of the users OLD_ID to determine his/her OLD_ID';
select * from t_document where doc_iduser_c = 'OLD_ID'; --we check the docs of the OLD_ID user
 
--now update the tables
update t_document set doc_iduser_c =  'NEW_ID' WHERE doc_iduser_c = 'OLD_ID';
update t_file set fil_iduser_c =  'NEW_ID' WHERE fil_iduser_c = 'OLD_ID';
update t_tag set tag_iduser_c =  'NEW_ID' WHERE tag_iduser_c = 'OLD_ID';
update t_comment set com_iduser_c =  'NEW_ID' WHERE com_iduser_c = 'OLD_ID';
update t_contributor set ctr_iduser_c =  'NEW_ID' WHERE ctr_iduser_c = 'OLD_ID';
update t_audit_log set log_iduser_c =  'NEW_ID' WHERE log_iduser_c = 'OLD_ID';
update t_acl set acl_targetid_c =  'NEW_ID' WHERE acl_targetid_c = 'OLD_ID';
```

```
sudo systemctl start jetty11
```

After the change, you should deactivate the old user in the UI backend.

# Create new documents which act as collectors

This is an example for document names with german titles. We call this script once per month to automatically create new documents. We can use [https://crontab.guru/](https://crontab.guru/) to generate a time schedule.

```bash
vim /etc/cron.d/teedy-prepare-monthly.sh
```

```bash
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 1 1 * *     root   /opt/teedy-prepare-monthly.sh > /dev/null
# “At 01:00 on day-of-month 1.”
```

```bash
vim /opt/teedy-prepare-monthly.sh
```

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
DB_USER="teedy"
DB_NAME="teedy_db"
TEEDY_USER="admin"
AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
if [ -z "$AUTH_TOKEN" ]
then
    echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
    exit 1
else
    THIS_MONTH=`date +'%m' -d 'now'` #return the recent month in format 01 ... 12
    THIS_YEAR=`date +'%Y' -d 'now'` #return the recent year
    #echo $THIS_MONTH
    #echo $THIS_YEAR
 
    #generate the date of the last day of the recent month
    TARGET_DATESTRING=$(date --date="$(date +$THIS_YEAR'-'$THIS_MONTH'-'01) + 1 month - 1 day 00:00" +"%s")000
    #echo $TARGET_DATESTRING
 
    #list of desired tags (clear name). Get the ID from database
    TAGID_SAMMELDOKUMENT=$(    psql -t -U$DB_USER $DB_NAME --command="SELECT tag_id_c FROM t_tag WHERE tag_name_c = 'Sammeldokument' AND tag_deletedate_d IS NULL;")
    TAGID_RECHNUNG=$(          psql -t -U$DB_USER $DB_NAME --command="SELECT tag_id_c FROM t_tag WHERE tag_name_c = 'Rechnung' AND tag_deletedate_d IS NULL;")
    TAGID_RECHNUNGSKORREKTUR=$(psql -t -U$DB_USER $DB_NAME --command="SELECT tag_id_c FROM t_tag WHERE tag_name_c = 'Rechnungskorrektur' AND tag_deletedate_d IS NULL;")
    TAGID_AUFTRAG=$(           psql -t -U$DB_USER $DB_NAME --command="SELECT tag_id_c FROM t_tag WHERE tag_name_c = 'Auftrag' AND tag_deletedate_d IS NULL;")
    TAGID_LIEFERSCHEIN=$(      psql -t -U$DB_USER $DB_NAME --command="SELECT tag_id_c FROM t_tag WHERE tag_name_c = 'Lieferschein' AND tag_deletedate_d IS NULL;")
    TAGID_ANGEBOT=$(           psql -t -U$DB_USER $DB_NAME --command="SELECT tag_id_c FROM t_tag WHERE tag_name_c = 'Angebot' AND tag_deletedate_d IS NULL;")
 
        TAGID_SAMMELDOKUMENT=${TAGID_SAMMELDOKUMENT:1}
              TAGID_RECHNUNG=${TAGID_RECHNUNG:1}
    TAGID_RECHNUNGSKORREKTUR=${TAGID_RECHNUNGSKORREKTUR:1}
               TAGID_AUFTRAG=${TAGID_AUFTRAG:1}
          TAGID_LIEFERSCHEIN=${TAGID_LIEFERSCHEIN:1}
               TAGID_ANGEBOT=${TAGID_ANGEBOT:1}
 
    #Create new documents - WARNING: NO CHECK FOR DUPLICATE DOCUMENTS RIGHT NOW
    curl --silent -X PUT -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document" -d "title=Ausgangsrechnungen "$THIS_YEAR"\\"$THIS_MONTH -d "create_date="$TARGET_DATESTRING -d "language=deu" -d "tags="$TAGID_RECHNUNG -d "tags="$TAGID_SAMMELDOKUMENT
    curl --silent  -X PUT -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document" -d "title=Ausgangsrechnungskorrekturen "$THIS_YEAR"\\"$THIS_MONTH -d "create_date="$TARGET_DATESTRING -d "language=deu" -d "tags="$TAGID_RECHNUNGSKORREKTUR -d "tags="$TAGID_SAMMELDOKUMENT
    curl --silent  -X PUT -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document" -d "title=Ausgangsaufträge "$THIS_YEAR"\\"$THIS_MONTH -d "create_date="$TARGET_DATESTRING -d "language=deu" -d "tags="$TAGID_AUFTRAG -d "tags="$TAGID_SAMMELDOKUMENT
    curl --silent  -X PUT -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document" -d "title=Ausgangslieferscheine "$THIS_YEAR"\\"$THIS_MONTH -d "create_date="$TARGET_DATESTRING -d "language=deu" -d "tags="$TAGID_LIEFERSCHEIN -d "tags="$TAGID_SAMMELDOKUMENT
    curl --silent  -X PUT -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document" -d "title=Ausgangsangebote "$THIS_YEAR"\\"$THIS_MONTH -d "create_date="$TARGET_DATESTRING -d "language=deu" -d "tags="$TAGID_ANGEBOT -d "tags="$TAGID_SAMMELDOKUMENT
 
fi
```

# Find ugly document titles

This statement looks for titles with unrequired whitespace duplicates

```sql
/*title which contain doubled whitespaces*/
SELECT
    doc_title_c
FROM t_document
WHERE
    LENGTH(RTRIM(LTRIM(doc_title_c))) <> LENGTH(doc_title_c) OR
    doc_title_c LIKE '%  %' AND
    doc_deletedate_d IS NULL
;
```

# Gastzugang und spezielle Anpassungen (serverseitiger Code/Scripts)

Wir wollen unsere Inventarplattform so aufbereiten, dass sie auch für Gäste etwas zum Stöbern bietet und die Funktionen von Teedy als praktibale Open Source Software zeigt. Teedy hat einen Gastmodus, der von Haus aus leider neben der regulären Verwendung auch ein gewisses Spamming per Web-Browser und API erlaubt. Zur Reduktion haben wir Scripts geschrieben, die dies unterbinden.

Wir möchten Kommentare und wild angelegte Tags und Dokumente damit vermeiden, da sie die Struktur zu leicht unlesbar machen. Gäste können Kommentare, Tags und Dokumente von anderen Gastsitzungen generell einfach modifizieren oder löschen. Deshalb eignet sich der Gastmodus nur als reiner Betrachtermodus. Selbiges trifft auch geteilte/generische) ReadOnly-Benutzer zu.

Die Speicher-Quota für den Gast und für sonstige ReadOnly-Benutzer beträgt 0 MB.

Neben Gast-Tags werden Tags von anderen Nutzern, die keine Admins oder Editoren sind, ebenso vom System erkannt und automatisch gelöscht. Wir behalten uns vor unser System für Mitglieder zu kapseln, sodass das Tool nur noch vereinsintern und nicht durch Gäste genutzt werden kann, falls zu viele Schabernackversuche auftreten.

### Scripts (diese laufen jeweils als cron job alle 10 Minuten)

- **Kommentare** (erzeugt von "guest" user) werden automatisch gescannt und gelöscht 
    - Siehe [Auto-delete guest comments](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/auto-delete-guest-comments "Auto-delete guest comments").
    - Ein Wiederherstellen ist mit dem Zurücksetzen der **com\_deletedate\_d** Spalte möglich
    - Der Haupt-Admin erhält eine Mail über alle gelöschten Kommentare ins Webmaster-Postfach
- **Dokumente** (erzeugt von "guest" user oder anderen Benutzern, die nicht den Gruppen "Editoren" oder "Administratoren" angehören) werden automatisch gescannt und ebenso gelöscht 
    - Siehe [Auto-delete guest documents](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/auto-delete-guest-documents "Auto-delete guest documents").
    - Ein Wiederherstellen ist mit dem Zurücksetzen der **doc\_deletedate\_d** Spalte möglich. Ein Wiederherstellen der Dateien ist allerdings nicht möglich
    - Der Haupt-Admin erhält eine Mail über alle gelöschten Dokumente ins Webmaster-Postfach
- **Tags** (erzeugt von "guest" user oder anderen Benutzern, die nicht den Gruppen "Editoren" oder "Administratoren" angehören) werden automatisch gescannt und gelöscht 
    - Siehe [Auto-delete guest tags](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/auto-delete-guest-tags "Auto-delete guest tags")
    - Ein Wiederherstellen ist mit dem Zurücksetzen der **tag\_deletedate\_d** Spalte möglich
    - Der Haupt-Admin erhält eine Mail über alle gelöschten Tags ins Webmaster-Postfach

## Manuelle Pflege der Tag-Besitzer

Wer Tags sehen und bearbeiten kann, kann vom Admin entweder über die Datenbank oder das User Interface gesteuert werden.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/96jgSf7mpy2rUsCR-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/96jgSf7mpy2rUsCR-grafik.png)

Aus Gründen der Gesamtübersicht (Grafana ACL Tabelle) entfernen wir Einzelbenutzer und definieren **nur** Gruppen. Leider können in Teedy Tags nicht von ihrem Einzelbenutzer getrennt werden. Zumindest können die Tags nicht gelöscht werden, die vom Haupt-Admin erstellt worden sind. Letzteres ist nur über Datenbank Update SQL Scripts änderbar:

```bash
/*Finde alle Tags, die nicht dem Haupt-Admin gehören. Gast-Tags tauchen hier generell nicht auf*/
SELECT
    T.tag_name_c AS "Tag",
    U.use_username_c AS "Ersteller",
    T.tag_createdate_d AS "Erstelldatum"
FROM
    t_tag AS T
JOIN t_user AS U ON U.use_id_c = T.tag_iduser_c
WHERE
    T.tag_deletedate_d IS NULL AND
    T.tag_iduser_c IN (
        SELECT
            use_id_c
        FROM t_user AS U
        JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
        JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
        WHERE
            U.use_deletedate_d IS NULL AND
            UG.ugp_deletedate_d IS NULL AND
            G.grp_deletedate_d IS NULL
    ) AND
    U.use_id_c != 'admin'
;
 
/*
Übertrage Ersteller/Eigentümer des Tags von anderen Editor/Administratoren auf den Haupt-Administrator.
Dazu müssen nachträglich ebenso die ACLs angepasst werden. Die Spalte "tag_iduser_c" spielt für die Berechtigung keine direkte Rolle sondern enthält nur die Information, wer den Tag initial erstellt hat.
Aber da wir in den ACLs den User gegen admin tauschen, machen wir es somit konsistent!
*/
/*UPDATE t_tag SET tag_iduser_c = 'admin';*/ /*dieses Statement ist überflüssig und sollte nicht ausgeführt werden*/
 
/*
Finde alles, was nicht "Editoren", "ErweiterteBetrachter" oder "Aktivmitglieder" ist.
*/
SELECT
    A.acl_id_c,
    T.tag_name_c "Tag",
    A.acl_perm_c AS "Permission",
    U.use_username_c
FROM t_tag AS T
JOIN t_acl AS A ON A.acl_sourceid_c = T.tag_id_c
LEFT JOIN t_user AS U ON U.use_id_c = A.acl_targetid_c
WHERE
    T.tag_deletedate_d IS NULL AND
    --A.acl_deletedate_d IS NULL AND
    U.use_disabledate_d IS NULL AND
    U.use_id_c = 'admin'
ORDER BY T.tag_name_c
;
 
/*
Wir wollen die ACLs nur auf Gruppen festlegen. Wir nehmen auch dem Haupt-Admin die Benutzerberechtigungen, da dieser Einzelbenutzer die Übersichtlichkeit (in Grafana) künstlich aufbläht.
Berechtigungen für Einzelnutzer soll es generell nicht geben (Ausnahme Tag "public" für den User "guest")
*/
UPDATE t_acl SET acl_deletedate_d = NOW() WHERE acl_id_c IN (
    SELECT
        A.acl_id_c
    FROM t_tag AS T
    JOIN t_acl AS A ON A.acl_sourceid_c = T.tag_id_c
    LEFT JOIN t_user AS U ON U.use_id_c = A.acl_targetid_c
    WHERE
        T.tag_deletedate_d IS NULL AND
        U.use_disabledate_d IS NULL AND
        U.use_id_c = 'admin'
    )
;
```

## Manuelle Pflege der Dokumenten-Besitzer

Wer Dokumente sehen und bearbeiten kann, kann vom Admin entweder über die Datenbank oder das User Interface gesteuert werden:

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/ZTseOkJwOxouLDzc-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/ZTseOkJwOxouLDzc-grafik.png)

```bash
/*Finde alle Dokumente, die nicht dem Haupt-Admin gehören. Gast-Tags tauchen hier generell nicht auf*/
SELECT
    D.doc_title_c AS "Gegenstand",
    U.use_username_c AS "Ersteller",
    D.doc_createdate_d AS "Erstelldatum"
FROM
    t_document AS D
JOIN t_user AS U ON U.use_id_c = D.doc_iduser_c
WHERE
    D.doc_deletedate_d IS NULL AND
    D.doc_iduser_c IN (
        SELECT
            use_id_c
        FROM t_user AS U
        JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
        JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
        WHERE
            U.use_deletedate_d IS NULL AND
            UG.ugp_deletedate_d IS NULL AND
            G.grp_deletedate_d IS NULL
    ) AND
    U.use_id_c != 'admin'
;
 
 
/*
Übertrage Ersteller/Eigentümer des Tags von anderen Editor/Administratoren auf den Haupt-Administrator.
Dazu müssen nachträglich ebenso die ACLs angepasst werden. Die Spalte "doc_iduser_c" spielt für die Berechtigung keine direkte Rolle sondern enthält nur die Information, wer das Dokument initial erstellt hat.
Aber da wir in den ACLs den User gegen admin tauschen, machen wir es somit konsistent!
*/
/*UPDATE t_document SET doc_iduser_c = 'admin';*/ /*dieses Statement ist überflüssig und sollte nicht ausgeführt werden*/
 
/*
Finde alles, was nicht "Editoren", "ErweiterteBetrachter" oder "Aktivmitglieder" ist.
*/
SELECT
    A.acl_id_c,
    D.doc_title_c "Gegenstand",
    A.acl_perm_c AS "Permission",
    U.use_username_c
FROM t_document AS D
JOIN t_acl AS A ON A.acl_sourceid_c = D.doc_id_c
LEFT JOIN t_user AS U ON U.use_id_c = A.acl_targetid_c
WHERE
    D.doc_deletedate_d IS NULL AND
    --A.acl_deletedate_d IS NULL AND
    U.use_disabledate_d IS NULL AND
    U.use_id_c = 'admin'
ORDER BY D.doc_title_c
;
```

# Reindex / index repairing script

<div class="page view" id="bkmrk-this-script-is-for-u"><article>This script is for use as cron for example.

## By teedy username and password

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
AUTH_TOKEN=$(curl -i -X POST -d username="username" -d password="password" "$BASE_URL/api/user/login" -k|grep "auth_token"|cut -c24-59)
if [ -z "$AUTH_TOKEN" ]
then
    echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
    exit 1
else
    curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/app/batch/reindex" -k
    curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/user/logout" -k
fi
```

## By database user and password

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
DB_USER="teedy
DB_NAME="teedy_db"
TEEDY_USER="theuser"
AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
if [ -z "$AUTH_TOKEN" ]
then
    echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
    exit 1
else
    curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/app/batch/reindex" -k
fi
```

<div class="wiki-content" id="bkmrk-"></div></article></div>

# Reprocess all files for a given user

- Note that you can only re-process files which are your's! If you want to reprocess everything for everybody you will need to loop over each user while getting his username/passwort or auth\_token (only way if user has 2FA secured account) from database. An auth\_token can only stripped out from database if the user did not log off (otherwise it's null/empty).
- reprocessing makes sense in case of updating tesseract version which might improve the OCR text output quality. So for longterm storing of documents it might be worth to reprocess files.
- Reprocessing will raise a lot of background tasks into schedule:  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/9tm02JYpGgg6QuR5-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/9tm02JYpGgg6QuR5-grafik.png)
- Note: Reprocessing of files does not work in quick upload mask (only works for files assigned to existing documents):  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/JbIuXmhqOsqX4kRD-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/JbIuXmhqOsqX4kRD-grafik.png)

## Way 1: pure API calls → for a regular user (no 2FA secured)

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
TEEDY_USER="admin"
TEEDY_USER_PASS="password"
AUTH_TOKEN=$(curl -i -X POST -d username="$TEEDY_USER" -d password="$TEEDY_USER_PASS" "$BASE_URL/api/user/login" -k|grep "auth_token"|cut -c24-59)
BACKUP_DIR="/backup/teedy"
TARGET_DOCLIST_JSON=$BACKUP_DIR"/documentlist_forfiles.json"
TARGET_FILELIST_JSON=$BACKUP_DIR"/filelist.json"
mkdir -p "$BACKUP_DIR"
rm $TARGET_DOCLIST_JSON
rm $TARGET_FILELIST_JSON
echo "Retrieving document list"
curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document/list?limit=0" -k | jq . > "$TARGET_DOCLIST_JSON"
echo "Retrieving file list based on document list"
COUNT=0
jq -c '.|{documents}|.[]|.[]|{id}+{title}+{create_date}' "$TARGET_DOCLIST_JSON" | while read -r i; do
    COUNT=$((COUNT + 1))
    DOC_ID=$(jq -c '.|{id}|.id' <<< $(printf '%s\n' "$i"))
    DOC_ID=${DOC_ID:1:-1}
    curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/list?id=$DOC_ID" >> "$TARGET_FILELIST_JSON"
    echo Getting $COUNT : $DOC_ID
    #put some sleep time here if your server has less ressources. Otherwise you might overload PostgreSQL as well as Jetty. It leads to unstability of the running instance
    #have a look at https://dms.yourdomain.de/#/settings/monitoring to check the average OCR time per document. Usually 3 to 10 seconds should be normal
    #sleep 5
done
echo "Starting to process files"
COUNT=0
jq -c '.[]|.[]|{id}' "$TARGET_FILELIST_JSON" | while read -r i; do
        COUNT=$((COUNT + 1))
    FILE_ID=$(jq -c '.|{id}|.id' <<< $(printf '%s\n' "$i"))
        FILE_ID=${FILE_ID:1:-1}
        echo Processing $COUNT : $FILE_ID
        curl  --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/$FILE_ID/process"
done
curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/user/logout" -k
```

## Way 2: API + psql calls → for a regular user (no 2FA secured)

This script is much shorter and more elegant to reprocess. Note that this script logins in the user by a fresh created token. It will fail if the user login is secured with 2FA. In this case see below!

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
DB_USER="teedy"
DB_NAME="teedy_db"
TEEDY_USER="admin"
TEEDY_USER_PASS="password"
AUTH_TOKEN=$(curl -i -X POST -d username="$TEEDY_USER" -d password="$TEEDY_USER_PASS" "$BASE_URL/api/user/login" -k|grep "auth_token"|cut -c24-59)
 
for FILE_ID in $(psql -t -U$DB_USER $DB_NAME --command="SELECT fil_id_c FROM t_file AS F JOIN t_user AS U ON U.use_id_c = F.fil_iduser_c WHERE F.fil_deletedate_d IS NULL AND U.use_username_c = '$TEEDY_USER';"); do
    echo PROCESSING "$FILE_ID"
    curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/$FILE_ID/process"
    sleep 5
done
 
#logout
curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/user/logout" -k
```

## Way 3: API + psql calls → for a single 2FA secured user

The following script can be used if your user is secured with 2FA. Note that the user needs to be logged in once (if he logs off the recent token will be destroyed).

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
DB_USER="teedy"
DB_NAME="teedy_db"
TEEDY_USER="admin"
 
#this reads exactly one token from the given user. If the user is not logged in it will be null and the token will be null too!
AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
 
for FILE_ID in $(psql -t -U$DB_USER $DB_NAME --command="SELECT fil_id_c FROM t_file AS F JOIN t_user AS U ON U.use_id_c = F.fil_iduser_c WHERE F.fil_deletedate_d IS NULL AND U.use_username_c = '$TEEDY_USER';"); do
        echo PROCESSING "$FILE_ID"
        #curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/$FILE_ID/process"
    sleep 2
done
 
#do not logout to keep the token
```

## Way 4: API + psql calls → loop over all users (2FA secured users)

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
DB_USER="teedy"
DB_NAME="teedy_db"
TEEDY_USERS=$(psql -t -U$DB_USER $DB_NAME --command="SELECT use_username_c FROM t_user;")
 
for TEEDY_USER in $TEEDY_USERS; do
  echo LOGGING IN AS $TEEDY_USER
  #this reads exactly one token from the given user. If the user is not logged in it will be null and the token will be null too!
  AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
  for FILE_ID in $(psql -t -U$DB_USER $DB_NAME --command="SELECT fil_id_c FROM t_file AS F JOIN t_user AS U ON U.use_id_c = F.fil_iduser_c WHERE F.fil_deletedate_d IS NULL AND U.use_username_c = '$TEEDY_USER';"); do
    echo PROCESSING "$FILE_ID"
    curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/$FILE_ID/process"
    sleep 2
  done
done
#do not logout after processing (to keep the token alive!)
```

# Scan for users without groups

To have better control about users, which logged in first time by LDAP and did not get mapped to default group, we use some SQL / bash script to inform the webmaster about that:

```bash
vim /opt/teedy-unmapped-users.sh
```

```bash
#!/bin/bash
#check for new users which have been created the last 10 minutes, but have no propery group membership. if result is not empty we send a new email
DB_USER="db_user"
DB_NAME="db_name"
OUT=$(psql -t -U$DB_USER $DB_NAME --no-align --command="
SELECT DISTINCT
    use_username_c,
	use_email_c
FROM t_user AS U
WHERE
    U.use_deletedate_d IS NULL AND
    U.use_disabledate_d IS NULL AND
    U.use_username_c NOT IN ('guest')
    
EXCEPT
    
SELECT DISTINCT
    use_username_c,
	use_email_c
     --count(use_id_c) AS "Gruppenanzahl"
FROM t_user AS U
JOIN t_user_group AS UG ON UG.ugp_iduser_c = U.use_id_c
JOIN t_group AS G ON G.grp_id_c = UG.ugp_idgroup_c
WHERE
    U.use_deletedate_d IS NULL AND
    U.use_disabledate_d IS NULL AND
    UG.ugp_deletedate_d IS NULL AND
    G.grp_deletedate_d IS NULL AND
    G.grp_name_c NOT IN ('Administratoren', 'Group2', 'Editors', 'Viewers') AND
    U.use_username_c NOT IN ('admin', 'anotherUser', 'anotherUser2')
GROUP BY use_email_c, use_username_c, use_id_c
;

")
 
if [[ ! -z $OUT ]]; then
    #echo -e -n _${OUT}_
	BASE_URL="https://dms.domain.org"
	BASE_URL="http://localhost:8080/dms"
	TEEDY_USER="ujoZzkKw2g"
	AUTH_TOKEN=$(psql -t -U$DB_USER $DB_NAME --command="SELECT aut_id_c FROM t_authentication_token AS A JOIN t_user AS U ON U.use_id_c = A.aut_iduser_c WHERE use_username_c = '$TEEDY_USER' AND aut_lastconnectiondate_d IS NOT NULL LIMIT 1;")
	if [ -z "$AUTH_TOKEN" ]
	then
    	echo "NO AUTHTOKEN. Please create a session for the user first to automate things!" >&2 #print to stderr to trigger cron.d mail on error
    	exit 1
	else
		for LINE in $OUT; do
			IFS='|' read -r -a LINE <<< $OUT
			USER=${LINE[0]}
			EMAIL=${LINE[1]}
			echo $USER
			echo $EMAIL
			MSG="Mapping $USER to 'Aktivmitglieder'"
			curl -X PUT -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/group/Aktivmitglieder" -d "username=$USER" -k | jq .
			echo $MSG
		    echo -e -n " "$MSG | mail -s "dms.domain.org | Dein Account wurde freigeschalten" webmaster@domain.org #copy
		    echo -e -n " "$MSG | mail -s "dms.domain.org | Dein Account wurde freigeschalten" $MAIL
		done
	fi
else
    echo "No users to map to groups ..."
fi
```

# Undelete document

Once a document was deleted, we can restore most information except document files:

```sql
SELECT * FROM  t_document WHERE doc_id_c = 'b9a538d2-906f-4566-b00d-ee4aa70d8ff8';
UPDATE t_document SET doc_deletedate_d = NULL  WHERE doc_id_c = 'b9a538d2-906f-4566-b00d-ee4aa70d8ff8';
```

# Teedy File and Document Processing

# Check index integrity / recompute quota

Sometimes the used space is wrong and may look like this:

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/QpOECnTwbQHLCyY8-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/QpOECnTwbQHLCyY8-grafik.png)

See also: [https://github.com/sismics/docs/issues/345](https://github.com/sismics/docs/issues/345)

## Checks on file system

```bash
cd /var/docs/
 
find ./ -type f -name '*' -exec du -ch {} + | grep total$
find ./ -type f -name '*_web' -exec du -ch {} + | grep total$
find ./ -type f -name '*_thumb' -exec du -ch {} + | grep total$
 
cd /var/docs/storage/
ll |grep -v "thumb\|web" |wc -l #Anzahl der Dateien, die weder _web noch _thumb sind
ll | grep "web" | wc -l         #Anzahl _web Dateien (sollte idealerweise mit _thumb deckend sein)
ll | grep "thumb" | wc -l       #Anzahl _thumb Dateien (sollte idealerweise mit _web deckend sein)
```

## Checks in Database / Recalculate Quota

**Get a list of all files from database which should exist/not exist on filesystem**

```sql
/*Get all files which should be existent on HDD*/
SELECT
fil_id_c AS "FileID",
fil_iduser_c AS "Besitzer"
FROM t_file
WHERE fil_deletedate_d IS NULL
;
 
/*Get all files which should be deteled from HDD*/
SELECT
fil_id_c AS "FileID",
fil_iduser_c AS "Besitzer"
FROM t_file
WHERE fil_deletedate_d IS NOT NULL
;
```

**Get the filesystem storage list**

The following commands generate file output and send them by mail to you. If your application server and your database are on the same server you just can create some bash script to make some complete scripting solution automating psql.

```bash
cd /var/docs/
 
#get recent storage list as CSV
ll | grep -v "thumb\|web" | awk 'NR > 3 {print "\"",$5,"\";\"",$9,"\""}' | sed 's/[[:blank:]]//g' | mail -s "Teedy FileSystem" your@mail.address
 
#or get it as SQL statement
ll | grep -v "thumb\|web" | awk 'NR > 3 {print "UPDATE#TMP_QUOTA_CHECK#SET#fil_size=\x27",$5,"\x27#WHERE#fil_id_c=\x27",$9,"\x27;"}' | sed 's/[[:blank:]]//g' | sed 's/#/ /g' | mail -s "Teedy FileSystem" your@mail.address
```

**Create a temporary SQL table to calculate quota**

```sql
CREATE TABLE TMP_QUOTA_CHECK(
    fil_id_c  character varying(36),
    fil_iduser_c  character varying(36),
    fil_size integer
);
```

**Pre-Fill the table with existing data**

```sql
INSERT INTO TMP_QUOTA_CHECK SELECT
fil_id_c,
fil_iduser_c
FROM t_file
WHERE fil_deletedate_d IS NULL
;
```

**Insert file size data from upper generated SQL UPDATE awk statements (bash), then perform some check and calculate total quota sizes**

```sql
SELECT * FROM tmp_quota_check WHERE fil_size IS NOT NULL;
SELECT * FROM tmp_quota_check WHERE fil_size IS NULL; --if your filesystem is consistent this must be empty! If not please check if Teedy failed to delete files in the past
 
SELECT
    CONCAT('UPDATE t_user SET use_storagecurrent_n=''',SUM(fil_size),''' WHERE use_id_c =''',fil_iduser_c,''';')
FROM TMP_QUOTA_CHECK
GROUP BY fil_iduser_c
;
```

Insert the new values into existing target table using the generated output statements from above

**Drop temporary table**

```sql
DROP TABLE TMP_QUOTA_CHECK;
```

### Removed commit

[https://github.com/sismics/docs/commit/d0335b6b161058250ec8cc44eeb2357f96176f54#diff-54e77110186f974f07aeb29c2673496fL690](https://github.com/sismics/docs/commit/d0335b6b161058250ec8cc44eeb2357f96176f54#diff-54e77110186f974f07aeb29c2673496fL690)

# Fix Preview Bug

In case the file preview is erroneous/empty but the file can be processed and it can be downloaded by URL like [https://dms.yourdomain.de/api/file/:FILE\_ID/data](https://dms.yourdomain.de/api/file/:FILE_ID/data):

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/KjIXuU25Hl733DtU-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/KjIXuU25Hl733DtU-grafik.png)

Root Cause: unkown. Seems to happen after migration from H2 to PostgreSQL

## Fixing proposal

### Remove the \_thumb and \_web files

and let Teedy create new ones by running a (complete) re-processing

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/SjmkuitYxvaI31rJ-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/SjmkuitYxvaI31rJ-grafik.png)

```bash
cd /var/docs/storage
ll | grep "<YOUR_FILE_ID>"
mv <YOUR_FILE_ID>_thumb <YOUR_FILE_ID>_thumb.bak
mv <YOUR_FILE_ID>_web <YOUR_FILE_ID>_web.bak
 
#or just move all stuff to some sub directory if you plan to re-process the complete file system:
mkdir thumb_web_bak
mv *_thumb *_web thumb_web_bak/
 
#restart your instance to let Teedy recognize that changes due to caching
sudo systemctl restart jetty9.service
```

### Reprocess documents

See [Teedy API Scripts / database queries](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-api-scripts-database-queries "Teedy API Scripts / database queries") for reprocessing of everything.

# Grafana Monitoring / Statistics

## Description

A Grafana monitoring dashboard for Teedy (Sismics Docs) statistics. Helpful to have a look over security things and the effort you put in your instance. Please check if this is okay for your own use - regarding privacy protection of the mates working together on the same instance. Sorry the language for that dashboard is german but you can translate it easily using tools like [deepl.com](https://deepl.com).

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/Yru8yOGezlHucHYg-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/Yru8yOGezlHucHYg-grafik.png)

## Download

- [https://gitea.fablabchemnitz.de/vmario/teedy-statistics/src/branch/master](https://gitea.fablabchemnitz.de/vmario/teedy-statistics/src/branch/master)
- [https://grafana.com/grafana/dashboards/11556](https://grafana.com/grafana/dashboards/11556)

# Icons in document titles

Inside Teedy we can use funny icons, if we know the correct Unicode number.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/5eXzAbc5z4wS3U9V-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/5eXzAbc5z4wS3U9V-grafik.png)

### Copy + Paste. Just select the one's you need and paste them into Teedy. It works for title and description

☀ ☁ ☂ ☃ ☄ ★ ☆ ☇ ☈ ☉ ☊ ☋ ☌ ☍ ☎ ☏ ☐ ☑ ☒ ☓ ☔ ☕ ☖ ☗ ☘ ☙ ☚ ☛ ☜ ☝ ☞ ☟ ☠ ☡ ☢ ☣ ☤ ☥ ☦ ☧ ☨ ☩ ☪ ☫ ☬ ☭ ☮ ☯ ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷ ☸ ☹ ☺ ☻ ☼ ☽ ☾ ☿ ♀ ♁ ♂ ♃ ♄ ♅ ♆ ♇ ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ♔ ♕ ♖ ♗ ♘ ♙ ♚ ♛ ♜ ♝ ♞ ♟ ♠ ♡ ♢ ♣ ♤ ♥ ♦ ♧ ♨ ♩ ♪ ♫ ♬ ♭ ♮ ♯ ♰ ♱ ♲ ♳ ♴ ♵ ♶ ♷ ♸ ♹ ♺ ♻ ♼ ♽ ♾ ♿ ⚀ ⚁ ⚂ ⚃ ⚄ ⚅ ⚆ ⚇ ⚈ ⚉ ⚊ ⚋ ⚌ ⚍ ⚎ ⚏ ⚐ ⚑ ⚒ ⚓ ⚔ ⚕ ⚖ ⚗ ⚘ ⚙ ⚚ ⚛ ⚜ ⚝ ⚞ ⚟ ⚠ ⚡ ⚢ ⚣ ⚤ ⚥ ⚦ ⚧ ⚨ ⚩ ⚪ ⚫ ⚬ ⚭ ⚮ ⚯ ⚰ ⚱ ⚲ ⚳ ⚴ ⚵ ⚶ ⚷ ⚸ ⚹ ⚺ ⚻ ⚼ ⚽ ⚾ ⚿ ⛀ ⛁ ⛂ ⛃ ⛄ ⛅ ⛆ ⛇ ⛈ ⛉ ⛊ ⛋ ⛌ ⛍ ⛎ ⛏ ⛐ ⛑ ⛒ ⛓ ⛔ ⛕ ⛖ ⛗ ⛘ ⛙ ⛚ ⛛ ⛜ ⛝ ⛞ ⛟ ⛠ ⛡ ⛢ ⛣ ⛤ ⛥ ⛦ ⛧ ⛨ ⛩ ⛪ ⛫ ⛬ ⛭ ⛮ ⛯ ⛰ ⛱ ⛲ ⛳ ⛴ ⛵ ⛶ ⛷ ⛸ ⛹ ⛺ ⛻ ⛼ ⛽ ⛾ ⛿ ✅

[https://en.wikipedia.org/wiki/Miscellaneous\_Symbols](https://en.wikipedia.org/wiki/Miscellaneous_Symbols)

# Importer for Windows

- The Bulk file importer tool is based on NodeJS
- Documentation also available under [https://github.com/sismics/docs/tree/master/docs-importer](https://github.com/sismics/docs/tree/master/docs-importer)

## Download the importer


The importer can be also downloaded at Github. The most recent version to find is [https://github.com/sismics/docs/releases/download/v1.5/docs-importer-win.exe](https://github.com/sismics/docs/releases/download/v1.5/docs-importer-win.exe) (which is an old one). We can also build ourselves. See below.

## Building the importer

### Requirements

<div class="rwui_item_content" id="bkmrk-nodejs-v10.18.0-%2864-">- NodeJS v10.18.0 (64 Bit) → [https://nodejs.org/dist/v10.18.0/node-v10.18.0-win-x64.zip](https://nodejs.org/dist/v10.18.0/node-v10.18.0-win-x64.zip) - newer version will fail!
- Git → [https://git-scm.com/download/win](https://git-scm.com/download/win)

</div><p class="callout warning">Check your `%PATH%` variable. This should contain the following executables</p>

<div class="rwui_item_content" id="bkmrk-nodejs.exe-git.exe">- nodejs.exe
- git.exe

</div>### Open elevated CMD Shell

1. press CTRL + R to open "Run"
2. Enter "cmd"
3. Click ok  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/nMFy823Mp6AK1zw7-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/nMFy823Mp6AK1zw7-grafik.png)
4. Start elevated shell from current cmd. This will open up a new cmd shell with admin privileges  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/TgfxVC1R943eZfwA-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/TgfxVC1R943eZfwA-grafik.png)

### Clone Repository and run build

```bash
cd C:\
git clone https://github.com/sismics/docs.git
cd docs\docs-importer
npm install
npm install -g pkg
pkg .
```

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/w70yXkcpxpI0Z6AL-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/w70yXkcpxpI0Z6AL-grafik.png)

### Check the built output

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/QajGjfQVu0mH3HtR-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/QajGjfQVu0mH3HtR-grafik.png)

## Configure to use the importer

### Create new share upload directory

<span lang="en">A special upload folder should be created, e.g. `C:\TeedyShare` - from this folder the documents will be uploaded and **cut later**.</span>

### Start docs-importer-win.exe and configure it

Please put the docs-importer-win.exe to some fixed place where it should stay, like in `C:\Teedy\docs-importer-win.exe`

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/YSMRyAvFzw1YQ3Ug-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/YSMRyAvFzw1YQ3Ug-grafik.png)

<p class="callout info">**N**ote that the screenshot contains some older directory name.</p>

After entering the connection data this information will be persisted in `%userprofile%\config\preferences\com.sismics.docs.importer.pref`

### Start as daemon and test upload

<span lang="en">The program can be started with the switch `-d`. It queries the specified folder every 30 seconds and uploads any existing documents to the DMS. The files are then deleted locally.</span>

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/23K0K88bcQdw2Kkc-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/23K0K88bcQdw2Kkc-grafik.png)

### Install as Windows Service

Create file `C:\Teedy\teedy-service.ps1`

```bash
Start-Process -WindowStyle hidden -FilePath C:\Teedy\docs-importer-win.exe -ArgumentList "-d"
```

Create a new task in task scheduler

<p class="callout info">Sorry for german screenshots. And please replace "SismicsDocs" with "Teedy" everywhere.</p>

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/kHu6S2fRo8qjHB6z-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/kHu6S2fRo8qjHB6z-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/jesZEsrhn0D4BNqq-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/jesZEsrhn0D4BNqq-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/5yJ6I3DeAqIgLH5P-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/5yJ6I3DeAqIgLH5P-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/7NXRNQ3u8O6iYScE-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/7NXRNQ3u8O6iYScE-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/u5qBGUwde7SEcnuV-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/u5qBGUwde7SEcnuV-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/gLwsNX6JDHUxf3pr-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/gLwsNX6JDHUxf3pr-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/gmXChlQkS5FPuMHI-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/gmXChlQkS5FPuMHI-grafik.png)

Check if service is running. Look for `docs-importer-win.exe`

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/8GqOEZBqrYWsBntr-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/8GqOEZBqrYWsBntr-grafik.png)

Create a new Desktop Shortcut for your share directory

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/mHtwqOb80bL21cir-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/mHtwqOb80bL21cir-grafik.png)

# Manually fix broken document relations in database

## Find documents which have relations to other documents which already were deleted

Sometimes documents link to other documents but the links are invalid because the linked document is not available anymore. We can manually reset those links only because there is no working mechanism yet.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/NH9xxdz1QDOY72Y4-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/NH9xxdz1QDOY72Y4-grafik.png)

### Get all relations with their id's

```sql
SELECT
    R.rel_id_c,
    F.doc_title_c "From",
    T.doc_title_c "To"
FROM
    t_relation AS R
JOIN t_document AS T ON R.rel_iddocfrom_c = T.doc_id_c
JOIN t_document AS F ON R.rel_iddocto_c = F.doc_id_c
WHERE
    R.rel_deletedate_d IS NULL AND
    T.doc_deletedate_d IS NOT NULL AND
    F.doc_deletedate_d IS NULL
 
UNION
 
SELECT
    R.rel_id_c,
    T.doc_title_c "From",
    F.doc_title_c "To"
FROM
    t_relation AS R
JOIN t_document AS T ON R.rel_iddocfrom_c = T.doc_id_c
JOIN t_document AS F ON R.rel_iddocto_c = F.doc_id_c
WHERE
    R.rel_deletedate_d IS NULL AND
    T.doc_deletedate_d IS NULL AND
    F.doc_deletedate_d IS NOT NULL
     
ORDER BY "From"
;
```

Now we just set the delete flag of the relation to a date not NULL so it will unshow in Teedy visually. That will remove invalid links.

```sql
update t_relation SET rel_deletedate_d = NOW() WHERE rel_id_c IN ('your relation id 1', 'your relation id 2', .. , 'your relation id n');
```

# Optical Character Recognition (OCR) and Scanning

## Handling

OCR data is stored in Teedy database table `t_file` which containts the string column `fil_content_c`. In H2 the data is stored als plaintext string. In PostgreSQL the column is filled as datatype `::text`. A normal select returns number. The unecrypted OCR text data can be accessed from the large object by using some SQL statement like

```sql
select
fil_name_c,
convert_from(loread(lo_open(fil_content_c::int, 131072), 999999999), 'UTF8')
from t_file WHERE fil_deletedate_d IS NULL AND fil_content_c IS NOT NULL limit 1;
```

Teedy uses a built in process runner to start the binary `tesseract` with a language parameter. This works if "`tesseract`" is contained in `$PATH` (Linux) or `%PATH%` (Windows) environment variable.

## Fixing faulty fil\_content\_c data the easy way

Some quick fix for issue described in [https://github.com/sismics/docs/issues/451](https://github.com/sismics/docs/issues/451)

```sql
SELECT fil_content_c FROM t_file
WHERE  LENGTH(fil_content_c) > 6
ORDER BY fil_createdate_d DESC;
 
UPDATE t_file SET fil_content_c = NULL
WHERE  LENGTH(fil_content_c) > 6;
```

Converting LOB data to plain text (was required at some point from updating Teedy 1.8 to Teedy 1.9)

```sql
/*
Show items which start with useless linefeeds. We need to correct those because otherwise we cannot continue with following statements (casting "fil_content_c::int" will fail and other issues)
Result may be empty
*/
SELECT
    fil_id_c,
    fil_name_c,
    fil_content_c
FROM t_file
WHERE
    fil_content_c LIKE E'%\n'
;
 
/*Trim beginning linefeeds (only) away*/
UPDATE t_file SET fil_content_c = TRIM(e'\n' FROM fil_content_c)
WHERE
    fil_content_c LIKE E'%\n'
;
 
/*
Show faulty data which would return "invalid byte sequence for encoding "UTF8": 0x00" or similar.
First we build some function to check for valid UTF8 bytea because sometimes we have faulty stuff inside DB
Result may be empty
*/
CREATE FUNCTION is_valid_utf8(bytea) RETURNS boolean
   LANGUAGE plpgsql AS
$$BEGIN
   PERFORM convert_from($1, 'UTF8');
   RETURN TRUE;
EXCEPTION
   WHEN character_not_in_repertoire THEN
      RAISE WARNING '%', SQLERRM;
      RETURN FALSE;
END;$$;
SELECT
    fil_id_c,
    fil_name_c,
    loread(lo_open(fil_content_c::int, CAST( x'20000' AS integer)), 999999999) AS BYTE_DATA,
    LENGTH(loread(lo_open(fil_content_c::int, CAST(x'20000' AS integer)), 999999999)) AS LEN
FROM t_file
WHERE
    fil_content_c IS NOT NULL AND
    fil_content_c != '' AND
    LENGTH(fil_content_c) <= 6 AND
    is_valid_utf8(fil_content_c::bytea) IS FALSE
;
 
/*We set NULL to all items with faulty UTF-8 encoding (if there were some from previous statement)*/
UPDATE t_file SET fil_content_c = NULL
WHERE 
    fil_content_c IS NOT NULL AND
    fil_content_c != '' AND
    LENGTH(fil_content_c) <= 6 AND
    is_valid_utf8(fil_content_c::bytea) IS FALSE
;
 
/*
Select OCR content which is in LOB format (Large Object) and valid UTF-8
*/
SELECT
    fil_id_c,
    fil_name_c,
    fil_content_c,
    fil_content_c::bytea, /*shows "invisible" data which does not trigger NULL or ''*/
    loread(lo_open(fil_content_c::int, CAST( x'20000' AS integer)), 999999999) AS BYTE_DATA,
     /*we use the encoding we used to create the database. See setup instructions. Usually this is "UNICODE" or "UTF8"*/
    LENGTH(loread(lo_open(fil_content_c::int, CAST(x'20000' AS integer)), 999999999)) AS LEN,
    convert_from(loread(lo_open(fil_content_c::int, CAST(x'20000' AS integer)), 999999999), 'UNICODE') as "fil_content_c"
FROM t_file
WHERE
    fil_content_c IS NOT NULL AND
    fil_content_c != '' AND
    LENGTH(fil_content_c) <= 6 AND
    is_valid_utf8(fil_content_c::bytea) IS TRUE
ORDER BY LEN ASC
;
 
/*Convert LOB data into plain text. First we do it for a custom selected file with fil_id_c*/
UPDATE t_file SET fil_content_c = convert_from(loread(lo_open(fil_content_c::int, CAST( x'20000' AS integer)), 999999999), 'UNICODE')::TEXT
WHERE
    fil_id_c = '13411bb0-12fd-4e25-b483-2e2d18b344ed'
;
 
/*Check the conversion value*/
SELECT
    fil_id_c,
    fil_name_c,
    fil_content_c
FROM t_file
WHERE
    fil_id_c = '13411bb0-12fd-4e25-b483-2e2d18b344ed'
;
 
/*
Now we do mass processing for LOB to plain text
DO NOT CONTINUE WITH OTHER STATEMENTS IF THIS ONE FAILS AND CHECK THE UPPER ONES AGAIN
*/
UPDATE t_file SET fil_content_c = convert_from(loread(lo_open(fil_content_c::int, CAST( x'20000' AS integer)), 999999999), 'UNICODE')::TEXT
WHERE
    fil_content_c IS NOT NULL AND
    fil_content_c != '' AND
    LENGTH(fil_content_c) <= 6 AND
    is_valid_utf8(fil_content_c::bytea) IS TRUE
;
 
/*We fix again useless linefeeds by trimming*/
UPDATE t_file SET fil_content_c = TRIM(e'\n' FROM fil_content_c)
WHERE
    fil_content_c LIKE E'%\n'
;
 
/*
Now that we converted all the LOB stuff we do mass processing for remaining stuff with length lesser than 6 chars because those OCR values are just crap
WARNING: DO NOT RUN THIS BEFORE CONVERTING BECAUSE YOU WILL OVERWRITE. IF YOU DID YOU WILL NEED TO REPROCESS ALL DOCUMENTS!
*/
UPDATE t_file SET fil_content_c = NULL
WHERE
    fil_content_c IS NOT NULL AND
    fil_content_c != '' AND
    LENGTH(fil_content_c) <= 6
;
 
/*Finally we check again the values visually*/
SELECT
    fil_id_c,
    fil_name_c,
    fil_content_c
FROM t_file
 
/*Finally re-run the indexing from background UI web interface or API to have a good search index again*/
```

## Tesseract OCR command line binary

The installation of tesseract is simple. Note that for different operating system versions there are different tesseract versions. All tesseract versions work different in their speed and quality. We figured out that tesseract 3 on Ubuntu 16 works much faster than tesseract 4 on Ubuntu 18.

<a rel="nofollow">https://github.com/tesseract-ocr/tesseract/wiki</a>

### Installation

For Linux users:

```bash
#install regular version
sudo apt install tesseract-ocr tesseract-ocr-deu #will install the most recent version belonging to your OS. So older system you might get older tesseract
 
#install devel version. See https://launchpad.net/~alex-p/+archive/ubuntu/tesseract-ocr-devel
sudo add-apt-repository ppa:alex-p/tesseract-ocr-devel sudo apt-get update
sudo apt install tesseract-ocr tesseract-ocr-deu #add your desired languages here
```

For Windows users:

[https://github.com/tesseract-ocr/tesseract/wiki/4.0-with-LSTM#4x-for-windows](https://github.com/tesseract-ocr/tesseract/wiki/4.0-with-LSTM#4x-for-windows)

### Critical optimization

[https://github.com/tesseract-ocr/tesseract/issues/2611](https://github.com/tesseract-ocr/tesseract/issues/2611)

Some users said that disabling multiprocessing in tesseract fixes speed problems. Therefore some environment flag should be set using export. See also [Environment Configuration](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/environment-configuration "Environment Configuration")

```bash
export OMP_THREAD_LIMIT=1
```

## Scanner Apps for Smartphones

There are a LOT of scanner apps in PlayStore. Most of them have nearly same naming. The following list is only a minimalistic overview of stuff around the web. Mainly we are looking for open source applications.

- [Genius Scan](https://play.google.com/store/apps/details?id=com.thegrizzlylabs.geniusscan.free&hl=de)
- [CamScanner](https://play.google.com/store/apps/details?id=com.intsig.camscanner&hl=de&gl=US)
- [Notebloc](https://play.google.com/store/apps/details?id=com.notebloc.app&hl=en_IN)
- [OpenNoteScanner](https://github.com/ctodobom/OpenNoteScanner)
- [SwiftScan](https://swiftscan.app/de/index.html)

Wishes

- automatic upload to or sending by mail
- problem: what if you use multiple instances of DMS? Then you will need multiple upload locations. All known app do not deal with that feature. With app cloning the scanner app could be multiplied so each Scanner app instance has its own configuration. Then the scanner app cand send to the correct inbox per DMS instance

# Searching and Tags

## Tags

- <span lang="en">Tags can be nested. For example, the "Insurance" tag can be created and, for example, the "Signal Iduna" and "Ammerländer" tags below the tag. These are child elements. If you search for "Ammerländer", you will only find documents that are tagged with Ammerländer. If you search for "insurance", you will find documents that are tagged with "insurance", "Ammerländer" or "Signal Iduna" at the same time.</span>
- **<span lang="en">Unfortunately, tags can be created twice! Attention!</span>**

## Search operators

<div class="table-wrap" id="bkmrk-operator-values-expl"><table role="grid"><colgroup><col></col><col></col><col></col></colgroup><thead><tr><td>Operator</td><td>values</td><td>Explanation</td></tr></thead><tbody><tr role="row"><td>by:</td><td>String</td><td>The creator of the document</td></tr><tr role="row"><td>tag:</td><td>String</td><td>document with given tag</td></tr><tr role="row"><td>!tag:</td><td>String</td><td>document without given tag</td></tr><tr role="row"><td>before:</td><td>date (allowed formats: yyyy or yyyy-MM or yyyy-MM-dd)</td><td>created before date</td></tr><tr role="row"><td>ubefore:</td><td>date (allowed formats: yyyy or yyyy-MM or yyyy-MM-dd)</td><td>edited before date</td></tr><tr role="row"><td>after:</td><td>date (allowed formats: yyyy or yyyy-MM or yyyy-MM-dd)</td><td>created after date</td></tr><tr role="row"><td>uafter:</td><td>date (allowed formats: yyyy or yyyy-MM or yyyy-MM-dd)</td><td>edited after date</td></tr><tr role="row"><td>at:</td><td>date (allowed formats: yyyy or yyyy-MM or yyyy-MM-dd)</td><td>created at date</td></tr><tr role="row"><td>uat:</td><td>date (allowed formats: yyyy or yyyy-MM or yyyy-MM-dd)</td><td>edited at date</td></tr><tr role="row"><td>lang:</td><td>"eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi\_sim", "chi\_tra", "jpn", "tha", "kor", "nld", "tur", "heb"</td><td>language</td></tr><tr role="row"><td>mime:</td><td>  
</td><td><p class="callout warning">does not work yet!</p>

1. image/jpeg
2. application/zip
3. application/pdf
4. image/png
5. text/csv
6. text/plain
7. application/vnd.openxmlformats-officedocument.presentationml.presentation
8. application/vnd.openxmlformats-officedocument.wordprocessingml.document
9. application/octet-stream

</td></tr><tr role="row"><td>shared:</td><td>yes, no</td><td>  
</td></tr><tr role="row"><td>workflow:</td><td>"me", String</td><td>  
</td></tr><tr role="row"><td>full:</td><td>String</td><td><span lang="en">Use OCR full-text search (files must have been processed with Tesseract!) - full search is default since Teedy 1.9  
</span>

</td></tr><tr role="row"><td>simple:</td><td>String</td><td>Performs simple search instead full search (ignores OCR)</td></tr><tr role="row"><td>\*</td><td>  
</td><td><span lang="en">Wildcard only possible at the end of the search input string. Not allowed before or in a word</span>

</td></tr><tr role="row"><td>|</td><td>  
</td><td>Pipe operator. Use this to filter things like "or". Example

- green|duck 
    - find docs which have green or duck in title

</td></tr><tr role="row"><td>"&lt;string&gt;"</td><td>  
</td><td>phrases can be put into quotes. This will return a more exact result. For example:

- "a green duck" 
    - rreturns docs with the exact title "a green duck"
- a green duck 
    - returns docs which contain a, green or duck

</td></tr></tbody></table>

</div><span lang="en">The operators ?, NOT, AND, OR are not possible - they do nothing. [Lucene Core Dokumentation](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Boolean%20operators) → Most operators unfortunately don't work in Teedy.</span>

<p class="callout info"><span lang="en">All other things which cannot be expressed by the given search parameters can be scripted by SQL queries for H2 or PSQL database instead. You will need to have according access to do this.</span></p>

<span lang="en">You can find a lot of useful SQL statement for filtering out your DMS in our Grafana Dashboard → [Grafana Monitoring / Statistics](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/grafana-monitoring-statistics "Grafana Monitoring / Statistics")</span>

# Example scheme for document title for things like invocies

`<YYYY>\<MM> - <creditor> <type> <document number> [#<index>]`

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-" style="border-width: 1px;"><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-rdark nogutter  bash" id="bkmrk--1"></div></div></div></div># Search Filter - Combination from words, tags and other operators

Example: find documents which are tagged by invoice and company and which have "01" and "2018" in their title

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk--2" style="border-width: 1px;"><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-rdark nogutter  bash" id="bkmrk--3"></div></div></div></div>`tag:company tag:invoice 2018 01`

# Teedy Installation and Configuration

**About Teedy Document Management System**

Welcome to the unofficial documentation space for Teedy. Teedy (prior "Sismics Docs") is an open source enterprise content management system (ECM) and/or document management system (DMS) with a lot of features and a modern user interface. We use it for different purposes.

Source Code: [github.com/sismics/docs](https://github.com/sismics/docs)

Homepages: [sismics.com](https://www.sismics.com/) and [teedy.io](https://teedy.io/#!/)

# Access to the H2 Database

External Database access for H2 can be granted on different ways. Teedy stores its H2 database in standard path `/var/docs/db`

## Variant 1: Download H2 Database Tool

1. Download → [<span>http://www.h2database.com/h2-2019-03-13.zip</span>](http://www.h2database.com/h2-2019-03-13.zip)  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/vDi50q650IzkCmJD-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/vDi50q650IzkCmJD-grafik.png)
2. Put your database **copy/backup** files (docs.mv.db and dovs.trace.db) into some directory where you can access it
3. Use it

**Run the h2.bat file and connect via Web Interface, or**

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/lGfsEHXAP8Xw21yv-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/lGfsEHXAP8Xw21yv-grafik.png)

**Connect via Console (Windows)**

```bash
cmd
cd C:\Users\mario\Downloads\h2-2019-03-13\h2\bin
#note: ignore the *.db ending. H2 will add it automatically. If you do it yourself it will fail!
java -jar h2-1.4.199.jar -url "jdbc:h2:file:~/Downloads/docs;CACHE_SIZE=65536;LOCK_TIMEOUT=10000;IFEXISTS=TRUE;" -driver "org.h2.Driver" -user "sa" -password ""
```

**Connect via Console (Linux)**

<p class="callout info">Good tips: [https://o7planning.org/en/11895/installing-h2-database-and-using-h2-console](https://o7planning.org/en/11895/installing-h2-database-and-using-h2-console)</p>

```bash
cd opt/
wget https://h2database.com/h2-2019-03-13.zip
unzip h2-2019-03-13.zip
rm h2-2019-03-13.zip
cd /opt/h2/bin/
 
#open Database with h2 driver - enable X11 forward to recieve graphical user interface (GUI)
export DISPLAY=localhost:10.0 && java -jar h2-1.4.199.jar -url "jdbc:h2:file:/var/docs.bak/db/docs;CACHE_SIZE=65536;LOCK_TIMEOUT=10000;IFEXISTS=TRUE;" -driver "org.h2.Driver" -user "sa" -password ""
 
#note that version h2-2019-10-14 failed on the Teedy H2 database. So i used older version h2-2019-03-13
```

## Variant 2: DBVisualizer

DBVisualizer comes with bundled support for H2 including the driver → [https://www.dbvis.com/features/h2-database-features](https://www.dbvis.com/features/h2-database-features/)

Some example configuration looks like this:

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/xnVdgV4B0y2O2iq3-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/xnVdgV4B0y2O2iq3-grafik.png)

<p class="callout info">Ignore the `*.db` ending. H2 will add it automatically. If you do it yourself it will fail! So the database "docs" contains of two files and you only need to enter the principal name.</p>

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/gEpfzq0vmHkqds07-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/gEpfzq0vmHkqds07-grafik.png)

# Apache Reverse Proxy and Firewall

## Install apache2

```
sudo apt install apache2
```

### Activate modules

```
sudo a2enmod headers rewrite proxy proxy_html proxy_http ssl vhost_alias
```

## Apache Reverse Proxy Configuration

```bash
sudo vim /etc/apache2/sites-available/dms.yourdomain.de_httpd.conf
```

```ini
<VirtualHost YOURPUBLICIP:7080 127.0.0.1:7080>
       ServerName dms.yourdomain.de
       RewriteEngine On
       RewriteCond %{HTTPS} off
       RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
<VirtualHost YOURPUBLICIP:7081 127.0.0.1:7081>
        ServerName dms.YOURDOMAIN.de
        ServerAdmin info@YOURDOMAIN.de
 
        ErrorLog ${APACHE_LOG_DIR}/error-sismics.log
        CustomLog ${APACHE_LOG_DIR}/access-sismics.log combined
 
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/YOURDOMAIN.de/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/YOURDOMAIN.de/privkey.pem
        SSLCertificateChainFile /etc/letsencrypt/live/YOURDOMAIN.de/chain.pem
 
        SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
        SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
        SSLHonorCipherOrder On
        Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        #Header always set X-Frame-Options DENY
        Header always set X-Content-Type-Options nosniff
        #Header set Content-Security-Policy "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';"
        Header unset X-Powered-By
        Header set Referrer-Policy "origin-when-cross-origin"
        Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure"
        Header set X-XSS-Protection "1; mode=block"
        Header always set Content-Security-Policy "upgrade-insecure-requests;" #upgrade unsafe gravatar icons to load from https instead of http
 
        # Requires Apache >= 2.4
        SSLCompression off
        #SSLUseStapling on
        #SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
        # Requires Apache >= 2.4.11
        SSLSessionTickets Off
 
        ProxyRequests Off
 
        # Auth changes in 2.4 - see http://httpd.apache.org/docs/2.4/upgrading.html#run-time
        <Proxy *>
                Require all granted
        </Proxy>
 
        ProxyPass / http://localhost:8080/dms/
        ProxyPassReverse / http://localhost:8080/dms/
        <Location />
             SSLRenegBufferSize 100000000
             Require all granted
       </Location>
 
       <Location "/api/app">
           AllowOverride None
           Order deny,allow
           Deny from All
       </Location>
  
       <Location ~ "/api/app/.*">
           AllowOverride None
           Allow from All
       </Location>
  
       RewriteEngine on
       RewriteCond %{REQUEST_FILENAME} !-d
       RewriteRule ^(.*)/$ /$1 [R=301,L]
</VirtualHost>
```

### Firewall Blocking Rule

Block direct access to Jetty9 on Port 8080 (ingoing and outgoing TCP traffic) to allow access only on SSL secured domain. Use iptables or similar.

# App for Android

Sismics Docs is an easy to use, modern and nice DMS (document management system). Sismics Docs can be used on Android. But at the moment there is no downloadable App on Google PlayStore. In the offical git repository you can find the required sources to compile it for yourself. This short tutorial will show you how you can do this. Please feel free to leave comments about additions and corrections. As usual different configurations force different bugs at different users.

## Variant 1: Build with Android Studio

1. Install Android Studio
2. Run Android Studio
3. Open the project "docs-android" in Android Studio
4. Connect Smartphone by USB in "data transfer" mode
5. Run the build process "Run" → "Run 'app'" - this will install Teedy on Android
6. Finished

## Variant 2: Build without Android Studio

### Pull the repository

Go to [https://github.com/sismics/docs](https://github.com/sismics/docs) and clone the repository to your local client.

### Install JDK 11

<div class="rwui_item_content" id="bkmrk-https%3A%2F%2Fwww.oracle.c">- [<span>https://www.oracle.com/technetwork/java/javase/downloads/index.html</span>](https://www.oracle.com/technetwork/java/javase/downloads/index.html)

</div>### Install Android SDK Tools

The SDK Tools cannot be found directly on android homepage. Instead search them via web:

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/nMJRuoWrvq6ROMQK-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/nMJRuoWrvq6ROMQK-grafik.png)

[https://filehippo.com/de/download\_android\_sdk](https://filehippo.com/de/download_android_sdk/)

Download the setup and install it to the given standard directory. After that just update/upgrade the components. The installer will ask you if you like to do this.

### Configure Windows environment variables

<div class="rwui_item_content" id="bkmrk-java_home-%E2%86%92-c%3A%5Cprogr">- `JAVA_HOME` → `C:\Program Files\Java\jdk-11`
- `ANDROID_HOME` → `%userprofile%\AppData\Local\Android\android-sdk`
- `PATH` → `%userprofile%\AppData\Local\Android\android-sdk\tools` (add to existing)
- `PATH` → `%userprofile%\AppData\Local\Android\android-sdk\platform-tools` (add to existing)

</div>### Create local.properties file in the repository

Locate the directory &lt;path to your repo&gt;\\docs-android\\ and create a new file named local.properties. Open that file and insert:

```
C:\Users\mario\Git\teedy\docs-android
sdk.dir=C:\\Users\\mario\\AppData\\Local\\Android\\android-sdk
```

### Start the app building process

Open a new cmd shell window and insert the following commands

```
cmd
cd C:\Users\mario\Git\teedy\docs-android
#gradlew tasks
gradlew build
#gradlew --stacktrace --debug
```

### Copy the built .apk file to your phone

The compiled .apk file was generated in `<path to your repo>\docs-android\app\build\outputs\apk\release`. Just put it somewhere on your phone to access it from Android user interface.

### Sign the .apk file

Because the compiled file is not signed you cannot install it on your phone until you do a signing process. I found an application called ["apk-signer" in Google PlayStore](https://play.google.com/store/apps/details?id=com.haibison.apksigner&hl=de) which did the job for me.

After downloading and installing apk-signer, start apk-signer and select the Teedy apk file within the application. By selecting it will be signed. After this start the freshly signed apk file within apk-signer and you are allowed to install it.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/TLvFdgdtJmZcCud4-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/TLvFdgdtJmZcCud4-grafik.png)

### Enter your configuration and start the app

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/w8ZMHDahbqFOJIXM-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/w8ZMHDahbqFOJIXM-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/dS11t8J9BZ7ZleHe-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/dS11t8J9BZ7ZleHe-grafik.png)

# Automatic Mail Importing (Inbox Scanning)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/jEZLJ5wYkcqqW0NV-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/jEZLJ5wYkcqqW0NV-grafik.png)

This feature can be configured by environment variables! → [Environment Configuration](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/environment-configuration "Environment Configuration")

# Backup and restore strategies

## Simple: Make backup of server app and data dir (H2 database)

```bash
#!/bin/bash
BUP_PATH="/backup/teedy/"
mkdir -p $BUP_PATH
rsync -lrptR /var/docs $BUP_PATH
rsync -lrptR /opt/jetty-home-11.0.15/jetty-base/webapps $BUP_PATH
cd $BUP_PATH
FILENAME=$(date +%Y-%m-%d)-teedy.tar.gz
tar -zcvf $FILENAME $BUP_PATH/var/
chown TARGETUSER:TARGETUSER $FILENAME
```

## Simple: Restore backup data dir (H2 database)

```bash
systemctl stop jetty11.service
cd /var; rm -rf docs
mkdir -p /var/docs
cp /backup/teedy/2018-12-30-teedy.tar.gz /var/docs
cd /var/docs
tar -xvzf 2018-12-30-teedy.tar.gz
chmod 777 /var/docs
systemctl start jetty11.service
```

## Simple: Backup PostgreSQL (not if you are using H2)

```bash
sudo -iu postgres bash -c "pg_dump YOURDATABASE > YOURDATABASE.sql" && mv /var/lib/postgresql/YOURDATABASE.sql "$BUP_POSTGRES"/YOURDATABASE.sql
```

## Simple: Restore PostgreSQL dump

The database you want to restore must exis in your psql instancet! If you move your DB from one server to another you need to recreate an empty DB first. See [Teedy with PostgreSQL](https://old.stadtfabrikanten.org/display/TEED/Teedy+1.12+with+PostgreSQL).

```bash
#move the database dump file to a location where postgres user can read it, for example /var/lib/postgres/
chown postgres /var/lib/postgresql/teedy_db.sql
su - postgres
 
#drop old database and create a new one
psql
drop database teedy_inventory_db;
CREATE DATABASE teedy_db WITH ENCODING 'UNICODE' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;
GRANT ALL PRIVILEGES ON DATABASE teedy_inventory_db TO teedy_inventory;
 
#now import the backup db dump
psql teedy_inventory_db < teedy_inventory_db.sql
```

## API Use: Bash script for API export of all documents and tags with cURL

```bash
#!/bin/bash
BASE_URL="https://dms.yourdomain.de"
AUTH_TOKEN=$(curl -i -X POST -d username="THEUSERNAME" -d password="THEPASSWORD" "$BASE_URL/api/user/login" -k|grep "auth_token"|cut -c24-59)
BACKUP_DIR="/backup/teedy"
TARGET_JSON_FILE=$BACKUP_DIR"/documentlist.json"
TARGET_JSON_FILE_PARSED=$BACKUP_DIR"/documentlist.txt"
TARGET_JSON_FILE_SORTED=$BACKUP_DIR"/documentlist.sorted.txt"
DIR_ZIP=$BACKUP_DIR/"ZIP"
DIR_PDF=$BACKUP_DIR/"PDF"
 
#create backup directory
mkdir -p "$BACKUP_DIR"
mkdir -p "$DIR_ZIP"
mkdir -p "$DIR_PDF"
 
#get the documents list
#curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document/list?limit=0" -k | jq . > "$TARGET_JSON_FILE"
curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document/list" -d "limit=999999" -k | jq . > "$TARGET_JSON_FILE"
 
#get the tags list
curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/tag/list?limit=0" -k | jq . > "$BACKUP_DIR"/taglist.json
#read the complete list of documents
jq -c '.|{documents}|.[]|.[]|{id}+{title}+{create_date}+{tags}' "$TARGET_JSON_FILE" > "$TARGET_JSON_FILE_PARSED"
 
#make sorted list which shows number of duplicates
jq -c '.|{documents}|.[]|.[]|{title}' "$TARGET_JSON_FILE" | sort | uniq -c | sort > "$TARGET_JSON_FILE_SORTED"
 
COUNT=0
TOTAL=$(jq -r '.total' $TARGET_JSON_FILE)
jq -c '.|{documents}|.[]|.[]|{id}+{title}+{create_date}' "$TARGET_JSON_FILE" | while read -r i; do
    COUNT=$((COUNT + 1))
 
    #parse the line and get parameters from the line
    DOC_ID=$(jq -c '.|{id}|.id' <<< $(printf '%s\n' "$i"))
    DOC_NAME=$(jq -c '.|{title}|.title' <<< $(printf '%s\n' "$i"))
    DOC_DATE=$(jq -c '.|{create_date}|.create_date' <<< $(printf '%s\n' "$i"))
 
    #EXPORT_FILE_NAME=$(date -d@${DOC_DATE:0:-3} +%Y-%m-%d)_${DOC_ID:1:-1}_${DOC_NAME:1:-1}.zip
    EXPORT_FILE_NAME=$(date -d@${DOC_DATE:0:-3} +%Y-%m-%d)_${DOC_ID:1:-1}
    echo $COUNT OF $TOTAL = $EXPORT_FILE_NAME ____ ${DOC_NAME:1:-1}
 
    #Export ZIP
    curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL"/api/file/zip?id="${DOC_ID:1:-1}" -k -o "$DIR_ZIP"/"$EXPORT_FILE_NAME".zip
 
    #Export PDF
    curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL"/api/document/"${DOC_ID:1:-1}"/pdf?margin=10\&metadata=false\&comments=true\&fitimagetopage=true -k -o "$DIR_PDF"/"$EXPORT_FILE_NAME".pdf
done
 
#logout if finished
curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/user/logout" -k
```

## API Use: Flat Hirarchy Export + File List Overview

```bash
#!/bin/bash
 
BASE_URL="https://dms.yourdomain.de"
AUTH_TOKEN=$(curl -i -X POST -d username="THEUSER" -d password="THEPASSWORD" "$BASE_URL/api/user/login" -k|grep "auth_token"|cut -c24-59)
BACKUP_DIR="/backup/teedy"
TARGET_DOCLIST_JSON=$BACKUP_DIR"/documentlist_forfiles.json"
TARGET_FILELIST_JSON=$BACKUP_DIR"/filelist.json"
 
mkdir -p "$BACKUP_DIR"
rm $TARGET_DOCLIST_JSON
rm $TARGET_FILELIST_JSON
 
echo "Retrieving document list"
#curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document/list?limit=0" -k | jq . > "$TARGET_DOCLIST_JSON"
curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/document/list" -d "limit=999999" -k | jq . > "$TARGET_JSON_FILE"
 
echo "Retrieving file list based on document list"
COUNT=0
jq -c '.|{documents}|.[]|.[]|{id}+{title}+{create_date}' "$TARGET_DOCLIST_JSON" | while read -r i; do
    COUNT=$((COUNT + 1))
    DOC_ID=$(jq -c '.|{id}|.id' <<< $(printf '%s\n' $i))
    DOC_ID=${DOC_ID:1:-1}
    curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/list?id=$DOC_ID" >> "$TARGET_FILELIST_JSON"
    #echo -e "\n" >> "TARGET_JSON_FILE"
    echo Getting $COUNT : $DOC_ID
done
 
echo "Dumping files into flat hirarchy"
mkdir "$BACKUP_DIR"/flat_hirarchy/
COUNT=0
jq -c '.[]|.[]|{create_date}+{name}+{id}+{document_id}+{mimetype}' "$TARGET_FILELIST_JSON" | while read -r i; do
    COUNT=$((COUNT + 1))
    DOC_ID=$(jq -c '.|{document_id}|.document_id' <<< $(printf '%s\n' "$i"))
    DOC_ID=${DOC_ID:1:-1}
    FILE_NAME=$(jq -c '.|{name}|.name' <<< $(printf '%s\n' "$i"))
    FILE_NAME=${FILE_NAME:1:-1}
    FILE_DATE=$(jq -c '.|{create_date}|.create_date' <<< $(printf '%s\n' "$i"))
    FILE_ID=$(jq -c '.|{id}|.id' <<< $(printf '%s\n' "$i"))
    FILE_ID=${FILE_ID:1:-1}
    #MIMETYPE=$(jq -c '.|{mimetype}|.mimetype' <<< $(printf '%s\n' "$i")|sed 's#/#_#g')
    #MIMETYPE=${MIMETYPE:1:-1}
    #FILE_TYPE=$(echo "$FILE_NAME"|awk -F. '{print $(NF)}')
    #FILE_TYPE=${FILE_TYPE:0:-1}
    #EXPORT_FILE_NAME=$(date -d@${FILE_DATE:0:-3} +%Y-%m-%d)_"$FILE_ID"."$FILE_ID"."$MIMETYPE"."$FILETYPE"
    EXPORT_FILE_NAME=$(date -d@${FILE_DATE:0:-3} +%Y-%m-%d)_"$FILE_ID"."$FILE_ID"."$FILE_NAME"
    echo Getting $COUNT : $EXPORT_FILE_NAME
    curl --silent -X GET -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/file/$FILE_ID/data" -o "$BACKUP_DIR"/flat_hirarchy/"$EXPORT_FILE_NAME"
done
  
#logout if finished
curl --silent -X POST -H "Cookie: auth_token=$AUTH_TOKEN" "$BASE_URL/api/user/logout" -k
```

# Basic installation with Jetty and H2 Database

**Install Required Software**

```bash
#install a lot of stuff
sudo apt update
sudo apt install tesseract-ocr tesseract-ocr-deu tesseract-ocr-eng libtesseract-dev ffmpeg mediainfo mediainfo-gui openjdk-11-jdk
 
#install Jetty Web Server
sudo apt install jetty11
 
#check versions
ffmpeg -version
tesseract -v
mediainfo --version
dpkg -l | grep jetty11
dpkg -l | grep jdk
```

You can also install jetty manually (not by `apt`) with ease and full control:

```bash
cd /opt
wget https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/11.0.15/jetty-home-11.0.15.tar.gz
tar -xvzf jetty-home-11.0.15.tar.gz
mkdir -p /opt/jetty-home-11.0.15/jetty-base/
cd /opt/jetty-home-11.0.15/jetty-base/
java -jar ../start.jar --add-modules=deploy,http
cp /opt/teedy/docs-web/target/docs-web-1.*.war /opt/jetty-home-11.0.15/jetty-base/webapps/dms.war #copy the compiled deployment war to target dir
chown jetty:adm /opt/jetty-home-11.0.15/jetty-base/webapps/dms.war
chown jetty:adm /opt/jetty-home-11.0.15/jetty-base/webapps/dms.xml
```

**Create dms.xml configuration files**

This allows to change the default docs home dir and other things. Have a look at [https://github.com/sismics/docs/blob/master/docs.xml](https://github.com/sismics/docs/blob/master/docs.xml)

```bash
vim /opt/jetty-home-11.0.15/jetty-base/webapps/dms.xml
```

```xml
<?xml version="1.0"?>

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/dms</Set>
  <Set name="war"><SystemProperty name="jetty.data" default="."/>/webapps/dms.war</Set>
  <Call class="java.lang.System" name="setProperty">
    <Arg>docs.home</Arg>
    <Arg>/var/docs</Arg>
  </Call>
</Configure>
```

Adjust the following lines according to your configured XML argument `docs.home` (if changed)

```bash
mkdir -p /var/docs/
chmod -T 770 /var/docs/
chown -R jetty:jetty /var/docs/
```

Take the pre-built `dms.war` file or [compile on your own](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/build-web-application-server-from-source "Build web application server from Source") and put it to /opt/jetty-home-11.0.15/jetty-base/webapps/dms.war

**Tuning**

Raise the heap space Xmx to prevent "java heap space error" - this often occures when OCR'ing a lot of files or uploading multiple files at once. This causes to restart jetty9 completely

```bash
sudo vim /lib/systemd/system/jetty11.service
```

```ini
Environment="JAVA_OPTS=-Xms1024m -Xmx3584m -Djava.awt.headless=true"
```

See also [Environment Configuration](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/environment-configuration "Environment Configuration") for reference

**JDK/JRE - Permissions Policy Adjustments (optional)**

<p class="callout danger">that might be dangerous / unsecure</p>

```ini
sudo vim /usr/lib/jvm/java-11-openjdk-amd64/lib/security/default.policy
```

Add to top of file:

```ini
grant {
    permission java.security.AllPermission "", "";
};
```

**Fix jetty read-only filesystem (since Jetty 9.4.15)**

Sympton → "Caused by: [java.io](http://java.io).FileNotFoundException: /var/docs/db/docs.trace.db (file system is readonly - but it is not!)"

The newest jetty package was changed to contain restricted settings in `/lib/systemd/system/jetty9.service`. You need to add another ReadWritePath for the `/var/docs` directory

```ini
sudo vim /lib/systemd/system/jetty11.service
```

```ini
ProtectSystem=strict
ReadWritePaths=/var/lib/jetty11
ReadWritePaths=/var/docs/
```

**Restart Jetty Service**

```ini
sudo systemctl restart jetty11.service
```

**Check the logs**

```ini
sudo less /var/log/jetty11/
sudo journalctl -f -u jetty11
```

**Access to Teedy Web Interface**


[https://YOURHOST:8080](https://YOURHOST:8080)

# Build web application server from Source

This short tutorial shows how to build Teedy. Finally you will get a ready-to-deploy dms.war java archive file.

**Pre-Requisites**

```bash
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt update
sudo apt install openjdk-11-jdk maven npm jetty11
 
sudo ln -s /usr/bin/nodejs /usr/bin/node
npm install -g grunt-cli
npm install -g npm@latest
```

**Building**

Additional steps are included in the following bash script.

- Adjust code for GDPR (data privacy → add some custom URLs to satisfy german TMG)
- adjusting hibernate pool size to allow more connections
- some CSS fix for bigger preview images

```bash
#!/bin/bash
BASE="/opt/teedy"
 
#altes Repo löschen
#rm -rf /opt/teedy/
 
#clone
git clone https://github.com/sismics/docs.git ${BASE}
cd ${BASE}/
 
#to update existing repo:
git stash
git pull
 
#GDPR adjustments
sed -i 's/<li>v{{ app.current_version }}<\/li>/<li><a href\="https:\/\/stadtfabrikanten.org\/impressum">Impressum<\/a><\/li><li><a href\="https:\/\/stadtfabrikanten.org\/datenschutz">Datenschutz<\/a><\/li><li>v{{ app.current_version }}<\/li>/g' ${BASE}/docs-web/src/main/webapp/src/share.html
sed -i 's/<li>v{{ app.current_version }}<\/li>/<li><a href\="https:\/\/stadtfabrikanten.org\/impressum">Impressum<\/a><\/li><li><a href\="https:\/\/stadtfabrikanten.org\/datenschutz">Datenschutz<\/a><\/li><li>v{{ app.current_version }}<\/li>/g' ${BASE}/docs-web/src/main/webapp/src/index.html
 
#CSS fix
sed -i 's/42px/100px/g' ${BASE}/docs-web/src/main/webapp/src/style/main.less
 
#Adjust hibernate.pool_size from 10 to 50
sed -i 's/\"hibernate.connection.pool_size\", \"10\"/\"hibernate.connection.pool_size\", \"50\"/g' ${BASE}/docs-core/src/main/java/com/sismics/util/jpa/EMF.java
 
#Building
cd ${BASE}/
mvn clean -DskipTests install
 
cd ${BASE}/docs-web
mvn -Pprod -DskipTests clean install
 
#Apply
JETTY_WEBAPPS_DIR="/opt/jetty-home-11.0.15/jetty-base/webapps"
service jetty11 stop
mv ${JETTY_WEBAPPS_DIR}/dms.war ${JETTY_WEBAPPS_DIR}/dms.war.bak
cp /opt/teedy/docs-web/target/docs-web-1.*-SNAPSHOT.war ${JETTY_WEBAPPS_DIR}/dms.war
chown jetty:adm ${JETTY_WEBAPPS_DIR}/dms.war
 
systemctl restart jetty11
journalctl -f -u jetty11.service
```

# Downloads

<div class="page view" id="bkmrk-teedy-consists-of%C2%A0ja"><article>Teedy consists of Java Server Application for Windows, MacOS or Linux Server and additionally:

<div class="wiki-content">- Android App
- Document Importer for Windows/Linux/MacOS

</div><p class="callout info">There's also a Grafana Dashboard for more deep analysis available. See [Grafana Monitoring / Statistics](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/grafana-monitoring-statistics "Grafana Monitoring / Statistics")</p>

<p class="callout info">You can make your own executable with source code. See [Build web application server from Source](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/build-web-application-server-from-source "Build web application server from Source")</p>

<div class="wiki-content" id="bkmrk-"></div></article></div>

# Environment Configuration

We use Fedora 38 Workstation, but the following steps should be similar on other systems.

We also use a timer to delay the startup of jetty11 at bootup, because it sometimes struggles to perform well at the first start.

```bash
vim /lib/systemd/system/jetty11.timer
```

```bash
[Unit]
Description=Timer for jetty11 Startup Delay
 
[Timer]
OnBootSec=1min
 
[Install]
WantedBy=timers.target
```

```bash
vim /lib/systemd/system/jetty11.service
```

```bash
[Unit]
Description=Jetty 11 Web Application Server
Documentation=https://www.eclipse.org/jetty/documentation/current/
After = syslog.target network.target
 
[Service]
# Configuration
Environment="JETTY_HOME=/opt/jetty-home-11.0.15"
Environment="JETTY_BASE=/opt/jetty-home-11.0.15/jetty-base"
Environment="JETTY_USER=jetty"
Environment="JETTY_HOST=127.0.0.1"
Environment="JETTY_ARGS=jetty.port=8080"
Environment="JETTY_STATE=/var/lib/jetty11/jetty.state"
 
Environment="JAVA_OPTS=-Xms1024m -Xmx3584m -Djava.awt.headless=true"
 
#Configure Jetty Service to use database connection instead of H2 local DB
Environment="DATABASE_URL=jdbc:postgresql://127.0.0.1:5432/teedy_db"
Environment="DATABASE_USER=teedy"
Environment="DATABASE_PASSWORD=password"
 
#set base url for password reset
Environment="DOCS_BASE_URL=https://your.domain.tld"
 
#Configure tesseract performance
Environment="OMP_THREAD_LIMIT=1"
 
# Lifecycle
Type=forking
ExecStart = /opt/jetty-home-11.0.15/bin/jetty.sh start
ExecStop = /opt/jetty-home-11.0.15/bin/jetty.sh stop
ExecReload = /opt/jetty-home-11.0.15/bin/jetty.sh restart
 
# Logging
SyslogIdentifier=jetty11
 
# Security
User=jetty
Group=jetty
PrivateTmp=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
WorkingDirectory=/usr/share/jetty11/
LogsDirectory=jetty11
LogsDirectoryMode=750
ProtectSystem=
ReadWritePaths=/var/lib/jetty9/
ReadWritePaths=/mnt/data/sismics/
 
[Install]
WantedBy=multi-user.target
```

<p class="callout info">More environment vars can be found at [<span>https://github.com/sismics/docs/blob/dd36e08d7d6cd8248f12a9570694b4631be3b04d/README.md#available-environment-variables</span>](https://github.com/sismics/docs/blob/dd36e08d7d6cd8248f12a9570694b4631be3b04d/README.md#available-environment-variables)</p>

```bash
systemctl daemon-reload
systemctl enable jetty11.timer #we do not enable jetty11.service too, because that service is just fully controlled by our timer
systemctl start jetty11.service
systemctl status jetty11.service
```

# Information about Teedy file structure

<div class="table-wrap" id="bkmrk-">  
</div><div class="page view" id="bkmrk-directory-notes-%2Fvar"><article><div class="wiki-content" id="bkmrk-directory-notes-%2Fvar-1"><div class="table-wrap"><table role="grid" style="width: 100.007%;"><colgroup><col style="width: 11.646%;"></col><col style="width: 88.3742%;"></col></colgroup><thead><tr><td>Directory</td><td>Notes</td></tr></thead><tbody><tr role="row"><td>`/var/docs/theme`</td><td>Background image stored here</td></tr><tr role="row"><td>`/var/docs/storage`</td><td>place for alle the files and their automatically generated thumbnails (\*\_thumb) and web previews.   
Note that alle the files inside this dir are encrypted and got hashed file names.   
You can access the file content only inside a running Teedy instance which decrypts the files for you

</td></tr><tr role="row"><td>`/var/docs/log`</td><td>log files. Same output which you get from journalctl -u jetty9.service (if you use systemd)</td></tr><tr role="row"><td>`/var/docs/db`</td><td>H2 database files. Empty but existing if PostgreSQL is used</td></tr><tr role="row"><td>`/var/docs`</td><td>Root Directory</td></tr></tbody></table>

</div></div></article></div><div id="bkmrk--1"><div class="no-print" id="bkmrk--2"></div></div>

# Known Limitations

<div class="page view" id="bkmrk-general-issues-can-b"><article><div class="wiki-content" id="bkmrk-general-issues-can-b-1">- General issues can be found in [https://github.com/sismics/docs/issues](https://github.com/sismics/docs/issues/345)
- If you delete a user, all his assigned documents get deleted without asking. The proper way to omit data loss, just deactivate the user and properly assign permissions to all tags, so other people can continue working with these documents.
- standard H2 database gets really slow when having thousands of documents or documents with thousands of files inside → [Migration from H2 to PostgreSQL](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/migration-from-h2-to-postgresql "Migration from H2 to PostgreSQL") helps! **Really**, PostgreSQL is the only useful way to be productive with Teedy!

</div></article></div>

# Migration from H2 to PostgreSQL

<p class="callout warning">Note that the migration will make you loosing all the OCR extracted text content from files in your documents. If you want to have those OCR strings back you will need to run a re-indexing for all of them.</p>

**Stop Teedy and make backup of recent H2 file structure**

```bash
service jetty9 stop
cd /var/
cp -R docs/ docs.bak/
chown -R jetty:jetty docs.bak/
chmod 777 docs.bak/
```

**Create some test instance of Teedy which is setup with PostgreSQL**

Please see [Teedy with PostgreSQL](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/teedy-with-postgresql "Teedy with PostgreSQL") to get some points on how to do this.

**Dumping scheme data from (filled) existing Teedy PostgreSQL instance to get a starting point for migration**

You will need to configure some temporary instance of Sismics to let it create the desired structure for you. This step is only required if you don't want to use the provided SQL statements in this documentation. Do that step if you want to do it for newer releases that will come up.

```bash
su - postgres #from root user
pg_dump --schema-only teedy_db > teedy_db.sql #that dump gets splitted in step1 and step2 later on
```

**Wipe away the psql test instance database and create a fresh/empty one again (for production use)**

```bash
psql
drop database teedy_db;
CREATE DATABASE teedy_db WITH ENCODING 'UNICODE' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;
GRANT ALL PRIVILEGES ON DATABASE teedy_db TO teedy;
\q
```

```sql
/*step1.sql*/
SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
SET default_tablespace = '';SET default_with_oids = false;CREATE TABLE public.t_acl (
    acl_id_c character varying(36) NOT NULL,
    acl_perm_c character varying(30) NOT NULL,
    acl_sourceid_c character varying(36) NOT NULL,
    acl_targetid_c character varying(36) NOT NULL,
    acl_deletedate_d timestamp without time zone,
    acl_type_c character varying(30) DEFAULT 'USER'::character varying NOT NULL
);
ALTER TABLE public.t_acl OWNER TO teedy;CREATE TABLE public.t_audit_log (
    log_id_c character varying(36) NOT NULL,
    log_identity_c character varying(36) NOT NULL,
    log_classentity_c character varying(50) NOT NULL,
    log_type_c character varying(50) NOT NULL,
    log_message_c character varying(1000),
    log_createdate_d timestamp without time zone,
    log_iduser_c character varying(36) DEFAULT 'admin'::character varying NOT NULL
);
ALTER TABLE public.t_audit_log OWNER TO teedy;CREATE TABLE public.t_authentication_token (
    aut_id_c character varying(36) NOT NULL,
    aut_iduser_c character varying(36) NOT NULL,
    aut_longlasted_b boolean NOT NULL,
    aut_creationdate_d timestamp without time zone NOT NULL,
    aut_lastconnectiondate_d timestamp without time zone,
    aut_ip_c character varying(45),
    aut_ua_c character varying(1000)
);
ALTER TABLE public.t_authentication_token OWNER TO teedy;CREATE TABLE public.t_base_function (
    baf_id_c character varying(20) NOT NULL
);
ALTER TABLE public.t_base_function OWNER TO teedy;CREATE TABLE public.t_comment (
    com_id_c character varying(36) NOT NULL,
    com_iddoc_c character varying(36) NOT NULL,
    com_iduser_c character varying(36) NOT NULL,
    com_content_c character varying(4000) NOT NULL,
    com_createdate_d timestamp without time zone,
    com_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_comment OWNER TO teedy;CREATE TABLE public.t_config (
    cfg_id_c character varying(50) NOT NULL,
    cfg_value_c character varying(250) NOT NULL
);
ALTER TABLE public.t_config OWNER TO teedy;CREATE TABLE public.t_contributor (
    ctr_id_c character varying(36) NOT NULL,
    ctr_iduser_c character varying(36) NOT NULL,
    ctr_iddoc_c character varying(36) NOT NULL
);
ALTER TABLE public.t_contributor OWNER TO teedy;CREATE TABLE public.t_document (
    doc_id_c character varying(36) NOT NULL,
    doc_iduser_c character varying(36) NOT NULL,
    doc_title_c character varying(100) NOT NULL,
    doc_description_c character varying(4000),
    doc_createdate_d timestamp without time zone,
    doc_deletedate_d timestamp without time zone,
    doc_language_c character varying(7) DEFAULT 'eng'::character varying NOT NULL,
    doc_subject_c character varying(500),
    doc_identifier_c character varying(500),
    doc_publisher_c character varying(500),
    doc_format_c character varying(500),
    doc_source_c character varying(500),
    doc_type_c character varying(500),
    doc_coverage_c character varying(500),
    doc_rights_c character varying(500),
    doc_updatedate_d timestamp without time zone NOT NULL,
    doc_idfile_c character varying(36)
);
ALTER TABLE public.t_document OWNER TO teedy;CREATE TABLE public.t_document_metadata (
    dme_id_c character varying(36) NOT NULL,
    dme_iddocument_c character varying(36) NOT NULL,
    dme_idmetadata_c character varying(36) NOT NULL,
    dme_value_c character varying(4000)
);
ALTER TABLE public.t_document_metadata OWNER TO teedy;CREATE TABLE public.t_document_tag (
    dot_id_c character varying(36) NOT NULL,
    dot_iddocument_c character varying(36) NOT NULL,
    dot_idtag_c character varying(36) NOT NULL,
    dot_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_document_tag OWNER TO teedy;CREATE TABLE public.t_file (
    fil_id_c character varying(36) NOT NULL,
    fil_iddoc_c character varying(36),
    fil_iduser_c character varying(36) NOT NULL,
    fil_mimetype_c character varying(100) NOT NULL,
    fil_createdate_d timestamp without time zone,
    fil_deletedate_d timestamp without time zone,
    fil_order_n integer,
    fil_content_c text,
    fil_name_c character varying(200),
    fil_version_n integer DEFAULT 0 NOT NULL,
    fil_latestversion_b boolean DEFAULT true NOT NULL,
    fil_idversion_c character varying(36)
);
ALTER TABLE public.t_file OWNER TO teedy;CREATE TABLE public.t_group (
    grp_id_c character varying(36) NOT NULL,
    grp_idparent_c character varying(36),
    grp_name_c character varying(50) NOT NULL,
    grp_idrole_c character varying(36),
    grp_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_group OWNER TO teedy;CREATE TABLE public.t_metadata (
    met_id_c character varying(36) NOT NULL,
    met_name_c character varying(50) NOT NULL,
    met_type_c character varying(20) NOT NULL,
    met_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_metadata OWNER TO teedy;CREATE TABLE public.t_password_recovery (
    pwr_id_c character varying(36) NOT NULL,
    pwr_username_c character varying(50) NOT NULL,
    pwr_createdate_d timestamp without time zone,
    pwr_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_password_recovery OWNER TO teedy;CREATE TABLE public.t_relation (
    rel_id_c character varying(36) NOT NULL,
    rel_iddocfrom_c character varying(36) NOT NULL,
    rel_iddocto_c character varying(36) NOT NULL,
    rel_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_relation OWNER TO teedy;CREATE TABLE public.t_role (
    rol_id_c character varying(36) NOT NULL,
    rol_name_c character varying(36) NOT NULL,
    rol_createdate_d timestamp without time zone NOT NULL,
    rol_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_role OWNER TO teedy;CREATE TABLE public.t_role_base_function (
    rbf_id_c character varying(36) NOT NULL,
    rbf_idrole_c character varying(36) NOT NULL,
    rbf_idbasefunction_c character varying(20) NOT NULL,
    rbf_createdate_d timestamp without time zone NOT NULL,
    rbf_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_role_base_function OWNER TO teedy;CREATE TABLE public.t_route (
    rte_id_c character varying(36) NOT NULL,
    rte_iddocument_c character varying(36) NOT NULL,
    rte_name_c character varying(50) NOT NULL,
    rte_createdate_d timestamp without time zone NOT NULL,
    rte_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_route OWNER TO teedy;CREATE TABLE public.t_route_model (
    rtm_id_c character varying(36) NOT NULL,
    rtm_name_c character varying(50) NOT NULL,
    rtm_steps_c character varying(5000) NOT NULL,
    rtm_createdate_d timestamp without time zone NOT NULL,
    rtm_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_route_model OWNER TO teedy;CREATE TABLE public.t_route_step (
    rtp_id_c character varying(36) NOT NULL,
    rtp_idroute_c character varying(36) NOT NULL,
    rtp_name_c character varying(200) NOT NULL,
    rtp_type_c character varying(50) NOT NULL,
    rtp_transition_c character varying(50),
    rtp_comment_c character varying(500),
    rtp_idtarget_c character varying(36) NOT NULL,
    rtp_idvalidatoruser_c character varying(36),
    rtp_order_n integer NOT NULL,
    rtp_createdate_d timestamp without time zone NOT NULL,
    rtp_enddate_d timestamp without time zone,
    rtp_deletedate_d timestamp without time zone,
    rtp_transitions_c character varying(2000)
);
ALTER TABLE public.t_route_step OWNER TO teedy;CREATE TABLE public.t_share (
    sha_id_c character varying(36) NOT NULL,
    sha_name_c character varying(36),
    sha_createdate_d timestamp without time zone,
    sha_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_share OWNER TO teedy;CREATE TABLE public.t_tag (
    tag_id_c character varying(36) NOT NULL,
    tag_iduser_c character varying(36) NOT NULL,
    tag_name_c character varying(36) NOT NULL,
    tag_createdate_d timestamp without time zone,
    tag_deletedate_d timestamp without time zone,
    tag_color_c character varying(7) DEFAULT '#3a87ad'::character varying NOT NULL,
    tag_idparent_c character varying(36)
);
ALTER TABLE public.t_tag OWNER TO teedy;CREATE TABLE public.t_user (
    use_id_c character varying(36) NOT NULL,
    use_idrole_c character varying(36) NOT NULL,
    use_username_c character varying(50) NOT NULL,
    use_password_c character varying(60) NOT NULL,
    use_email_c character varying(100) NOT NULL,
    use_createdate_d timestamp without time zone NOT NULL,
    use_deletedate_d timestamp without time zone,
    use_privatekey_c character varying(100) DEFAULT ''::character varying NOT NULL,
    use_storagequota_n bigint DEFAULT '10000000000'::bigint NOT NULL,
    use_storagecurrent_n bigint DEFAULT 0 NOT NULL,
    use_totpkey_c character varying(100),
    use_disabledate_d timestamp without time zone,
    use_onboarding_b boolean DEFAULT true NOT NULL
);
ALTER TABLE public.t_user OWNER TO teedy;CREATE TABLE public.t_user_group (
    ugp_id_c character varying(36) NOT NULL,
    ugp_iduser_c character varying(36) NOT NULL,
    ugp_idgroup_c character varying(36) NOT NULL,
    ugp_deletedate_d timestamp without time zone
);
ALTER TABLE public.t_user_group OWNER TO teedy;CREATE TABLE public.t_vocabulary (
    voc_id_c character varying(36) NOT NULL,
    voc_name_c character varying(50) NOT NULL,
    voc_value_c character varying(500) NOT NULL,
    voc_order_n integer NOT NULL
);
ALTER TABLE public.t_vocabulary OWNER TO teedy;CREATE TABLE public.t_webhook (
    whk_id_c character varying(36) NOT NULL,
    whk_event_c character varying(50) NOT NULL,
    whk_url_c character varying(1024) NOT NULL,
    whk_createdate_d timestamp without time zone NOT NULL,
    whk_deletedate_d timestamp without time zone
);
```

```bash
psql -d teedy_db -U teedy -f step1.sql -L teedy_db_dump_step1.log
```

**Export data from H2 to CSV**

Access the H2 database and run the following call statements. See this tutorial on how to open your existing H2 database → [Access / view H2 Database](https://old.stadtfabrikanten.org/pages/createpage.action?spaceKey=TEED&title=Teedy+1.12+Access+%2F+view+H2+Database&linkCreation=true&fromPageId=57770584)

```bash
/*get an overview of all tables*/
SHOW TABLES;
```

The following call statements were built up from the "`SHOW TABLES;`" output above.

```bash
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_ACL.csv', 'SELECT * FROM T_ACL', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_AUDIT_LOG.csv', 'SELECT * FROM T_AUDIT_LOG', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_AUTHENTICATION_TOKEN.csv', 'SELECT * FROM T_AUTHENTICATION_TOKEN', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_BASE_FUNCTION.csv', 'SELECT * FROM T_BASE_FUNCTION', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_COMMENT.csv', 'SELECT * FROM T_COMMENT', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_CONFIG.csv', 'SELECT * FROM T_CONFIG', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_CONTRIBUTOR.csv', 'SELECT * FROM T_CONTRIBUTOR', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_DOCUMENT.csv', 'SELECT * FROM T_DOCUMENT', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_DOCUMENT_METADATA.csv', 'SELECT * FROM T_DOCUMENT_METADATA', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_DOCUMENT_TAG.csv', 'SELECT * FROM T_DOCUMENT_TAG', 'charset=utf8');
/*the column "fil_content_c" is ignored because it contains a lot of weird plain text which is almost impossible to export to CSV in a clean fashion*/
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_FILE.csv', 'SELECT FIL_ID_C, FIL_IDDOC_C, FIL_IDUSER_C, FIL_MIMETYPE_C, FIL_CREATEDATE_D, FIL_DELETEDATE_D, FIL_ORDER_N,NULL AS FIL_CONTENT_C,FIL_NAME_C, FIL_VERSION_N, FIL_LATESTVERSION_B, FIL_IDVERSION_C FROM T_FILE', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_GROUP.csv', 'SELECT * FROM T_GROUP', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_METADATA.csv', 'SELECT * FROM T_METADATA', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_PASSWORD_RECOVERY.csv', 'SELECT * FROM T_PASSWORD_RECOVERY', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_RELATION.csv', 'SELECT * FROM T_RELATION', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_ROLE.csv', 'SELECT * FROM T_ROLE', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_ROLE_BASE_FUNCTION.csv', 'SELECT * FROM T_ROLE_BASE_FUNCTION', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_ROUTE.csv', 'SELECT * FROM T_ROUTE', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_ROUTE_MODEL.csv', 'SELECT * FROM T_ROUTE_MODEL', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_ROUTE_STEP.csv', 'SELECT * FROM T_ROUTE_STEP', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_SHARE.csv', 'SELECT * FROM T_SHARE', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_TAG.csv', 'SELECT * FROM T_TAG', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_USER.csv', 'SELECT * FROM T_USER', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_USER_GROUP.csv', 'SELECT * FROM T_USER_GROUP', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_VOCABULARY.csv', 'SELECT * FROM T_VOCABULARY', 'charset=utf8');
call CSVWRITE ('C:/users/mario/Downloads/h2data/T_WEBHOOK.csv', 'SELECT * FROM T_WEBHOOK', 'charset=utf8');
```

**Upload csv files to server (by SSH/SFTP)**

```bash
mkdir ~/h2data
#put csv files here
```

**Move files to postgres directory (to grant access)**

```bash
#as root
mv ~/h2data /var/lib/postgresql/
cd /var/lib/postgresql/
chown -R postgres:postgres h2data/
```

**SQL Import Copying**

The folloing copy statements were built up bases on the "call CSVWRITE" statements in the H2 export.

```bash
psql
\c teedy_db;
#You are now connected to database "teedy_db" as user "postgres".
COPY T_ACL FROM '/var/lib/postgresql/h2data/T_ACL.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_CONFIG FROM '/var/lib/postgresql/h2data/T_CONFIG.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_GROUP FROM '/var/lib/postgresql/h2data/T_GROUP.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_ROLE FROM '/var/lib/postgresql/h2data/T_ROLE.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_USER FROM '/var/lib/postgresql/h2data/T_USER.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_CONTRIBUTOR FROM '/var/lib/postgresql/h2data/T_CONTRIBUTOR.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_METADATA FROM '/var/lib/postgresql/h2data/T_METADATA.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_WEBHOOK FROM '/var/lib/postgresql/h2data/T_WEBHOOK.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_VOCABULARY FROM '/var/lib/postgresql/h2data/T_VOCABULARY.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_BASE_FUNCTION FROM '/var/lib/postgresql/h2data/T_BASE_FUNCTION.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_AUDIT_LOG FROM '/var/lib/postgresql/h2data/T_AUDIT_LOG.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_RELATION FROM '/var/lib/postgresql/h2data/T_RELATION.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_USER_GROUP FROM '/var/lib/postgresql/h2data/T_USER_GROUP.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_ROLE_BASE_FUNCTION FROM '/var/lib/postgresql/h2data/T_ROLE_BASE_FUNCTION.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_ROUTE_MODEL FROM '/var/lib/postgresql/h2data/T_ROUTE_MODEL.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_AUTHENTICATION_TOKEN FROM '/var/lib/postgresql/h2data/T_AUTHENTICATION_TOKEN.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_TAG FROM '/var/lib/postgresql/h2data/T_TAG.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_PASSWORD_RECOVERY FROM '/var/lib/postgresql/h2data/T_PASSWORD_RECOVERY.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_SHARE FROM '/var/lib/postgresql/h2data/T_SHARE.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_COMMENT FROM '/var/lib/postgresql/h2data/T_COMMENT.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_DOCUMENT FROM '/var/lib/postgresql/h2data/T_DOCUMENT.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_DOCUMENT_METADATA FROM '/var/lib/postgresql/h2data/T_DOCUMENT_METADATA.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_DOCUMENT_TAG FROM '/var/lib/postgresql/h2data/T_DOCUMENT_TAG.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_FILE FROM '/var/lib/postgresql/h2data/T_FILE.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_ROUTE FROM '/var/lib/postgresql/h2data/T_ROUTE.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
COPY T_ROUTE_STEP FROM '/var/lib/postgresql/h2data/T_ROUTE_STEP.csv' DELIMITER ',' CSV HEADER ENCODING 'utf-8';
\q
```

**SQL Step 2 - Adjust foreign keys and indexes**

```sql
/*step2.sql*/
ALTER TABLE public.t_webhook OWNER TO teedy;ALTER TABLE ONLY public.t_acl
    ADD CONSTRAINT t_acl_pkey PRIMARY KEY (acl_id_c);
ALTER TABLE ONLY public.t_audit_log
    ADD CONSTRAINT t_audit_log_pkey PRIMARY KEY (log_id_c);
ALTER TABLE ONLY public.t_authentication_token
    ADD CONSTRAINT t_authentication_token_pkey PRIMARY KEY (aut_id_c);
ALTER TABLE ONLY public.t_base_function
    ADD CONSTRAINT t_base_function_pkey PRIMARY KEY (baf_id_c);
ALTER TABLE ONLY public.t_comment
    ADD CONSTRAINT t_comment_pkey PRIMARY KEY (com_id_c);
ALTER TABLE ONLY public.t_config
    ADD CONSTRAINT t_config_pkey PRIMARY KEY (cfg_id_c);
ALTER TABLE ONLY public.t_contributor
    ADD CONSTRAINT t_contributor_pkey PRIMARY KEY (ctr_id_c);
ALTER TABLE ONLY public.t_document_metadata
    ADD CONSTRAINT t_document_metadata_pkey PRIMARY KEY (dme_id_c);
ALTER TABLE ONLY public.t_document
    ADD CONSTRAINT t_document_pkey PRIMARY KEY (doc_id_c);
ALTER TABLE ONLY public.t_document_tag
    ADD CONSTRAINT t_document_tag_pkey PRIMARY KEY (dot_id_c);
ALTER TABLE ONLY public.t_file
    ADD CONSTRAINT t_file_pkey PRIMARY KEY (fil_id_c);
ALTER TABLE ONLY public.t_group
    ADD CONSTRAINT t_group_pkey PRIMARY KEY (grp_id_c);
ALTER TABLE ONLY public.t_metadata
    ADD CONSTRAINT t_metadata_pkey PRIMARY KEY (met_id_c);
ALTER TABLE ONLY public.t_password_recovery
    ADD CONSTRAINT t_password_recovery_pkey PRIMARY KEY (pwr_id_c);
ALTER TABLE ONLY public.t_relation
    ADD CONSTRAINT t_relation_pkey PRIMARY KEY (rel_id_c);
ALTER TABLE ONLY public.t_role_base_function
    ADD CONSTRAINT t_role_base_function_pkey PRIMARY KEY (rbf_id_c);
ALTER TABLE ONLY public.t_role
    ADD CONSTRAINT t_role_pkey PRIMARY KEY (rol_id_c);
ALTER TABLE ONLY public.t_route_model
    ADD CONSTRAINT t_route_model_pkey PRIMARY KEY (rtm_id_c);
ALTER TABLE ONLY public.t_route
    ADD CONSTRAINT t_route_pkey PRIMARY KEY (rte_id_c);
ALTER TABLE ONLY public.t_route_step
    ADD CONSTRAINT t_route_step_pkey PRIMARY KEY (rtp_id_c);
ALTER TABLE ONLY public.t_share
    ADD CONSTRAINT t_share_pkey PRIMARY KEY (sha_id_c);
ALTER TABLE ONLY public.t_tag
    ADD CONSTRAINT t_tag_pkey PRIMARY KEY (tag_id_c);
ALTER TABLE ONLY public.t_user_group
    ADD CONSTRAINT t_user_group_pkey PRIMARY KEY (ugp_id_c);
ALTER TABLE ONLY public.t_user
    ADD CONSTRAINT t_user_pkey PRIMARY KEY (use_id_c);
ALTER TABLE ONLY public.t_vocabulary
    ADD CONSTRAINT t_vocabulary_pkey PRIMARY KEY (voc_id_c);
ALTER TABLE ONLY public.t_webhook
    ADD CONSTRAINT t_webhook_pkey PRIMARY KEY (whk_id_c);
CREATE INDEX idx_acl_sourceid_c ON public.t_acl USING btree (acl_sourceid_c);
CREATE INDEX idx_acl_targetid_c ON public.t_acl USING btree (acl_targetid_c);
CREATE INDEX idx_doc_createdate_d ON public.t_document USING btree (doc_createdate_d);
CREATE INDEX idx_doc_language_c ON public.t_document USING btree (doc_language_c);
CREATE INDEX idx_doc_title_c ON public.t_document USING btree (doc_title_c);
CREATE INDEX idx_dot_composite ON public.t_document_tag USING btree (dot_iddocument_c, dot_idtag_c, dot_deletedate_d);
CREATE INDEX idx_log_identity_c ON public.t_audit_log USING btree (log_identity_c);
ALTER TABLE ONLY public.t_authentication_token
    ADD CONSTRAINT fk_aut_iduser_c FOREIGN KEY (aut_iduser_c) REFERENCES public.t_user(use_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_comment
    ADD CONSTRAINT fk_com_iddoc_c FOREIGN KEY (com_iddoc_c) REFERENCES public.t_document(doc_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_comment
    ADD CONSTRAINT fk_com_iduser_c FOREIGN KEY (com_iduser_c) REFERENCES public.t_user(use_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_document_metadata
    ADD CONSTRAINT fk_dme_iddocument_c FOREIGN KEY (dme_iddocument_c) REFERENCES public.t_document(doc_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_document_metadata
    ADD CONSTRAINT fk_dme_idmetadata_c FOREIGN KEY (dme_idmetadata_c) REFERENCES public.t_metadata(met_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_document
    ADD CONSTRAINT fk_doc_idfile_c FOREIGN KEY (doc_idfile_c) REFERENCES public.t_file(fil_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_document
    ADD CONSTRAINT fk_doc_iduser_c FOREIGN KEY (doc_iduser_c) REFERENCES public.t_user(use_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_document_tag
    ADD CONSTRAINT fk_dot_iddocument_c FOREIGN KEY (dot_iddocument_c) REFERENCES public.t_document(doc_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_document_tag
    ADD CONSTRAINT fk_dot_idtag_c FOREIGN KEY (dot_idtag_c) REFERENCES public.t_tag(tag_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_file
    ADD CONSTRAINT fk_fil_iddoc_c FOREIGN KEY (fil_iddoc_c) REFERENCES public.t_document(doc_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_file
    ADD CONSTRAINT fk_fil_iduser_c FOREIGN KEY (fil_iduser_c) REFERENCES public.t_user(use_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_role_base_function
    ADD CONSTRAINT fk_rbf_idbasefunction_c FOREIGN KEY (rbf_idbasefunction_c) REFERENCES public.t_base_function(baf_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_role_base_function
    ADD CONSTRAINT fk_rbf_idrole_c FOREIGN KEY (rbf_idrole_c) REFERENCES public.t_role(rol_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_route
    ADD CONSTRAINT fk_rte_iddocument_c FOREIGN KEY (rte_iddocument_c) REFERENCES public.t_document(doc_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_route_step
    ADD CONSTRAINT fk_rtp_idroute_c FOREIGN KEY (rtp_idroute_c) REFERENCES public.t_route(rte_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_route_step
    ADD CONSTRAINT fk_rtp_idvalidatoruser_c FOREIGN KEY (rtp_idvalidatoruser_c) REFERENCES public.t_user(use_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_tag
    ADD CONSTRAINT fk_tag_iduser_c FOREIGN KEY (tag_iduser_c) REFERENCES public.t_user(use_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.t_user
    ADD CONSTRAINT fk_use_idrole_c FOREIGN KEY (use_idrole_c) REFERENCES public.t_role(rol_id_c) ON UPDATE RESTRICT ON DELETE RESTRICT;
REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM postgres;
GRANT ALL ON SCHEMA public TO postgres;
GRANT ALL ON SCHEMA public TO PUBLIC;
```

```bash
psql -d teedy_db -U teedy -f step2.sql -L teedy_db_dump_step2.log
```

**Configure Jetty Service to use database connection instead of H2 local DB**

See [Teedy with PostgreSQL](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/teedy-with-postgresql "Teedy with PostgreSQL") on how to do this

**Restart Jetty**

```bash
service jetty9 restart && journalctl -f -u jetty9.service
```

**Reindex all files with Tesseract OCR scanning libraries (the OCR data from is H2 is lost)**


There is no way to click some button for this. It has to be scripted. The provided API deals with it. Please see this page for re-processing files by API → [API Scripts / database queries](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-api-scripts-database-queries "Teedy API Scripts / database queries")

Before executing this please move (or remove) the \*\_thumb and \*\_web files away because they will be re-processed too. If you don't do this the following will occur: [Fix Preview Bug](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/fix-preview-bug "Fix Preview Bug")

# Multilingual Support

<div class="page-metadata-wrapper" id="bkmrk-"><div class="page-metadata"></div></div><div class="page view" id="bkmrk-teedy-is-available-i"><article>Teedy is available in different languages. Please help to make some commits to fix your own language. You can use the following tool to validate your modified json files which contain translatable strings: [https://jsonlint.com](https://jsonlint.com/)

Language file directories from git project:

- `teedy\docs-web\src\main\webapp\src\locale`
- `teedy\docs-core\src\main\resources`

</article></div>

# Security

## General Tips

- run Teedy in Intranet
- use SSL
- adjust apache2 to hide /api/app information (json output which contains information like used version, user count, etc.)

```bash
<Location "/api/app">
    AllowOverride None
    Order deny,allow
    Deny from All
</Location>
 
<Location ~ "/api/app/.*">
    AllowOverride None
    Allow from All
</Location>
 
#rewrite /api/app/ to /api/app and so on. Otherwise api/app will be blocked but api/app/ will not be blocked
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]
```

- enable 2FA for each user account
- Change admin user name to something else

```bash
UPDATE t_user SET use_username_c = 'yournewusername' WHERE use_username_c = 'admin';
```

On Linux you can use [https://wiki.ubuntuusers.de/QtQR](https://wiki.ubuntuusers.de/QtQR/) to import a QR code image from Desktop in case you just want to genate TOTP tokens with tools like [KeePassXC](https://keepassxc.org/) for instance. Just run qtqr and select "Decode from File" to open such QR code.

```bash
apt install qtqr
qtqr #run the app
```

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/ZdfE8f532LPjH6IE-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/ZdfE8f532LPjH6IE-grafik.png)

# Teedy with PostgreSQL

Teedy 1was tested sucessfully with PostgreSQL Version 16 and lower

## Install and configure PostgreSQL

<p class="callout info">Teedy requires at least PSQL 9.4 (PostgreSQL94Dialect)</p>

<p class="callout warning">PostgreSQL 10 and upwards are configured to deliver SSL by standard! You will need to configure it's SSL cert!</p>

```bash
sudo apt install -y postgresql postgresql-client libpq-dev postgresql-contrib
```

```bash
sudo vim /etc/postgresql/<VERSION>/main/pg_hba.conf
```

```ini
#add local to trust to omit password input. If you change to md5 you will need to enter passwords if you run scripts (e.g. bash)
# "local" is for Unix domain socket connections only
local       all       all       trust
#host       all       all       0.0.0.0/0 md5
hostssl     all       all       0.0.0.0/0 md5
```

```bash
sudo vim /etc/postgresql/<VERSION>/main/postgresql.conf
```

```ini
listen_addresses = '*'         # what IP address(es) to listen on;
ssl = on
ssl_cert_file = '/etc/ssl/yourdomain.de.pem'
ssl_key_file = '/etc/ssl/private/yourdomain.de.key'
```

```bash
#login as postgres user
su - postgres
psql
 
CREATE USER teedy WITH PASSWORD 'password';
CREATE DATABASE teedy_db WITH ENCODING 'UNICODE' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;
GRANT ALL PRIVILEGES ON DATABASE teedy_db TO teedy ;
 
#remove old database if required
#service postgresql restart #kick old connections
#REVOKE ALL PRIVILEGES ON DATABASE teedy_db FROM teedy;
#DROP DATABASE teedy_db;
#DROP USER teedy;
```

## PostgreSQL SSL

```bash
cd /etc/letsencrypt/live/yourdomain.de/
cp privkey.pem /etc/ssl/private/yourdomain.de.key
 
(cat privkey.pem; printf "\n\n"; cat cert.pem; printf "\n\n"; cat chain.pem; printf "\n\n") >> /etc/ssl/yourdomain.de.pem
cd /etc/ssl/
 
chgrp ssl-cert /etc/ssl/private/yourdomain.de.key
chmod 640 /etc/ssl/private/yourdomain.de.key
chgrp ssl-cert /etc/ssl/yourdomain.de.pem
chmod 640 /etc/ssl/yourdomain.de.pem
 
less /var/log/postgresql/postgresql-9.5-main.log #check for occuring errors belonging to SSL cert
```

## Configure dms.xml (optional)

```bash
vim /opt/jetty-home-11.0.15/jetty-base/webapps/dms.xml
```

```xml
<?xml version="1.0"?>

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/dms</Set>
  <Set name="war"><SystemProperty name="jetty.data" default="."/>/webapps/dms.war</Set>
  <Call class="java.lang.System" name="setProperty">
    <Arg>docs.home</Arg>
    <Arg>/var/docs</Arg>
  </Call>
</Configure>
```

## Configuration for usage of PostgreSQL instead H2

<p class="callout info"><span lang="en">Note: The database connection is set via a central environment variable configuration for the entire Jetty service and cannot be set for individual WebAppContext. </span></p>

<span lang="en">Have a a look at [Environment Configuration](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/environment-configuration "Environment Configuration") on how to swap to PostgreSQL</span>

## External connection test with Oracle SQL Developer

- <span lang="en">enable ports and configure firewall correctly</span>
- download SQL Developer driver for PostgreSQL → [https://jdbc.postgresql.org/download.html#current](https://jdbc.postgresql.org/download.html#current) (Treiber = postgresql-42.2.6)
- add the driver in system settings
- <span lang="en">User and database differ from the name. Therefore, the entry must be entered as follows:  
    </span>[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/PqkgOQfgGcRM1p2T-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/PqkgOQfgGcRM1p2T-grafik.png)
    
    
    - user: teedy
    - hostname: yourdomain.de:5432/teedy\_db?
    - port: stays empty
    - database: teedy\_db

# Vom Etikettenschild zum Objekt

# 1. Bereitstellungskonzept

## Objekt-Exportdatei generieren und per Web Server bereit stellen

Grundlage für die Etikettierung bildet der Objekt-Export aus der Teedy-Instanz. Der Export erfolgt durch Erzeugung einer CSV-Datei:

- die CSV-Datei wird alle 10 Minuten automatisch vom Server generiert
- die CSV-Datei wird über eine public URL vom Web Server bereitgestellt. Der Zugang ist mit Passwort und Nutzername gesichert
- Der Client kann die CSV-Datei mit bekannten Credentials und dem cURL Tool herunterladen

**Der Aufbau**

- Document ID
- Objekt Id
- Titel
- Nutzungsberechtigung
- Werkstattbereich

**Export-Script / Web Server Konfiguration**

```
htpasswd -c /etc/nginx/.htpassword USER
```

```
mkdir -p /var/www/vhosts/things.fablabchemnitz.de/flc/
```

```
vim /etc/nginx/sites-enabled/things.fablabchemnitz.de
 
location /flc/ {
    root /var/www/vhosts/things.fablabchemnitz.de;
    autoindex on;
    autoindex_format json;
    auth_basic "Administrator Login";
    auth_basic_user_file /etc/nginx/.htpassword;
}
 
service nginx restart
```

```
vim /opt/teedy-inventorylist.sh
```

```sql
#!/bin/bash
DB_USER="user"
DB_NAME="db"
#OUT=$(
psql -t -U$DB_USER $DB_NAME --no-align $'-F,' --command="
/*Warnung. Diese SQL-Abfrage muss mit der Tag-Struktur synchron gehalten werden, da Änderungen nicht automatisch erkannt werden*/
SELECT DISTINCT
CONCAT(
'\"',D.doc_id_c,'\",',
'\"',REPLACE(D.doc_title_c,'\"','\"\"'),'\",',
'\"',dme_value_c,'\",',
'\"',NB."Nutzungsberechtigung",'\",'
'\"',WB."Werkstattbereich",'\"'
)
FROM t_document AS D
JOIN t_document_tag ON D.doc_id_c = t_document_tag.dot_iddocument_c
JOIN t_tag ON t_tag.tag_id_c = t_document_tag.dot_idtag_c
FULL JOIN t_document_metadata AS M ON M.dme_iddocument_c = D.doc_id_c
FULL JOIN t_metadata AS N ON M.dme_idmetadata_c = N.met_id_c
FULL JOIN (
SELECT
t_document.doc_id_c,
t_tag.tag_name_c as "Nutzungsberechtigung"
FROM t_document
JOIN t_document_tag ON t_document.doc_id_c = t_document_tag.dot_iddocument_c
JOIN t_tag ON t_tag.tag_id_c = t_document_tag.dot_idtag_c
WHERE
t_tag.tag_name_c IN (
WITH RECURSIVE RES AS (SELECT
t_tag.tag_id_c,
t_tag.tag_idparent_c,
t_tag.tag_name_c
FROM t_tag WHERE tag_name_c = 'Nutzungsberechtigung'
UNION SELECT
e.tag_id_c,
e.tag_idparent_c,
e.tag_name_c
FROM t_tag AS e INNER JOIN RES s ON s.tag_id_c = e.tag_idparent_c
) SELECT tag_name_c FROM RES
) AND
--t_tag.tag_name_c in ('1/Allgemeine_Arbeitsschutzeinweisung', '2/Kurzeinweisung', '3/Maschinenführerschein') AND
t_document_tag.dot_deletedate_d IS NULL
) AS NB on NB.doc_id_c = D.doc_id_c
FULL JOIN (
SELECT
t_document.doc_id_c,
t_tag.tag_name_c as "Werkstattbereich"
FROM t_document
JOIN t_document_tag ON t_document.doc_id_c = t_document_tag.dot_iddocument_c
JOIN t_tag ON t_tag.tag_id_c = t_document_tag.dot_idtag_c
WHERE
t_tag.tag_name_c IN (
WITH RECURSIVE RES AS (SELECT
t_tag.tag_id_c,
t_tag.tag_idparent_c,
t_tag.tag_name_c
FROM t_tag WHERE tag_name_c = 'Werkstattbereich'
UNION SELECT
e.tag_id_c,
e.tag_idparent_c,
e.tag_name_c
FROM t_tag AS e INNER JOIN RES s ON s.tag_id_c = e.tag_idparent_c
) SELECT tag_name_c FROM RES
) AND
t_document_tag.dot_deletedate_d IS NULL
) AS WB on WB.doc_id_c = D.doc_id_c
WHERE
D.doc_deletedate_d IS NULL AND
N.met_deletedate_d IS NULL AND
(N.met_name_c = 'id' OR N.met_name_c IS NULL)
;
" > /var/www/vhosts/things.fablabchemnitz.de/flc/inventory.csv
```

```
vim /etc/cron.d/teedy-inventorylist
```

```
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/10 * * * * root /opt/teedy-inventorylist.sh > /dev/null
#
```

Abruf der CSV-Datei

```
curl --user "USER:PASSWORD" https://things.fablabchemnitz.de/flc/inventory.csv
```

Output-Beispiel

```
"00ae8d4e-8a05-4cba-b5e4-fb928168289d","Handlabelgerät P-Touch 1010 von Brother","61","","Büro"
```

# 2. Etikettenkonzept

## InkScape Erweiterung "Inventory Sticker" generiert Aufkleber-Vektordateien

Jedes Objekt im FabLab bekommt seinen eigenen Inventaraufkleber mit einer eigenen Nummer (id) als fortlaufenden Integerwert. Wir vermeiden die Verwendung von sog. [UUIDs](https://de.wikipedia.org/wiki/Universally_Unique_Identifier), da diese für unseren Zweck zu lang sind. Selbst "shortened UUIDs" sind noch zu lang, wie beispielsweise "mhvXdrZT4jP5T8vBxuvm75". Wir wollen nur ganz wenige Daten in einem jeweils einzigartigen Etiketten-Barcode speichern, damit dieser auf dimensional kleine Aufkleber passt und trotzdem gut scanbar bleibt.

Objekte, die es mehrfach gibt, erhalten auch jeweils eine einzigartige Nummer. Über ein client-seitiges Script können alle gewünschten Inventarobjekte aus einer Grafana .csv-Exportdatei eingelesen werden und daraus die entsprechenden Etiketten generiert werden (und anschließend gedruckt werden).

## Aufbau / Inhalt

Die Etiketten enthalten die folgenden Daten, welche aus einer komma-separierten .csv-Datei stammen **ohne Kopfzeile**. Der CSV-Aufbau unter [1. Bereitstellungskonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/1-bereitstellungskonzept "1. Bereitstellungskonzept") zu finden. Ein Beispiel:

```bash
"00ae8d4e-8a05-4cba-b5e4-fb928168289d","Handlabelgerät P-Touch 1010 von Brother","61","","Büro"
```

1. Kennzeichnung über Zugehörigkeit des Vereins (Konstante)
2. DataMatrix-Code (2D Barcode) 
    - mit Smartphones scanbar
    - enthält URL, die direkt zum Artikel im Inventarsystem hinleitet
    - 16x16 DataMatrix für geringsten Platzbedarf (hat im Vergleich zu BeeCode, AztecCode oder QR-Code bessere Datendichte) - eignet sich mit 16x16 für Zahlen in der Größenordnung bis 99999+
    - QR Codes auf weißem Hintergrund, da die meisten Apps invertierte QR Code Farbschemas leider nicht erkennen
3. Objektnummer (id)
4. Objektname (Titel sind in Teedy auf 100 Zeichen begrenzt)
5. Nutzungsberechtigung

Nicht auf dem Inventarkleber zu finden ist der aktuelle Ort, denn dieser kann sich regelmäßig ändern (je nach Werkstattordnung). Die Folge wäre ein ständiges Re-Labeling der Objekte, was für die Gesamtordnung hinderlich ist.

## Etikettengrößen (Grafikgrößen, Etikettenrollen, Reelle Größen nach Druck und Schnitt)

Die Inventaraufkleber (und folglich die Schilder, auf die sie kommen) sind mit Absicht sehr klein gehalten, denn sie sollen auf möglichst viele Objekte passen und keine wichtigen Stellen verdecken. Durch den platzsparenden 16x16 DataMatrix Barcode sind diese erwiesenermaßen aus ca. 25 - 50 cm ohne Probleme mit einer Standard Smarthone-Kamera einscannbar. Folgende Maße sollten beachtet werden:

- → Exportdatei (SVG/PNG): das von uns verwendete Aufkleberformat beträgt 696x308 Pixel. Das ergibt umgerechnet eine Etikettengröße von 62 x 27,4 mm
- → wir nutzen 62 mm breite Endlosetiketten von [DK22205](https://www.brother.de/verbrauchsmaterial/label-printers/labels/dk/dk22205) von Brother.
- → Die eigentlichen Etiketten, die man vom Träger abzieht, sind 62 mm breit, aber das Trägerformat selbst ist 66 mm breit
- → Da der Etikettendrucker jeweils ein paar Millimeter "Feed" (scrap size) generiert, sind die Etiketten höher, als in der Grafik angegeben, nämlich 32,2 mm (also 32,2 mm - 27,4 mm = 4,8 mm Aufmaß). **Das endgültige Maß <u>mit Träger ist also 66 x 32,2 mm</u> bzw. <u><span style="color: rgb(0,51,102);">aufgeklebt 62 x 32,2 mm</span></u>**
- → Die Etikettenmotive sind auf dem Etikett geringfügig kleiner gedruckt, da links und rechts noch ein Rand vom Drucker eingefügt wird (ca. 1,6 mm Rand links und ca. 1,0 mm Rand rechts)

Die InkScape-Erweiterung nutzt folgende Schritte zur Etikettengenerierung

1. Objekt-Exportdatei (.csv) einlesen
2. SVG-Vektorgrafik auf Templatebasis generieren (InkScape) → eigenes PlugIn (generator-cli + Barcode generator Hybrid). Siehe Gitea. Für jedes Objekt wird eine eigene Datei auf dem Rechner abgelegt. Das Dateinamenschema ist &lt;ID&gt;\_&lt;Titel&gt;.SVG
3. die SVG als PNG exportieren
4. die PNG über das Tool "brother\_ql" an den Etikettendrucker QL-720NW von Brother senden und ausdrucken (optional). Siehe [3. Druckerkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/3-druckerkonzept "3. Druckerkonzept")

User Interface InkScape Extension "Inventory Sticker"

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/tpKTpFPCAUvVQOMP-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/tpKTpFPCAUvVQOMP-grafik.png)

Siehe auch [Inventory Sticker](https://old.stadtfabrikanten.org/display/IFM/Inventory+Sticker)

<p class="callout warning">**Achtung** Wird während des Nutzens der Erweiterung im Backend der Titel des Things geändert, dann muss ein paar Minuten gewartet werden, bis der automatisch laufende Cronjob eine neue `inventory.csv` Datei generiert hat! Andernfalls drucken wir alte Etiketten.</p>

## Beispiel Output

Die Titellänge kann maximal 100 Zeichen betragen. Entsprechend wurden die Aufkleber so designed, dass sie dies unterstützen. Um die Platzausbeute zu maximieren wurden Tricks in den Quellcode eingebaut, damit der Text nicht Wort für Wort, sondern auch nach beliebigen Buchstaben auf neue Zeilen umgebrochen werden kann. Um das zu erreichen wird zunächst hinter jeden Buchstabe ein gesondertes Leerzeichen eingefügt. Um diese Lücken zu kompensieren benötigen wir die Attribute "xml:space" = "preserved" und das Attribut "letter-spacing".

<table border="1" id="bkmrk-100-zeichen-im-titel" style="border-collapse: collapse; width: 99.9734%; height: 266.017px;"><colgroup><col style="width: 49.9668%;"></col><col style="width: 49.9668%;"></col></colgroup><thead><tr style="height: 38.1167px;"><td style="height: 38.1167px;">**100 Zeichen im Titel (maximal möglich)**</td><td style="height: 38.1167px;">**42 Zeichen im Titel (übliche Längen)**</td></tr></thead><tbody><tr style="height: 227.9px;"><td style="height: 227.9px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/WWVlWGZSo3TD2EAB-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/WWVlWGZSo3TD2EAB-grafik.png)

</td><td style="height: 227.9px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/ZYWpleo7PU7eZTUe-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/ZYWpleo7PU7eZTUe-grafik.png)</td></tr></tbody></table>


Ein ausgedrucktes Label (gerade beim Erfassen mit einem [Barcode Scanner](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/5-scanner-konzept "5. Scanner-Konzept")):

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/Nm4cu8FEbpFHI056-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/Nm4cu8FEbpFHI056-grafik.png)

# 3. Druckerkonzept

Client-Script sendet die Aufkleber an einen Etikettendrucker ([Modell Brother QL-720NW](https://things.fablabchemnitz.de/#/document/view/5df2694b-a6be-405e-a6aa-e0f39d744c44/content))

Die im Schritt [2. Etikettenkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/2-etikettenkonzept "2. Etikettenkonzept") erzeugten Aufkleberdateien werden durch das InkScape-PlugIn auf dem Dateisystem abgelegt. Das folgende Script kann genutzt werden, um diese Aufkleber dann auf einem Brother QL-720NW auszudrucken. Diese Funktionalität ist auch Teil des InkScape Plugins. Jedoch kann es auch separat genutzt werden. Wir nutzen dafür das Betriebssystem Ubuntu 20 LTS, den offiziellen Treiber von Brother und eine spezielle Python-Bibliothek namens "[brother-ql](https://pypi.org/project/brother-ql)". Zunächst muss auf dem Rechner, an dem der Etikettendrucker angeschlossen ist, eine entsprechende Installation der Treiber und des Python-Pakets vorgenommen werden.

## Brother QL-720NW auf Windows/Linux installieren

Die Treiber können unter [https://support.brother.com/g/b/downloadtop.aspx?c=de&amp;lang=de&amp;prod=lpql720nweuk](https://support.brother.com/g/b/downloadtop.aspx?c=de&lang=de&prod=lpql720nweuk) heruntergeladen werden

## Ausdrucken von Labels mit brother\_ql

Wir nutzen nicht das Standardwerkzeug "lp" bzw. "lpr" zur Druckausgabe, da dies schlecht einstellbar ist. Stattdessen nutzen wir das Python-Paket [https://pypi.org/project/brother-ql](https://pypi.org/project/brother-ql/)

**brother\_ql installieren (pip notwendig)**

```bash
cd ~/
sudo apt install python3-pip
python -m venv venv
~/venv/bin/pip3 install --upgrade brother_ql
```

**Verfügbare Drucker listen**

```bash
~/venv/bin/brother_ql --backend pyusb discover
```

Für Windows-Systeme benötigen wir zusätzlich **libusb →** Download [libusb-win32-devel-filter-1.2.6.0.exe](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/XCdXzcOUnIXUskTi-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/XCdXzcOUnIXUskTi-grafik.png)

**Normalen Nutzern außer root Zugang zum Drucker erlauben (Linux)**

Das fixt das Problem "<span class="js-issue-title">usb.core.USBError: \[Errno 13\] Access denied (insufficient permissions)</span>":

```bash
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2044", MODE="666"' > /etc/udev/rules.d/99-garmin.rules && sudo udevadm trigger
```

**PNG ausdrucken**

Größen → 696 px sind 62 mm

```bash
#Linux
brother_ql -m QL-720NW --backend pyusb --printer usb://04f9:2044 print -l 62 --600dpi -r auto /home/tomate/Downloads/InventorySticker/Holzwerkstatt/1_Bandsäge_LB1200F_von_Makita.png
 
#Windows
brother_ql -m QL-720NW --backend pyusb --printer usb://04f9:2044 print -l 62 --600dpi -r auto C:\Users\tomate\Desktop\InventorySticker\1_Bandsäge_LB1200F_von_Makita.png
```

**Troubleshooting**

**ANTIALIAS Fehler beseitigen**

Die Bibliothek ist veraltet und muss manuell editiert werden. Dazu bearbeiten wir:

```bash
sudo vim ~/venv/lib/python3.13/site-packages/brother_ql/conversion.py
```

Siehe [https://github.com/pklaus/brother\_ql/pull/169/commits/324d5c833e62e63778060aeb5287ec97187de95f](https://github.com/pklaus/brother_ql/pull/169/commits/324d5c833e62e63778060aeb5287ec97187de95f)

# 4. Schilderkonzept

## Etiketten werden auf farblich getrennte und geometrisch geeignete Träger aufgebracht

Das Schilderkonzept für die per [3. Druckerkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/3-druckerkonzept "3. Druckerkonzept") gedruckten Aufkleber dient der übersichtlichen Beschriftung von Werkzeugen und den Markierungen, wo diese ortsgebunden hingehören. Entsprechend unserer räumlichen Trennung wird den Trägermedien für die Aufkleber jeweils eine Farbe zugewiesen - diese soll sofort in den Blick des Betrachters fallen (Infos zur Farbzuweisung siehe [Werkstattorientierung im FabLab - Digitales Inventar](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/werkstattorientierung-im-fablab-digitales-inventar "Werkstattorientierung im FabLab - Digitales Inventar")). Ziel ist es, dass die Aufkleber am jeweiligen Inventargegenstand bei Benutzung nicht stören und nicht so schnell kaputt gehen können. Es soll möglichst auf Kleber auf den Schildern verzichtet werden, weil sich dieser meist schlecht ablösen lässt. Das Befestigen dieser sollte deshalb möglichst durch sicheres Anheften erfolgen - dazu benötigen die Schilder entsprechende geometrische Merkmale wie Löcher, Ösen oder Laschen. Wir drucken diese selbst mit dem 3D-Drucker, denn dadurch sind wir flexibel, was die Farb- und Materialauswahl angeht und können individuelle Anpassungen an Etikettengrößen vornehmen. Standardschlüsselschilder sind deshalb leider nicht hinreichend geeignet. Wir möchten außerdem darauf verzichten die gedruckten Inventaraufkleber aus [2. Etikettenkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/2-etikettenkonzept "2. Etikettenkonzept") vom Trägermedium abzuziehen, denn auch die Schilder sollen mehrfach wiederverwendet werden können. Die Etiketten werden auf das entsprechende Schild aufgeschoben und mit einer passend zugeschnittenen Transparentblende (0,5mm PETG Folie) versehen. Die Blende dient zum Schutz des Aufklebers, damit dieser länger haltbar und scanbar bleibt (z.B. typische Probleme durch Kratzer, Schmutz, Nasswerden, etc.)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/ARh3cclDdcqmZpxN-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/ARh3cclDdcqmZpxN-grafik.png)

*Beispiel-Beschilderung am ehemaligen 3D-Drucker "Stahlschweinmammut"*

## Der Träger mit Schutzfolie

3D Modell siehe hier: [https://manyfold.stadtfabrikanten.org/models/nv1rc9cc0ks4](https://manyfold.stadtfabrikanten.org/models/nv1rc9cc0ks4)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/N40AK4nzlNgC49Ol-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/N40AK4nzlNgC49Ol-grafik.png)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/dzeKC1sqwkhSWjH2-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/dzeKC1sqwkhSWjH2-grafik.png)

*Ausgelasertes Vivak-Schild mit beidseitger Schutzfolie*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/GdzmAXLEMsrqFFXb-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/GdzmAXLEMsrqFFXb-grafik.png)

*13x Träger auf einmal ausgedruckt*

## Ideen und Vorschläge zum Aufbringen von Etiketten/Schildträgern

Zum Befestigen der Schilder mit Aufklebern bzw. nur der Aufkleber (direktes Aufkleben) gibt es je nach benötigter Situation unzählige Möglichkeiten mit diversen Vor- und Nachteilen. Gut sind vor allem günstige Massenprodukte wie Büroklammern, Maulklammern, Nadeln, Pins, Schrauben, Nägel, Haarklammern, doppelseitiges Klebeband, Klettverschluss, Magneten oder Fäden. Für die folgenden, beispielhaft gedachten Anwendungsfälle eignen sich bestimmte Produkte besser als andere. Unser 3D-Modell vom Schildträger ist so konstruiert, dass verschiedene dieser Dinge daran befestigt werden können. So ist es versatil genug, um auf viele unterschiedliche Varianten zu verzichten.

- **für Dokumente, Magazine und Bücher (auch Handbücher):** Schildträger mit Haarklammer oder nur der blanke Aufkleber auf der Innenseite oder Rückseite des Buches
- **für Spinde aus Metall:** Schildträger mit Magneten
- **für Kork-Pinnwände:** Schildträger mit Steckerchen/Pins
- **für Whiteboards:** Schildträger mit Magneten
- **für Regale:** Schildträger mit doppelseitigem Klebeband
- **für Flexfolien/PVC-Folien:** Schildträger mit Büroklammer, Geldklammer, Haarklammer, Wäscheklammer - ggf. mit Filzen zwischendrin, um die Folien zu schonen
- **für Filamentrollen (3D-Druck)**: jeweils einzeln im großen Zipperverschlussbeutel oder in großen Stopftabak-Box (1000 Gramm)
- **für Schaumstoff oder tonartige Masse:** Schildträger mit aufgebogene Büroklammer
- **für kleine Handmaschinen:** Schildträger mit doppelseitigem Klebeband, direktes Aufkleben an sichtbare Stelle, die nicht abgegriffen oder abgenutzt wird
- **für Kleine Handwerkzeuge:** Einfärben, Aufkleben und Umwickeln des Aufklebers mit durchsichtigem Klebeband oder Folie (z.N. wenn es sich in Griffnähe befinden muss und angefasst wird)
- **für Kabel:** [transparenter Schrumpfschlauch](https://www.ebay.de/itm/116895016467?_skw=schrumpfschlauch+5+1+transparent&var=417019111434&itmmeta=01KKSPSZ27FCW5DQQR4VSFR6HY&hash=item1b377c6613:g:5QQAAOSw3upa03Co&itmprp=enc%3AAQALAAAA4GfYFPkwiKCW4ZNSs2u11xDFc6h1zIPkLMIPj2FKcu0YsZZ%2FndeAAj27K7MrGtZy5Ckbaf7vAWaeY%2BO5AKj1N2X2Moa5KwDW9FbZo%2FTf2vn7cKpVoIoaxv3E1%2FIAKSNIk2v3eQWfVzcA7KB3HQJAKYeVGbF1ZWOo2YKBdmpuCijLroU9DsNgzE0FuqJyp6LC21pPOpVTd%2F76cQzDwZfraH597oNzb9AASmRY8ICOpdCf%2BPe3DlgASgYviigC6%2Fj7fGY5jwgfpjjrYMGVxPIseYoQB5IWG3ji2W3HUz%2F8aNx%2B%7Ctkp%3ABlBMULbx57aeZw) mit verschiedenen Schrumpfverhältnissen von 2:1 bis 5:1, Etiketten mit Fähnchen
- **für Objekte, wo alles andere stört:** hier entfallen zum Teil die 3D-gedruckten Schildträger, weil sie zu groß sind und stören (zum Beispiel an einem Hammer oder einem Schraubendreher)

### Befestigungsmöglichkeiten der Schildträger - Beispiele

Das von uns verwendete Trägermodell erlaubt die Verwendung von Maulklammern, handelsüblichen Haarspangen, Nadeln, Sicherheitsnadeln, Senkschrauben und Magneten (idealerweise Neodymmagneten mit dem maximalen Maß von Ø8 x 1,0 mm oder kleiner)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/sVnhfhziefoaP8bd-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/sVnhfhziefoaP8bd-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/v4RufSRBP7G7OlOP-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/v4RufSRBP7G7OlOP-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/G3F6090hfSbUWPb6-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/G3F6090hfSbUWPb6-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/zXjzXYuzFGt6L874-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/zXjzXYuzFGt6L874-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/XhI7uGjakLHVAkZF-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/XhI7uGjakLHVAkZF-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/m6NblhWD1qiPyDEU-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/m6NblhWD1qiPyDEU-grafik.png)

### Etiketten ohne Schildträger

Falls kein Platz ist, dann kann das Etikett auch mit einem Farbstift oder einem farbigen Klebepunkt markiert werden.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/GpsVY689oeqU62Uo-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/GpsVY689oeqU62Uo-grafik.png)

## Weitere Tipps für die Werkstattordnung

Insbesondere Werkzeugwände sind häufig chaotisch. Optimal sind Lochrasterwände, Nadelwände oder Magnetwände und dazu aufgezeichnete Werkzeugumrisse. Innerhalb oder neben jedem Umriss könnte jeweils ein Schild mit Inventaraufkleber angebracht sein, was für ein Werkzeug hingehört.

# 5. Scanner-Konzept

Schilder (siehe [4. Schilderkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/4-schilderkonzept "4. Schilderkonzept")) werden auf die Objekte angebracht und sind per Barcode scanbar durch die Nutzer (Scanner-Konzept).

## Apps

Geeignete (auf Funktionaität geprüfte) Barcode-Scanner Software für Android Geräte sind

- ["Barcode Scanner" von ZXing Team](https://f-droid.org/packages/com.google.zxing.client.android/) ([https://github.com/zxing/zxing](https://github.com/zxing/zxing)) (Apache 2.0 License)
- [QR &amp; Barcode Scanner](https://f-droid.org/packages/com.example.barcodescanner) ([https://github.com/dmitriy-ilchenko/QrAndBarcodeScanner](https://github.com/dmitriy-ilchenko/QrAndBarcodeScanner)) (Unlicense)
- [Binary Eye](https://f-droid.org/en/packages/de.markusfisch.android.binaryeye/) ([https://github.com/markusfisch/BinaryEye](https://github.com/markusfisch/BinaryEye)) (MIT License)

Die Apps können so konfiguriert bzw. verwendet werden, dass sie die URLs automatisch nach dem Scannen öffnen.

## Test mit [QR &amp; Barcode Scanner](https://f-droid.org/packages/com.example.barcodescanner)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/bKfqMoA29k6EGTRm-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/bKfqMoA29k6EGTRm-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/hlVBuoifiqvXC8tN-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/hlVBuoifiqvXC8tN-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/W9zt580jz58Md644-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/W9zt580jz58Md644-grafik.png)

# 6. Web Server Redirect Konzept

<div class="page view" id="bkmrk-nutzer-scannen-die-o"><article>## Nutzer scannen die Objekte und werden auf die entsprechende Objektseite im Inventarsystem umgeleitet

Der mit einer Barcode Scanner App gescannte Datensatz (siehe [5. Scanner-Konzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/5-scanner-konzept "5. Scanner-Konzept")) enthält eine URL, die direkt zum Inventarsystem weiterleitet. Dafür hat unser Verein die Domain [http://qwa.es](http://qwa.es) akquiriert. Auf dieser Domain lauscht ein simpler Web Server mit nginx-Redirect Konfiguration. So leitet beispielsweise die Eingabe "[qwa.es/1](http://qwa.es/1)" (8 Zeichen) an den Artikel mit der internen Objekt-ID auf folgende URL weiter: [https://things.fablabchemnitz.de/#/document/view/62dced06-4883-496a-b6d0-bf33e60b4ef6/content](https://things.fablabchemnitz.de/#/document/view/62dced06-4883-496a-b6d0-bf33e60b4ef6/content) (93 Zeichen). Es ergibt sich der Vorteil, dass der Redirect jederzeit umgebaut werden kann. Falls sich die URL der Inventarplattform verändert, so müssen keine neuen Barcodes generiert werden, somit auch nicht zwangsweise neue Inventaraufkleber bereitgestellt werden.

Die interne Dokumentation zum nginx-Web Server Setup ist hier zu finden: [qwa.es Iventory Document ID ↔ metaid vhost mapper](https://old.stadtfabrikanten.org/pages/viewpage.action?pageId=86082326)

<div class="wiki-content" id="bkmrk-"></div></article></div>

# 7. Werkstattmonitor-Konzept

# Allgemein

Zur Orientierung in der Werkstatt und im Verein allgemein befinden sich in jedem größeren Bereich werkstatttaugliche Touch-Monitore. Wir nutzen das Modell [FT156TMBCAPOB von Faytech](https://things.fablabchemnitz.de/#/document/view/bd829a6a-ecbd-45eb-bd5c-d5039f3297b5/content). Diese sind für Multitouch-Input geeignet und außerdem staub- sowie spritzwassergeschützt. An jedem Monitor befindet sich jeweils ein [Raspberry Pi 4 Model B](https://things.fablabchemnitz.de/#/document/view/26b0ef66-21e6-4806-82b1-45984e81f5c4/content) mit PoE-Stromversorgung, da wir PoE-LAN ausbauen und so auf extra Netzteile verzichten können. Auf den Monitoren sollen wichtige Dinge angezeigt werden können, die für Mitglieder relevant sind, sowie allgemeine Infos, die auch Besuchern der offenen Werkstatt angezeigt werden (Besucherleitsystem). Das sind zum Beispiel aktuelle Veranstaltungen im Verein, ein Newsfeed unserer Blogs und sonstige Neuigkeiten aus der Werkstatt, wie neu hinzugekommenes Equipment oder der Stromverbrauch im Rückblick. Für diese Zwecke wurden mehrere Grafana Dashboards entwickelt und den Mitgliedern zur Verfügung gestellt. Die Raspberry Pi's sind so eingerichtet, dass sie beim Hochfahren automatisch einen Browser und die entsprechenden URLs öffnen. Auf diese Weise ist ein schnelles Zurechtfinden möglich und Mitglieder bzw. NutzerInnen können up to date bleiben.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/rSSXB93ZX1nhA5d2-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/rSSXB93ZX1nhA5d2-grafik.png)

*Raspberry Pi 4 Model B als Werkstattmonitor-Leitsteuerung*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/8SKuPWpYgZsQSgUT-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/8SKuPWpYgZsQSgUT-grafik.png)

*Das zentrale Grafana Dashboard als Startseite*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/80Lr9IWjU6Nkphog-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/80Lr9IWjU6Nkphog-grafik.png)

*Inventar-Plattform als Schnellzugriff*

## Hardware-Konzept

Für die verwendeten Monitore wurden keine fertigen Wandhalterungen gekauft. Diese wurden stattdessen selbst als integrale Komplettgehäuse aus Sperrholzplatten und 3D-gedruckten Winkeln konzipziert. Dieses Konzept wird nachfolgend vorgestellt und dokumentiert.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/ByMYEMyYx4MUdoph-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/ByMYEMyYx4MUdoph-grafik.png)

*Ein Werkstattmonitor in der Holzwerkstatt*

## Gedanken

- Solide Kapselung aller Komponenten (Monitor, Raspberry Pi mit PoE Hat und Gehäuse, HDMI-Kabel (Audio + Video), USB-Kabel vom Touch Device Input)
- Gehäuseinneres selbst ist nicht 100%ig staubgeschützt, jedoch die wichtigsten Komponenten selbst schon
- Kabelage (Ständiges An- und Abklemmen der KAbel an Raspberry Pi und Monitor soll nach Möglichkeit vermieden werden)  
    
    - Das Monitor-Netzteil befindet sich außerhalb vom selbst hergestellten Gehäuse. Das Netzteil bleibt dauerhaft am Display angeschlossen
    - Ein Ethernet-Kabel bleibt dauerhaft mit dem Werkstattmonitor verbunden. Die Monitore werden in die Nähe der PoE Ethernet-Dosen installiert.
- Es gibt Aussparung zum Bedienen des An-/Aus-Knopfes und Hell/Dunkel, nicht jedoch der oberen beiden Buttons (Quellenauswahl / Scan), da diese die Grundkonfiguration des Displays unnötig verstellbar machen.  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-08/scaled-1680-/DF5x7aobomK0tFMh-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-08/DF5x7aobomK0tFMh-grafik.png)
- Die Halterung ist so aufgebaut, dass sie einfach an der Wand festgeschraubt werden kann (3D-gedruckte Scharniere mit definierten Abmessungen). Das Gehäuse erlaubt zusammen mit den Wandwinkeln und dem Monitorhebel mehrere angenehme Positionen, wobei der geringste Winkel ca. -5° beträgt, der maximale ca. 55°  
    [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/Ie5z0LSxizNpYGu3-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/Ie5z0LSxizNpYGu3-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/5zQb1VtD7ppz7kAC-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/5zQb1VtD7ppz7kAC-grafik.png)
- Das Display ist rückseitig verschraubt und ebenbündig mit der vordersten Platte
- das Display wird so an die Wand befestigt, dass die hintere Verstellung zur Anpassung des Winkels genutzt werden kann.
- Die Wandhalterung besteht aus mehreren Sperrholzplatten (10 Stück) die **nicht** verleimt, sondern verschraubt wurden. Dadurch ist ein nachträgliches Anpassen der einzelnen Bauteilschichten einfacher. Insbesondere wenn eine der unten genannten Modifikationen eingebracht werden soll (z.B. externe USB-Zugang, extra Sensoren, etc.)
- Anschlüsse extern 
    - Stromversorgung Raspberry Pi via USB-C Netzteil oder PoE → die Raspis sollten permanent an bleiben
    - Monitor separat mit Strom versorgt via Kaltgerätestecker → Monitor sollte ggf. manuell via Rückseite ausgeschalten werden und/oder per Schaltsteckdose ggf. zu bestimmten Uhrzeiten automatisch an-/abgeschalten werden
- Wartung des Monitorsystems: Falls man an die Hardware gelangen möchte, so muss die Rückseite abgeschraubt werden. Dazu müssen alle Schrauben gelockert werden (auch die von den 3D-gedruckten Halterungen)

Unsere Werkstattmonitorgehäuse wurden mit Inkscape angefertigt. Das 3D-Modell der Monitore findet sich in der vom Hersteller bereitgestellten [3D PDF Datei](http://b72957eb44a7a2a73e3f-8ad46fee36ae8f657bde8837886a1f96.r30.cf6.rackcdn.com/3d%20models/Cap_TM/FT156TMBCAPOB_3D_Model.pdf).

## Source Files

Siehe [https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/README.md](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/README.md)

## Benötigtes Werkzeug

- Bandschleifer (zum Schleifen der zusammengeschraubten Platten, die das Gehäuse ergeben)
- Senker mit Begrenzung (zum individuellen Senken der Scharnierteile, falls Senkschrauben genutzt werden)
- Torx Bit (Größe T10 für 3 mm Holzschrauben)
- Akkuschrauber oder Schraubendreher mit Bit-Halterung
- Cuttermesser
- Lasercutter (ca. 33 Minuten reine Laserzeit)
- Sechskantschüssel SW 2,5 mm (zur Befestigung des Monitors an der Halteplatte (Platte 3)
- Farbroller (zum gleichmäßigen Auftragen des Klarlacks)
- Etikettiergerät (zum Beschriften von An/Laut/Leise Knöpfen)

## Stückliste

<details id="bkmrk-st%C3%BCckliste-nr-teil-m"><summary>Stückliste</summary>

<table border="1" id="bkmrk-nr-teil-menge-bild-h" style="border-collapse: collapse; width: 99.9734%; height: 2385.42px;"><thead><tr style="height: 38.1167px;"><td style="height: 38.1167px;">Nr</td><td style="height: 38.1167px;">Teil</td><td style="height: 38.1167px;">Menge</td><td style="height: 38.1167px;">Bild</td><td style="height: 38.1167px;">Hinweise</td></tr></thead><tbody><tr style="height: 182.3px;"><td style="height: 182.3px;">1</td><td style="height: 182.3px;">[Sperrholz Platte #1 (Deckplatte mit Logo)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte1.svg)</td><td style="height: 182.3px;">1 Stück</td><td style="height: 182.3px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/w9pxfMtpQAlTooY1-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/w9pxfMtpQAlTooY1-grafik.png)

</td><td style="height: 182.3px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

</td></tr><tr style="height: 182.3px;"><td style="height: 182.3px;">2</td><td style="height: 182.3px;">[Sperrholz Platte #2 (Rahmen)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte2.svg)</td><td style="height: 182.3px;">1 Stück</td><td style="height: 182.3px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/odHM0gg6k4SIH2FM-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/odHM0gg6k4SIH2FM-grafik.png)

</td><td style="height: 182.3px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

</td></tr><tr style="height: 182.3px;"><td style="height: 182.3px;">3</td><td style="height: 182.3px;">[Sperrholz Platte #3 (Aufnahme für Monitor / Halteplatte)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte3.svg)</td><td style="height: 182.3px;">1 Stück</td><td style="height: 182.3px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/mMPKnLv9uwLoQ6eh-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/mMPKnLv9uwLoQ6eh-grafik.png)

</td><td style="height: 182.3px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

<p class="callout warning">Die Platte liegt dauerhaft auf den **Buttons Quellenauswahl und Scan** des Monitors auf. Falls dies stört kann eine ca. 1 mm hohe Aussparung auf der Rückseite ausgekratzt werden</p>

</td></tr><tr style="height: 72.3333px;"><td style="height: 72.3333px;">4</td><td style="height: 72.3333px;">[Sperrholz Platte #4 (Zwischenplatte)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte4.svg)</td><td style="height: 72.3333px;">4 Stück</td><td style="height: 72.3333px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/lhrRZQnf74UQBhhN-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/lhrRZQnf74UQBhhN-grafik.png)

</td><td style="height: 72.3333px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

</td></tr><tr style="height: 101.5px;"><td style="height: 101.5px;">5</td><td style="height: 101.5px;">[Sperrholz Platte #5 (Zwischenplatte mit Haltenasen)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte5.svg)</td><td style="height: 101.5px;">1 Stück</td><td style="height: 101.5px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/NSnK0DFfjuORdSaa-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/NSnK0DFfjuORdSaa-grafik.png)

</td><td style="height: 101.5px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

</td></tr><tr style="height: 167.7px;"><td style="height: 167.7px;">6</td><td style="height: 167.7px;">[Sperrholz Platte #6 (Versteifungsplatte)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte6.svg)</td><td style="height: 167.7px;">1 Stück</td><td style="height: 167.7px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/BKndE8FAUz8cM0ae-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/BKndE8FAUz8cM0ae-grafik.png)

</td><td style="height: 167.7px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

<p class="callout warning">Die Löcher in dieser Platte sind enger als die aller anderen Platten. Sowohl die Schrauben aus Platte 10, als auch die der Platten 1 bis 8 werden in dieser "zentral" verankert.</p>

</td></tr><tr style="height: 73.3333px;"><td style="height: 73.3333px;">7</td><td style="height: 73.3333px;">[Sperrholz Platte #7 (Bodenplatte)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Werkstattmonitore_v3_Platte7.svg)</td><td style="height: 73.3333px;">1 Stück</td><td style="height: 73.3333px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/BH3lB8lrlM7QdslW-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/BH3lB8lrlM7QdslW-grafik.png)

</td><td style="height: 73.3333px;">Sperrholzplatte Meranti - minimale Abmessungen: 295 x 435 x 5,6 mm

</td></tr><tr style="height: 45.1667px;"><td style="height: 45.1667px;">8</td><td style="height: 45.1667px;">[Raspberry Pi 4 Model B](https://things.fablabchemnitz.de/#/document/view/26b0ef66-21e6-4806-82b1-45984e81f5c4/content)</td><td style="height: 45.1667px;">1 Stück</td><td style="height: 45.1667px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/u40xt6vnzXE6E6nH-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/u40xt6vnzXE6E6nH-grafik.png)

</td><td style="height: 45.1667px;">  
</td></tr><tr style="height: 45.1667px;"><td style="height: 45.1667px;">9</td><td style="height: 45.1667px;">[Raspberry Pi 4 PoE Hat](https://www.raspberrypi.org/products/poe-hat/)</td><td style="height: 45.1667px;">1 Stück</td><td style="height: 45.1667px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/k8ansGx8jgQ3JluC-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/k8ansGx8jgQ3JluC-grafik.png)

</td><td style="height: 45.1667px;">  
</td></tr><tr style="height: 73.3333px;"><td style="height: 73.3333px;">10</td><td style="height: 73.3333px;">[Raspberry Pi 4 Gehäuse "Argon Neo"](https://www.reichelt.de/de/de/gehaeuse-fuer-raspberry-pi-4-alu-schwarz-rpi4c-argon-neo-p290779.html?r=1)</td><td style="height: 73.3333px;">1 Stück</td><td style="height: 73.3333px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/OaPGeNtArOxLVPxD-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/OaPGeNtArOxLVPxD-grafik.png)

</td><td style="height: 73.3333px;">  
</td></tr><tr style="height: 73.3333px;"><td style="height: 73.3333px;">11</td><td style="height: 73.3333px;">[Faytech Werkstattmonitor FT156TMBCAPOB](https://things.fablabchemnitz.de/#/document/view/bd829a6a-ecbd-45eb-bd5c-d5039f3297b5/content)</td><td style="height: 73.3333px;">1 Stück</td><td style="height: 73.3333px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/3wAvJUtlGQAyfOxw-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/3wAvJUtlGQAyfOxw-grafik.png)

</td><td style="height: 73.3333px;">  
</td></tr><tr style="height: 38.1167px;"><td style="height: 38.1167px;">12</td><td style="height: 38.1167px;">SD-Karte 32 GB SDHC</td><td style="height: 38.1167px;">1 Stück</td><td style="height: 38.1167px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/LreSqZJvTSczpvG1-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/LreSqZJvTSczpvG1-grafik.png)

</td><td style="height: 38.1167px;">  
</td></tr><tr style="height: 59.2333px;"><td style="height: 59.2333px;">13</td><td style="height: 59.2333px;">Faytech Werkstattmonitor Netzteil</td><td style="height: 59.2333px;">1 Stück</td><td style="height: 59.2333px;">  
</td><td style="height: 59.2333px;">  
</td></tr><tr style="height: 61.2333px;"><td style="height: 61.2333px;">14</td><td style="height: 61.2333px;">Faytech USB-Kabel für Touch Input</td><td style="height: 61.2333px;">1 Stück</td><td style="height: 61.2333px;">  
</td><td style="height: 61.2333px;">wird zum Monitor mitgeliefert</td></tr><tr style="height: 59.2333px;"><td style="height: 59.2333px;">15</td><td style="height: 59.2333px;">Faytech Monitor Tischhalterung</td><td style="height: 59.2333px;">1 Stück</td><td style="height: 59.2333px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/WPob9QLKhfogHoKd-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/WPob9QLKhfogHoKd-grafik.png)

</td><td style="height: 59.2333px;">wird zum Monitor mitgeliefert</td></tr><tr style="height: 80.35px;"><td style="height: 80.35px;">16</td><td style="height: 80.35px;">Faytech Monitor Tischhalterung Befestigungsschrauben</td><td style="height: 80.35px;">4 Stück</td><td style="height: 80.35px;">  
</td><td style="height: 80.35px;">wird zum Monitor mitgeliefert</td></tr><tr style="height: 38.1167px;"><td style="height: 38.1167px;">17</td><td style="height: 38.1167px;">HDMI Kabel</td><td style="height: 38.1167px;">1 Stück</td><td style="height: 38.1167px;">  
</td><td style="height: 38.1167px;">  
</td></tr><tr style="height: 38.1167px;"><td style="height: 38.1167px;">18</td><td style="height: 38.1167px;">Ethernet Kabel RJ45</td><td style="height: 38.1167px;">1 Stück</td><td style="height: 38.1167px;">  
</td><td style="height: 38.1167px;">  
</td></tr><tr style="height: 125.467px;"><td style="height: 125.467px;">19</td><td style="height: 125.467px;">Zylinderkopfschraube ISO 4762 A2 - M3 x 10 mm</td><td style="height: 125.467px;">8 Stück</td><td style="height: 125.467px;">  
</td><td style="height: 125.467px;">Fixieren das Display im Gehäuse

<p class="callout warning">**Warnung:** keine längeren Schrauben verwenden! Dies kann das Display beschädigen</p>

</td></tr><tr style="height: 59.2333px;"><td style="height: 59.2333px;">20</td><td style="height: 59.2333px;">Unterlegscheibe DIN 125 A - 3mm</td><td style="height: 59.2333px;">16 Stück</td><td style="height: 59.2333px;">  
</td><td style="height: 59.2333px;">Für die Zylinderkopfschrauben (2 Stück je Schraube) - verhindern Durchrutschen der Schraubköpfe</td></tr><tr style="height: 80.35px;"><td style="height: 80.35px;">21</td><td style="height: 80.35px;">Spanplattenschraube Senkkopf Torx T10 A2 3 x 16 mm</td><td style="height: 80.35px;">19 Stück</td><td style="height: 80.35px;">  
</td><td style="height: 80.35px;">zur Befestigung der Rückplatte (Platte 10) an Platte 9 - bei Bedarf können u.U. auch längere Schrauben verwendet werden (z.b. 3 x 20 mm)</td></tr><tr style="height: 80.35px;"><td style="height: 80.35px;">22</td><td style="height: 80.35px;">Spanplattenschraube Senkkopf Torx T10 A2 3 x 20 mm</td><td style="height: 80.35px;">8 Stück</td><td style="height: 80.35px;">  
</td><td style="height: 80.35px;">zur Befestigung der Scharnierteile an der Rückseite des zusammengeschraubten Gehäuses</td></tr><tr style="height: 80.35px;"><td style="height: 80.35px;">23</td><td style="height: 80.35px;">Spanplattenschraube Senkkopf Torx T10 A2 3 x 50 mm</td><td style="height: 80.35px;">13 Stück</td><td style="height: 80.35px;">  
</td><td style="height: 80.35px;">für Verschraubung der Sperrholzplatten miteinander (Platten 1 bis 9)</td></tr><tr style="height: 45.1667px;"><td style="height: 45.1667px;">24</td><td style="height: 45.1667px;">[Scharnier (3D-Druck)](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Scharnier.stl)</td><td style="height: 45.1667px;">2 Stück</td><td style="height: 45.1667px;">  
</td><td style="height: 45.1667px;">  
</td></tr><tr style="height: 59.2333px;"><td style="height: 59.2333px;">25</td><td style="height: 59.2333px;">Zylinderschraube ISO 4762 - A2 M5 x 70 mm</td><td style="height: 59.2333px;">1 Stück</td><td style="height: 59.2333px;">  
</td><td style="height: 59.2333px;">Bolzen für 3D-gedruckte Wandhalterung (u.U. reichen Zylinderschrauben der Länge M5 x 65 mm)</td></tr><tr style="height: 59.2333px;"><td style="height: 59.2333px;">26</td><td style="height: 59.2333px;">Sicherungsmutter ISO 10511 - A2 M5</td><td style="height: 59.2333px;">1 Stück</td><td style="height: 59.2333px;">  
</td><td style="height: 59.2333px;">Mutter für Bolzen Zylinderschraube M5 x 70</td></tr><tr style="height: 59.2333px;"><td style="height: 59.2333px;">27</td><td style="height: 59.2333px;">Unterlegscheibe DIN 125 A - 5mm</td><td style="height: 59.2333px;">2 Stück</td><td style="height: 59.2333px;">  
</td><td style="height: 59.2333px;">Zwischenscheibe für M5 x 75 mm Bolzen</td></tr><tr style="height: 80.35px;"><td style="height: 80.35px;">28</td><td style="height: 80.35px;">Hornbach Wohnraumlasur, farblos 750 ml</td><td style="height: 80.35px;">1 Dose</td><td style="height: 80.35px;">  
</td><td style="height: 80.35px;">verhindert Schmutz und Eindringen von Feuchtigkeit; macht das Gehäuse abwaschbar. Lasur zum Pinseln / Auftragen mit Farbroller</td></tr><tr style="height: 45.1667px;"><td style="height: 45.1667px;">29</td><td style="height: 45.1667px;">[Bohrschablone](https://gitea.fablabchemnitz.de/vmario/flc-werkstattmonitore/src/branch/master/Bohrschablone.pdf)</td><td style="height: 45.1667px;">1 Stück</td><td style="height: 45.1667px;">[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/m06rdUt55RrgcYrm-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/m06rdUt55RrgcYrm-grafik.png)

</td><td style="height: 45.1667px;">Für die Befestigung an der Wand (DIN A3 Format)</td></tr></tbody></table>


</details>## Dokumentation der Aufbauschritte


<details id="bkmrk-geh%C3%A4use-iteration-1-"><summary>Gehäuse Iteration 1 - Aller Anfang ist schwer (gescheitert)</summary>

Der erste Entwurf unseres Werkstattmonitorprototyps hat leider nicht ganz gepasst. Selten passt ein Prototyp gleich beim ersten Mal!

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/TLMOvxzyIugXSJi8-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/TLMOvxzyIugXSJi8-grafik.png)

*Beim Zusägen des Merantisperrholzes*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/zZaZ8pbG51kDJemV-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/zZaZ8pbG51kDJemV-grafik.png)

*Jeder Millimeter zäht. 5,6 mm gemessen, aber aus Versehen mit 6,2 mm modelliert*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/SiRP8KnQuiIV0wFj-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/SiRP8KnQuiIV0wFj-grafik.png)

*Ausschneiden mit dem Laser*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/wl6bSQcnW6wsyaAe-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/wl6bSQcnW6wsyaAe-grafik.png)

*Einzelteile*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/NtNnGw3udAj011kA-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/NtNnGw3udAj011kA-grafik.png)

*Mit Display eingesetzt sieht es schon hübsch aus.*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/QiK5m8MLE0qKQf46-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/QiK5m8MLE0qKQf46-grafik.png)

*Kabelkollisionen stören beim Einsetzen. Außerdem ist der Raspberry Pi zu dick*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/kMg9ZOiXN7juO6WD-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/kMg9ZOiXN7juO6WD-grafik.png)

*Erst geleimt. Und dann festgestellt, dass Verschrauben besser wäre*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/I3lD3QjJKztMHZzr-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/I3lD3QjJKztMHZzr-grafik.png)

*Also noch extra verschraubt ...*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/SlClYBVGm9eqiiX0-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/SlClYBVGm9eqiiX0-grafik.png)

*Nach dem Schleifen*

</details><details id="bkmrk-geh%C3%A4use-iteration-2-"><summary>Gehäuse Iteration 2 - Besser, aber immer noch nicht so ganz passend (gescheitert)</summary>

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/5mNmho2YfPwc5LDJ-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/5mNmho2YfPwc5LDJ-grafik.png)

 *Acrylteile zur besseren Befestigung des Klettbands*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/v9d3PzCwk2FZfC5H-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/v9d3PzCwk2FZfC5H-grafik.png)

*Extra Kabelführungskanäle und Klebeflächen aus Acryl*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/Nl1jmK8xWGCzBmRn-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/Nl1jmK8xWGCzBmRn-grafik.png)

*Mit Kabeln eingepasst und vorbereitet für den Einsatz des Displays*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/YbXp88KoLN8yuNOi-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/YbXp88KoLN8yuNOi-grafik.png)

*Prototyp v2 vollbestückt. Sieht schon gut aus. Passt leider immer noch nicht. Der Platz für die Kabel im Inneren ist zu eng und das Display hält mit den verwendeten Klettveschlüssen schlechter als erhofft.*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/hW29vrKftIr7yuLT-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/hW29vrKftIr7yuLT-grafik.png)

*Rückseite*

</details><details id="bkmrk-geh%C3%A4use-iteration-3-"><summary>Gehäuse Iteration 3 - Die fertige Variante</summary>

1. vor dem Einsetzen und Verschrauben des Monitors im Gehäuse ist darauf zu achten, dass HDMI als Source (Eingang) ausgewählt ist und der Sound auf 100% Volume eingestellt wurde. Denn nach dem Einbau ist der Einstellknopf nicht mehr erreichbar, da er verdeckt ist. Die Standardsettings normalerweise (100% Audiolautstärke):[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/8YdGUXZJbOZf8t9Y-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/8YdGUXZJbOZf8t9Y-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/kEC6N7DFk0yoZWhF-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/kEC6N7DFk0yoZWhF-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/PaA4O1SWNRXGIlrK-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/PaA4O1SWNRXGIlrK-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/FkFm9JzQO3XcEOoN-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/FkFm9JzQO3XcEOoN-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/DdAuDhdTKb5ZyWkI-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/DdAuDhdTKb5ZyWkI-grafik.png)
2. Vor dem Einsetzen des Displays in das Gehäuse schon alle Zylinderschrauben mit Unterlegscheiben einlegen (hilft beim besseren Einfädeln)
3. Für das Anbringen an die Wand gibt es eine Bohrschablone <p class="callout warning">Toleranzen einplanen. Die Winkel sollten sehr genau angeschraubt werden</p>
4. Für die Bedienung der hinten gelegenen Knöpfe sollte ein Etikettiergerät verwendet werden, um die Laut/Leise/Einschalt-Knopf zu beschriften

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/qMLFTKYn1TOW4uh6-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/qMLFTKYn1TOW4uh6-grafik.png)

 *Abkleben der Monitorelektronik*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/PZKBbw7o4mAOVHN2-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/PZKBbw7o4mAOVHN2-grafik.png)

 *Scharniere aus dem Prusa i3 MK3S - beim Slicing*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/yxaYmj1EZM55ePDa-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/yxaYmj1EZM55ePDa-grafik.png)

 *Scharniere drucken - der fast fertige Druck*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/KD74cFSmjg9Rr05Q-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/KD74cFSmjg9Rr05Q-grafik.png)

 *Ansicht der übereinandergelegten Platten*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/K8sGs75m8hpG4ELR-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/K8sGs75m8hpG4ELR-grafik.png)

 *Fertig zusammengeschraubt*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/9GOKWapWPvA5kGRj-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/9GOKWapWPvA5kGRj-grafik.png)

*Nach stundenlangem Laserschneiden und Zusammenschrauben*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/eGrRZTMDu5bDgXhx-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/eGrRZTMDu5bDgXhx-grafik.png)

*Geschliffene Außenflächen*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/sPBhwyN3DN6uWi9y-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/sPBhwyN3DN6uWi9y-grafik.png)

*Beim Lasieren*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/xb8lkapWRVsXkNAn-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/xb8lkapWRVsXkNAn-grafik.png)

 *Fertig lasiert*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/IKqK0lQXVHWPcfHh-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/IKqK0lQXVHWPcfHh-grafik.png)

*Rückseitendeckel verschraubt*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/9bmKokp3k8wt0iAj-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/9bmKokp3k8wt0iAj-grafik.png)

*Winkel aufgeschraubt*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/zqbhBWX7nlXseGiS-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/zqbhBWX7nlXseGiS-grafik.png)

 *mit Kabeln und Raspberry Pi bestückt*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/8Rbgbjyd8oM8ctu5-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/8Rbgbjyd8oM8ctu5-grafik.png)

*Zugentlastung eingebaut*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/op7dBBrrX5vSIree-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/op7dBBrrX5vSIree-grafik.png)

*Bohrschablone anlegen und Löcher vorbereiten*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/pCBeVWOkODPOOw8S-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/pCBeVWOkODPOOw8S-grafik.png)

*Winkelbefestigungen anschrauben*

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/gIMu0DajP0JjWysD-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/gIMu0DajP0JjWysD-grafik.png)

*Im Betrieb*

</details>## Mögliche Probleme und Nachbetrachtungen

### Thermisches

Die Faytech Monitore sind für Betriebstemperaturen zwischen 10 °C und 60 °C laut Datenblatt ausgelegt. Die Kapselung in ein Holzgehäuse ist damit mit höchster Wahrscheinlichkeit völlig unproblematisch. Im Gegensatz kann es u.U. Kühlungsprobleme mit der Kombination aus Raspberry Pi 4 Model B, dem PoE Hat und den verwendeten Raspi-Gehäusen geben. Der PoE Hat hat einen 25mm Axiallüfter verbaut. Da der Lüfter nach oben zum Display zeigt erfüllt eine Ausparung im magnetischen Deckel keinen Zweck. Kleine seitliche Bohrungen (Ø5 - 8 mm) im Deckel können die Zuluft-Abluft-Thematik eventuell entschärfen.

### Mechanisches

Die verwendeten Sperrholzplatten sind relativ weich und bieten keinen Schutz gegen starke mechanische Einwirkungen. Wahrscheinlich bekommen sie mit den Jahren ihren eigenen Chabby Chic Style.

## Weitere Ideen zur langfristigen Ergänzung

Die Werkstattmonitore könnten noch um verschiedene Gadgets und Sensoren erweitert werden, um noch mehr nutzvolle Dinge zu verrichten.

### Barcode Scanner

Durch eine USB Webcam oder eine RPi Cam und einem Python Script, welches aus permanenter Hintergrunddienst läuft, können Objekte mit Barcode aus der Werkstatt gescannt werden und analog zum [5. Scanner-Konzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/5-scanner-konzept "5. Scanner-Konzept") zum entsprechenden Objekt in der Inventar-Instanz weiterleiten. Ein gutes Tutorial findet sich unter [https://tutorials-raspberrypi.de/raspberry-pi-barcode-scanner-qr-mit-kamera-selber-bauen](https://tutorials-raspberrypi.de/raspberry-pi-barcode-scanner-qr-mit-kamera-selber-bauen/). Ein passendes Python Codeschnipsel findet sich dort.

### Medien darstellen

Eine USB-Verlängerung nach außen zu legen, um damit Geräte wie USB Sticks anzuschließen, kann bei der Werkstattnutzung praktisch sein. Nutzer könnten z.B. mitgebrachte technische Zeichnungen aufrufen und darstellen. So könnte der Monitor auch als temporäre Projekthilfe genutzt werden. Der Raspberry Pi 4 Model B ist leistungsfähig genug, um flüssig Videos von YouTube und Co. wiederzugeben. Eine weitere praktische Ergänzung könnte eine Maus-Tastatur-Kombination mit Funk-Empfänger/-Sender sein.

### Durchpausen von Grafiken

Rollenhalter mit Pauspapier und Klemmleiste oben/unten anbringen könnte eine weitere Hilfe sein. Damit kann das Display als Durchpaus-Tool verwendet werden. An der Unterseite könnte ein Papierschneider angebracht werden, um das Pauspapier abzuschneiden. Da das Touch-Display auch reagiert, wenn ein Blatt Papier darauf liegt, muss die USB-Verbindung zwischenzeitlich manuell getrennt werden. Das kann mit einem Schalter passieren. Es gibt USB-Kabel mit an/aus Schalter zu kaufen. Ein kleiner Köcher an der Seite ergänzt das Display mit geeigneten Stiften.

### Werkstattdaten per Sensoren / Monitoring

Ergänzen der Werkstattmonitore um Sensoren für Temperatur, Luftfeuchtigkeit und Helligkeit kann beim Monitoring helfen. Durch die gesammelten Daten ließe sich unter anderem ablesen bzw. deuten, ob Heizungen vergessen wurden auszuschalten, ob evtl. das Licht noch brennt oder ob gelüftet werden sollte. Für die Ressourcennutzung und den sparsamen Einsatz wäre dies ein u.U. sinnvoller Versuch. Die installierten PoE Hats der neu angeschafften Raspberry's verwenden bereits I2C Header, weshalb Sensoren mit 1-Wire (Digital Pin) Anschluss besser geeignet sind. Außerdem benötigen wir Passthrough Stecker ([https://www.raspberrypi.org/blog/introducing-power-over-ethernet-poe-hat)](https://www.raspberrypi.org/blog/introducing-power-over-ethernet-poe-hat/)und es müsste ein Loch in die Außenhülle des Gehäuses für die Kabel der Sensoren eingefeilt werden. Mögliche Sensoren zum Nachrüsten sind:

- [https://eckstein-shop.de/DHT22-AM2302-Digital-Temperatur-Feuchtigkeit-Sensor-mit-Kabel-und-47-kOhm-Widerstand](https://eckstein-shop.de/DHT22-AM2302-Digital-Temperatur-Feuchtigkeit-Sensor-mit-Kabel-und-47-kOhm-Widerstand) (1-Wire)
- [http://www.uugear.com/portfolio/using-light-sensor-module-with-raspberry-pi](http://www.uugear.com/portfolio/using-light-sensor-module-with-raspberry-pi/) (1-Wire mit 1x analog und/oder digital)

## Software-Konzept

Die Software-Konfiguration der Raspberry Pi's findet sich im Admin-Bereich unter [Werkstattmonitore mit Raspberry OS (überholt)](https://old.stadtfabrikanten.org/pages/viewpage.action?pageId=89686107).

## Förderungen

Die Anschaffung der Werkstattmonitore und Raspberry Pi's inklusive Netzwerksystem wurden gefördert von der Beauftragen der Bundesregierung für Kultur und Medien und dem Bundesverband Soziokultur <span class="aCOpRe">im Rahmen der Initiative NEUSTART KULTUR im Jahr 2020.</span>

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/T95OV9HngRe80Ghs-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/T95OV9HngRe80Ghs-grafik.png)

[www.kulturstaatsministerin.de](https://www.kulturstaatsministerin.de)

Die Erarbeitung der Werkstattmonitor-Befestigung (Lasercut Gehäuse und 3D-Druck Winkel) als technologisches Schaubeispiel und deren Dokumentation über Entwicklung, Bau und Installation wird mitfinanziert mit Steuermitteln auf Grundlage des vom Sächsischen Landtag beschlossenen Haushalts im Rahmen der Initiative [Fachkräfteallianz.](https://www.cwe-chemnitz.de/wirtschaft/fachkraefte-berufsorientierung/fachkraefteallianz-chemnitz/)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/Frf14v7FdPbclvXW-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/Frf14v7FdPbclvXW-grafik.png)

# Intro

<div class="page view" id="bkmrk-da-jedes-objekt-im-f"><article>Da jedes Objekt im FabLab seine eigene eindeutige Inventarnummer und zugewiesene Eigenschaften bekommt, ist auch jedes Objekt nachvollziehbar in seiner kompletten Historie und Beschaffenheit. Als physischer Gegenpart zur Software steht deshalb als Aufgabe das Labeln der Objekte mit Inventaraufklebern. Die Gegenstände im FabLab Chemnitz erhalten nach und nach alle einen Inventaraufkleber zur Identifizierung. Ein Etikett dient dazu, das Objekt in Teedy anzuzeigen, indem es per Barcode Scanner gescannt wird. Dieser Barcode enthält eine eindeutige URL, die direkt im Browser aufgerufen wird. Dadurch kann der Nutzer schnell herausfinden, um welches Objekt es sich genau handelt (Fotos, Handbücher, Eigenschaften). Falls eine Sache aus mehreren Objekten zusammengesetzt ist (z.B. Gerät mit Fernbedienung), dann sollte möglichst jedes Einzelobjekt etikettiert werden, um die Zusammengehörigkeit wieder herstellen zu können. Inbesondere, wenn Unordentlichkeit herrscht und die Werkzeuge unsortier in der Werkstatt rumliegen.

**Der Ablauf:**

<div class="wiki-content" id="bkmrk-objekt-exportdatei-g">- Objekt-Exportdatei generieren und per Web Server bereit stellen ([Bereitstellungskonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/1-bereitstellungskonzept "1. Bereitstellungskonzept"))
- InkScape Erweiterung generiert Aufkleber-Vektordateien ([Etikettenkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/2-etikettenkonzept "2. Etikettenkonzept"))
- Client-Script sendet die Aufkleber an einen Etikettendrucker ([Modell Brother QL-720NW](https://things.stadtfabrikanten.org/#/document/view/5df2694b-a6be-405e-a6aa-e0f39d744c44/content)) ([Druckerkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/3-druckerkonzept "3. Druckerkonzept"))
- Etiketten werden auf farblich getrennte und geometrisch geeignete Träger (Schildchen) aufgebracht, Schilder werden dann auf die Objekte angebracht ([Schilderkonzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/4-schilderkonzept "4. Schilderkonzept"))
- Barcodes scannen 
    - Nutzer scannen mit dem eigenen Smarthpone oder Tablet ([Scanner-Konzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/5-scanner-konzept "5. Scanner-Konzept"))
    - Nutzer scannen die Objekte mit einem Scanner an den in den Werkstattbereichen installierten Werkstattmonitoren ([Werkstattmonitor-Konzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/7-werkstattmonitor-konzept "7. Werkstattmonitor-Konzept"))
- Nutzer werden auf die entsprechende Objektseite im Inventarsystem umgeleitet ([Web Server Redirect Konzept](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/6-web-server-redirect-konzept "6. Web Server Redirect Konzept")) (auf dem eigenen Smartphone)

</div></article></div>

# Was benötigen wir für eine eigene Inventar-Instanz?

Du hast dich für eine digitale Inventarverwaltung entschieden? Fein! Das ist der erste Schritt für echte Nachhaltigkeit in der Werkstatt. Ordnung ist das halbe Leben, so sagt man. "Ordnung ist nur was für Leute, die zu faul zum Suchen sind" - so sagt man auch. Das ist leider nur bedingt richtig. Denn diese Aussage gilt nur für Einzelpersonen, aber nicht im Punkt der Zusammenarbeit in einer Gruppe.

Immer dann, wenn mehrere Menschen in einer geteilten Sphäre arbeiten wollen, dann müssen sie sich gut organisieren. Denn niemand weiß, was im Kopf des anderen vorgeht und welche Logik sich im jeweiligen (spontan) entstandenen Use-Case entwickelt. Deshalb kommt man nicht herum, Werkstattregeln zu definieren. Eine Inventarinstanz kann hier extrem helfen.

Absofort willst du Handbücher und Maschinennamen schnell (wieder)finden, sinnloses Umherlaufen vermeiden und nichts mehr doppelt kaufen oder unbedarft wegschmeißen. Du willst endlich wissen, wie Dinge (auch) heißen bzw. deren Synonyme kennen und du willst vielleicht sogar allem einen eigenen Name geben. Du hast dich schon immer gefragt, wofür bestimmte Sachen oder komische Zubehörteile sich eignen, wie man sie handhabt und was man lieber nicht damit ausprobieren sollte. Du möchtest mit einem zentralen System andere in deiner Umgebung schulen und dein eigenes Wissen oder das Wissen deiner Kollegen weitergeben und darauf zeigen können, wo man es findet. Du willst rapide etwas über eine Reparatur oder ein Zubehör nachlesen und rausfinden, wo es seinen festen Ort hat oder ob es noch einen Ort braucht. Du willst verdammt nochmal wissen wem das Teil gehört und ob es Kunst ist oder weg kann.

Für Beginner, die ihre vorhandene Werkstatt grundlegend digital ordnen wollen und sich weniger mit den digitalen IT-Themen auseinandersetzen, sich jedoch zutrauen eine eigene Teedy-Instanz aufzusetzen (aufsetzen zu lassen), bieten wir hier einen Schritt-für-Schritt-Plan.

## Notwendige Hardware und Software

Im einfachsten Fall lässt sich ein eigener Teedy-Server im Büro aufsetzen. Ganz ohne Miet-Server mit monatlichen Fixkosten. Was du dazu nutzen kannst:

- ein Raspberry Pi 3 oder neuer (Für ca. 50-100 € zu erhalten - je nach Ausführung in puncto Netzteil, Gehäuse, Kühlung und Zubehör wie Festplatte): 
    - Wir empfehlen einen Raspberry Pi 3 B+ mit einer auf Ausdauer ausgelegte (high endurance) 8 GB SD-Karte (auf dieser soll nur das Betriebssystem sein)
    - Für die Inventardaten empfehlen wir eine externe USB-Festplatte oder eine im Netzwerk angeschlossene NAS)
    - Betriebssystem: Raspbian
    - Installation von Teedy → Siehe [Teedy Installation and Configuration](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-installation-and-configuration "Teedy Installation and Configuration")
- eine Portfreigabe auf dem Router, falls externer Zugriff (außerhalb der Werkstatt - z.B. von zuhause aus) gewünscht ist
- ggf. dynamisches DNS, um sich keine häufig wechselnde, öffentliche IP-Adresse zu merken, sondern eine feste Domain zu haben, z.B. 
    - kostenfrei via "MyFRITZ!Net" von AVM ([https://sso.myfritz.net](https://sso.myfritz.net/)), sofern eine Fritz!Box genutzt wird
    - kostenfrei via Synology DDNS Service ([https://account.synology.com](https://account.synology.com)), sofern eine NAS-Lösung von Synology genutzt wird

## Notwendige Kompetenzen und Vorplanung

Hardware und Software kann man sich relativ schnell zusammenorganisieren. Nach dieser Hürde, die zugegeben sehr lange dauern kann, wenn etwas schief geht, kommen weitere Folgeaufgaben hinzu. Was brauchen wir? Zunächst pauschal gesagt: einen guten Plan und Durchhaltevermögen. Am Anfang ist alles chaotisch und ein riesiger, unbezwingbarer Berg an Arbeit steht scheinbar an. Konkret brauchen wir Antworten und Entscheidungen:

1. Wer soll sich um das Inventar kümmern? Einer oder alle? Wer kann mit Software gut umgehen? Wer hat ausreichend technischen Sachverstand, um am Inventar zu pflegen oder die notwendigen Infos weiterzugeben?
2. idealerweise eine Übersicht der Werkstatträume (wer gleich gut planen will, der vergibt folgende Attribute: Werkstattnummer, Werkstattname, zugewiesene Farbe zur Wiedererkennung)
3. eine Entscheidung darüber, ob ... 
    - elektrische Maschinen und Werkzeuge eingepflegt werden sollen
    - nicht-elektrische (Hand)werkzeuge eingepflegt werden sollen
    - Möbel eingepflegt werden sollen
    - Verbrauchsmaterialien wie Schrauben oder Kleber eingepflegt werden sollen
4. einen Plan über notwendige Objektmerkmale, wie z.B. EAN-Nummer, Gewicht, elektrische Leistung, Besitzer, Pate, etc. etc.
5. ein gutes Schema für Objekte, was möglichst konsequent durchgezogen wird
6. Rollenverteilung: soll der Gastmodus aktiv sein? Wer sind die Nutzer und wer sind die Editoren/Moderatoren der Instanz?
7. schlussendlich eine durchdachte Stichwortstruktur. Diese erlaubt das sinnvolle Einsortieren und Filtern von Objekten. Hier findest Du unsere Tag-Struktur als Beispiel (bitte beachten: die Stichworte sind höchst individuell. **Blankes Copy/Paste wird euch schnell an eure eigenen Verständnis- und Umgangsgrenzen bringen**):

![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/tzaqO9LZoqyV1Ysk-grafik.png) ![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/G8hkvou6dgYU8aJ2-grafik.png)![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/8XHwiCQOvOPMxSPC-grafik.png)![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/856MvCfpuPpOmgPR-grafik.png)![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/r34phiQSwd234zF2-grafik.png)![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/ZQSxXn9RwPSJQBh0-grafik.png) ![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/gdXVRckbNcYRR2vs-grafik.png) ![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/jJTxHMhzOiQh4qMO-grafik.png) ![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/Wr0oEC0JnjNnXq8B-grafik.png)![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-02/scaled-1680-/SjNXFWlOvR4k29tF-grafik.png)

## Wie befüllen und pflegen wir das Inventarsystem?

Wir brauchen jede Menge Informationen zu den einzelnen Objekten, die wir allesamt neu anlegen müssen. Je mehr Infos, desto besser. Aber kein Grund zur Sorge: Am Anfang fehlt alles und jede Information ist besser als keine. Alles kann nach und nach angereichert und verbessert (oder auch wieder gelöscht) werden. Für den Anfang ist es gut, wenn man "einfach mal" durch die Werkstattbereiche geht und alle möglichen Objekte fotografiert (zu empfehlen sind Fotos vom gesamten Objekt, vom direkten Zubehör im Umfeld, vom Maschinenmodell (Aufdrucke, Logos), vom Typenschild (Seriennummern, elektrische Kennwerte, Modell-Revision, etc.) und ggf. Defekte und Modifikationen). Am besten vom Groben ins Feine (von groben Werkstattecken bis zur aufgezogenen Werkbankschublade), ggf. im Uhrzeigersinn oder nach einem geeigneten Schema, um auch im Anschluss (digitale Pflege) vorm geistigen Auge durch die Werkstatt zu gehen. Anhand von Fotos erkennt man bereits häufig auch später im DMS ganz schnell das **"Was?"** und das **"Wo?"** der Sachen. Anhand dieser Infos können wir per Online-Suchmaschine schnell die passenden Handbücher und Stücklisten zusammenfinden und ins DMS hochladen, sofern die Maschine noch keine 20 Jahre alt ist (aus einer Zeit stammt, wo PDFs noch nicht gebräuchlich waren). Eigenschaften wie Zustand, Gewicht und Zubehör einzupflegen sind typische Folgeaufgaben einer gewachsenen Inventarinstanz und helfen bei der eigentlichen Optimierung und Vorplanung der Werkstatt. Häufig ist eine Aufgabe auch das Einscannen vorhandener papierischer Handbücher, Quick Intros oder Reparaturscheine, Garantiescheine, Verschrottungsnachweise, whatever).

## Professionalisierung (siehe Konzepte unter [Vom Etikettenschild zum Objekt](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/vom-etikettenschild-zum-objekt "Vom Etikettenschild zum Objekt"))

Für die professionellere Anwendung sollten weitere Dinge beachtet oder ergänzt werden

- in einer idealen Inventarinstanz fließen neu angeschaffte oder entsorgte Geräte direkt als Information in das DMS ein, indem wir neue Objekte anlegen (ggf. nur vorbelegen) oder wieder löschen. Dazu helfen die Abteilungen/Rollen Buchhaltung, Einkauf und Werkstattleitung mit ihrer Kompetenz und Informationsweitergabe aus.
- auf eigener Sub-Domain hosten: eigenen DNS-Server konfiguriert werden - entweder beim Domain-Hoster oder auf dem eigenen Server mit angepasstem Nameserver-Setup
- Background-Auswertung der Datenbank (PostgreSQL) realisieren
- Quellcode von Teedy anpassen und eigene Features hinzufügen/weiterentwickeln
- Werkstattmonitore
- Inventar-Stickern

# Werkstattorientierung im FabLab - Digitales Inventar

<p class="callout info">Unser Inventarsystem: [https://things.fablabchemnitz.de](https://things.fablabchemnitz.de) - (Login notwendig)</p>

## Einleitung / Allgemeines

Das physische Inventar im FabLab ist ein zentraler Bestandteil. Ohne Werkzeuge und Maschinen kann nichts ausprobiert oder hergestellt werden. Ohne Ordnung findet man sich nicht gut zurecht. Insbesondere an einem Ort, wo sich diese Dinge geteilt werden, ist deshalb etwas Organisation notwendig. Umso wichtiger ist neben der Pflege des Inventars (technisch einwandfreier Zustand) die Übersicht zu behalten. Transparenz ist wichtig, um sinnvoll mit den Betriebsmitteln zu wirtschaften. Insbesonders neue Mitglieder im Verein, die sich noch weniger gut auskennen, sind meistens einfach nur überfragt in Sachen Auffinden von Tools. Aber auch altangestammte Mitglieder haben meist zu wenig Einsicht in die verborgenen Schätze unserer Werkstatt. Das soll sich ändern!

"Eigentum verpflichtet" heißt es so schön. Das Inventar einer Werkstatt stellt demnach eine Schlüsselkomponente dar, denn ohne gepflegtes Equipment gibt es häufig Probleme beim Nachvollziehen und Beantworten von Fragen wie ...

- wem gehört das Objekt?
- in welchem Zustand ist das Objekt gerade?
- wer ist Ansprechpartner für das Objekt?
- wo gehört das Objekt eigentlich hin? Stammt es etwa aus einem anderen Werkstattbereich und sollte mal wieder einsortiert werden?
- wo finde ich das Handbuch zum Objekt?
- wann wurde das Objekt zuletzt geprüft? Wurde es überhaupt schon einmal geprüft? Kann ich mich schneller als erwartet daran verletzen? (Stichwort "DGUV-3 Prüfung")
- haben wir so ein Objekt schon im Lab oder sollten wir sowas neu anschaffen?

Diese und weitere Fragen ergeben sich regelmäßig, wenn man mit vielen Werkzeugen und Maschinen zu tun hat und die Werkstatt so richtig nutzen möchte. Die Idee der Inventarverwaltung ist eine alte und begleitet das FabLab schon seit vielen Jahren. Der Grundgedanke ist lange verankert und durchlebte verschiedene digitale Orte, an denen begonnen wurde, dieses Wissen zu sammeln. 2016 wurden die ersten Inventargegenstände in ein zweckentfremdetes Shop-System eingepflegt (Projektname "Farringdon"). Leider war dieses System zu unhandlich und komplex und staubte langsam ein. Währenddessen wuchs der Wunsch nach mehr Komplettierung. In einem Shop-System lassen sich zugehörige Dokumente und Verläufe von Dingen meist weniger gut darstellen (z.B. Repararaturen und Hinweise, Handnotizen, Handbücher, Software, Treiber, usw.). 2018 ging Confluence als Wiki-System online und es pflegten sich in kurzer Zeit Projektdokumentationen und Handbücher von gekauften wie selbstgebauten Maschinen ein. Dadurch gab es einen Split zwischen dem Shop-System und der Wiki. Beide vorhergehenden Lösungen waren nicht ideal, weil es Ihnen an Funktionalitäten fehlte, andere Features dafür sogar zu groß waren. 2020 wurde eine Instanz von [Teedy](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-installation-and-configuration "Teedy Installation and Configuration") für das Inventar online genommen, welches nun Wiki und Shop-System für die Bedürfnisse der Inventarpflege ablöst. Die alten Systeme wurden alle in das neue System integriert und vereinheitlicht. Viele Datenleichen wurden damit entfernt.

# Farbschema für Räume und Etikettenschilder

Im FabLab nutzen wir Farben, um Räume, Bereiche und deren Inventar besser auseinander zu halten. So haben zum Beispiel unsere Schlüssel (Türschlüssel, Werkbankschlüssel, ...) Farbmarkierungen. Auch unser Inventar bekommt Stück für Stück farbige Markierungen. Unsere Inventarschildchen werden mit Filamenten in RAL-Farbe gedruckt. Die folgenden Farben nutzen wir unter anderem:

<table class="fixed-table wrapped confluenceTable tablesorter tablesorter-default stickyTableHeaders" id="bkmrk-" role="grid" style="padding: 0px;"><colgroup><col style="width: 37.0px;"></col><col style="width: 112.0px;"></col><col style="width: 111.0px;"></col><col style="width: 226.0px;"></col><col style="width: 104.0px;"></col><col style="width: 174.0px;"></col><col style="width: 111.0px;"></col></colgroup></table>

<table id="bkmrk-raum-nr.-farbe-als-b" role="grid" style="width: 99.9734%; height: 1380.38px;"><thead><tr style="height: 59.2333px;"><td style="width: 16.689%; height: 59.2333px;">**Raum Nr.**</td><td class="align-center" style="width: 16.689%; height: 59.2333px;">**Artikelnr. Filament (extrudr.com)**</td><td class="align-center" style="width: 16.689%; height: 59.2333px;">**Hex Code regular**</td><td class="align-center" style="width: 16.689%; height: 59.2333px;">**Hex Code websafe**</td><td class="align-center" style="width: 16.689%; height: 59.2333px;">[**RAL Nummer**](https://rgb.to/ral/) (mit RGB-Mapping im Icon)</td><td class="align-center" style="width: 8.31126%; height: 59.2333px;">**[Brother TZe-\* Band](https://www.brother.de/etiketten-und-belegdrucker/schriftbaender)** (12 mm / 24 mm)</td><td style="width: 8.31126%;">[**Sprühfarbe Molotow Premium von Belton**](https://brand.molotow.com/fileadmin/Dateien/PDF/Produktflyer/Spray/Premium/PREMIUM_ProduktFlyer_490x210mm_DE_EN.pdf)</td></tr></thead><tbody><tr style="height: 38.1167px; background-color: rgb(0, 0, 0);"><td style="width: 16.689%; height: 38.1167px;"><span style="color: rgb(236, 240, 241);">A000 Flur</span></td><td class="align-center" style="width: 16.689%; height: 38.1167px;"><span style="color: rgb(236, 240, 241);">[9010241043026](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTg4Mw%3D%3D) (RAL 9017)</span>

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-02/scaled-1680-/PzTlpSyJJ4pRPOhM-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-02/PzTlpSyJJ4pRPOhM-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 38.1167px;"><span style="color: rgb(236, 240, 241);">\#000000</span></td><td class="align-center" style="width: 16.689%; height: 38.1167px;"><span style="color: rgb(236, 240, 241);">\#000000</span></td><td class="align-center" style="width: 16.689%; height: 38.1167px;"><span style="color: rgb(236, 240, 241);">9005 Tiefschwarz</span></td><td class="align-center" style="width: 8.31126%; height: 38.1167px;"><span style="color: rgb(236, 240, 241);">335  
BLACK - WHITE INK - LAMINATED  
</span></td><td class="align-center" style="width: 8.31126%;"><span style="color: rgb(236, 240, 241);">221  
deep black</span></td></tr><tr style="background-color: rgb(102, 153, 51); height: 148.2px;"><td style="width: 16.689%; height: 148.2px; background-color: rgb(102, 153, 51);">A001 Sozial/Empfang</td><td class="align-center" style="width: 16.689%; height: 148.2px; background-color: rgb(102, 153, 51);">[9010241043194](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTg4MA%3D%3D) (RAL 6001)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/KsQI8OivErQP2xkc-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/KsQI8OivErQP2xkc-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 148.2px; background-color: rgb(97, 153, 59);">\#61993b</td><td class="align-center" style="width: 16.689%; height: 148.2px; background-color: rgb(102, 153, 51);">\#669933</td><td class="align-center" style="width: 16.689%; height: 148.2px; background-color: rgb(102, 153, 51);">6018 Gelbgrün  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/ZKblwi7EJFEPdLiw-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/ZKblwi7EJFEPdLiw-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 148.2px; background-color: rgb(102, 153, 51);">MQG35  
LIME GREEN - WHITE INK - LAMINATED  
</td><td class="align-center" style="width: 8.31126%; background-color: rgb(102, 153, 51);">163  
avocado</td></tr><tr style="background-color: rgb(204, 102, 153); height: 145.2px;"><td style="width: 16.689%; height: 145.2px;">A002 Textil/Workshop</td><td class="align-center" style="width: 16.689%; height: 145.2px;">[9010241043217](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTI2OA%3D%3D) (RAL 4008)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/dmuGvCNenbTjm9qH-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/dmuGvCNenbTjm9qH-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(196, 97, 140);">\#c4618c</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(204, 102, 153);">\#cc6699</td><td class="align-center" style="width: 16.689%; height: 145.2px;">4003 Erikaviolett  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/9B64a8RS2OGIBeqL-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/9B64a8RS2OGIBeqL-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 145.2px;">MQP35  
BERRY-PINK WHITE INK - LAMINATED  
  
(nicht als 24 mm Tape käuflich. Wir nutzen Washi-Tape + transparente 24 mm Labels)  
  
TZE-SC24P  
TZE-MQP55  
BPT-TZE-MQP55  
</td><td style="width: 8.31126%;">053

candy

</td></tr><tr style="background-color: rgb(204, 204, 204); height: 145.2px;"><td style="width: 16.689%; height: 145.2px;">A003 Büro/Server</td><td class="align-center" style="width: 16.689%; height: 145.2px;">[9010241043040](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTg4NA%3D%3D) (RAL 7044)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/ZOkYm5kqUJHArVO5-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/ZOkYm5kqUJHArVO5-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(197, 199, 196);">\#c5c7c4</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(204, 204, 204);">\#cccccc</td><td class="align-center" style="width: 16.689%; height: 145.2px;">7035 Lichtgrau  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/9dx4iaA0lxhueUMV-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/9dx4iaA0lxhueUMV-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 145.2px;">MQ531  
PASTEL BLUE - BLACK INK - LAMINATED  
</td><td class="align-center" style="width: 8.31126%;">073  
thistle</td></tr><tr style="height: 145.2px; background-color: rgb(204, 102, 102);"><td style="width: 16.689%; height: 145.2px;">A004 Elektronik/3D-Druck</td><td class="align-center" style="width: 16.689%; height: 145.2px;">[9010241043262](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTg3Ng%3D%3D) (RAL 3024)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/Y2xLUKTlSkh45WaC-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/Y2xLUKTlSkh45WaC-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(203, 85, 93);">\#cb555d</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(204, 102, 102);">\#cc6666</td><td class="align-center" style="width: 16.689%; height: 145.2px;">3017 Rosé  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/H1X9vF4fqhKUeXiL-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/H1X9vF4fqhKUeXiL-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 145.2px;">431 oder 435  
  
</td><td class="align-center" style="width: 8.31126%;">041  
strawberrry red</td></tr><tr style="height: 145.2px; background-color: rgb(102, 204, 204);"><td style="width: 16.689%; height: 145.2px; background-color: rgb(5, 139, 140);">A005 Grafikwerkstatt</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(5, 139, 140);">[9010241043200](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTI2NA%3D%3D) (RAL 5018)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/JG7F2UcmwSY7twJ4-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/JG7F2UcmwSY7twJ4-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(0, 179, 149);">\#00b395</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(0, 204, 153);">\#00cc99</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(5, 139, 140);">5018 Türkisblau [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/AjvZf1zMToZf2Bt8-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/AjvZf1zMToZf2Bt8-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 145.2px; background-color: rgb(5, 139, 140);">731 oder 735  
GREEN - WHITE / BLACK INK - LAMINATED / TURQOISE BLUE</td><td class="align-center" style="width: 8.31126%; background-color: rgb(5, 139, 140);">126  
lagoon blue</td></tr><tr style="height: 145.2px; background-color: rgb(204, 153, 153);"><td style="width: 16.689%; height: 145.2px;">A006 Metallwerkstatt</td><td class="align-center" style="width: 16.689%; height: 145.2px;">n.A.</td><td class="align-center" style="width: 16.689%; height: 145.2px;">\#d8a0a6</td><td class="align-center" style="width: 16.689%; height: 145.2px;">\#cc9999</td><td class="align-center" style="width: 16.689%; height: 145.2px;">3015 Hellrosa  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/yvdUHhwvMbC192PA-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/yvdUHhwvMbC192PA-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 145.2px;">MQE31  
PASTEL PINK - BLACK INK - LAMINATED  
</td><td class="align-center" style="width: 8.31126%;">048  
mauve</td></tr><tr style="height: 145.2px; background-color: rgb(51, 102, 153);"><td style="width: 16.689%; height: 145.2px; background-color: rgb(51, 102, 153);"><span style="color: rgb(236, 240, 241);">A007 Wasserstrahlschneidwerkstatt</span></td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(51, 102, 153);"><span style="color: rgb(236, 240, 241);">[9010241043422](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTQ0OA%3D%3D) (RAL 5013)  
</span>[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/N26CdVFr0SLtsosT-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/N26CdVFr0SLtsosT-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(66, 105, 140);"><span style="color: rgb(236, 240, 241);">\#42698c</span></td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(51, 102, 153);"><span style="color: rgb(236, 240, 241);">\#336699</span></td><td class="align-center" style="width: 16.689%; height: 145.2px; background-color: rgb(51, 102, 153);"><span style="color: rgb(236, 240, 241);">5023 Fernblau  
</span>[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/MgbuAGTk4Pcczrjn-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/MgbuAGTk4Pcczrjn-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 145.2px; background-color: rgb(51, 102, 153);"><span style="color: rgb(236, 240, 241);">531 oder 535  
BLUE WHITE / BLACK INK - LAMINATED  
</span></td><td class="align-center" style="width: 8.31126%; background-color: rgb(51, 102, 153);"><span style="color: rgb(236, 240, 241);">096  
tulip blue middle</span></td></tr><tr style="height: 166.283px; background-color: rgb(255, 204, 51);"><td style="width: 16.689%; height: 166.283px;">A08 Holzwerkstatt</td><td class="align-center" style="width: 16.689%; height: 166.283px;">[9010241043163](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTI2MA%3D%3D) (RAL 1023)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/vhz0Ssirl322q2Ub-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/vhz0Ssirl322q2Ub-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 166.283px; background-color: rgb(250, 202, 48);">\#faca30</td><td class="align-center" style="width: 16.689%; height: 166.283px; background-color: rgb(255, 204, 51);">\#ffcc33</td><td class="align-center" style="width: 16.689%; height: 166.283px;">1018 Zinkgelb  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/3LIaIyHPbo6Uhrqe-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/3LIaIyHPbo6Uhrqe-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 166.283px;">631  
YELLOW - BLACK INK - LAMINATED  
</td><td class="align-center" style="width: 8.31126%;">002  
zink yellow</td></tr><tr style="height: 59.2333px;"><td style="width: 16.689%; height: 59.2333px; background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);">Messe/Unterwegs/Ausgeliehen</span></td><td class="align-center" style="width: 16.689%; height: 59.2333px; background-color: rgb(255, 255, 255);">[9010241043019](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTg4Nw%3D%3D) (RAL 9003)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/I1L9yExwKuAmxHDR-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/I1L9yExwKuAmxHDR-grafik.png)

  
  
</td><td class="align-center" style="width: 16.689%; height: 59.2333px; background-color: rgb(236, 236, 231);"><span style="color: rgb(0, 0, 0);">\#ecece7</span></td><td class="align-center" style="width: 16.689%; height: 59.2333px; background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);">\#ffffff</span></td><td class="align-center" style="width: 16.689%; height: 59.2333px; background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);">9003 Signalweiß  
</span>[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/nqY5Ck8ox4Kjqkie-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/nqY5Ck8ox4Kjqkie-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 59.2333px; background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);">123  
WHITE - BLACK INK - LAMINATED  
</span></td><td class="align-center" style="width: 8.31126%; background-color: rgb(255, 255, 255);"><span style="color: rgb(0, 0, 0);">231  
signal white</span></td></tr><tr style="height: 38.1167px; background-color: rgb(153, 153, 153);"><td style="width: 16.689%; height: 38.1167px;">Außenbereich</td><td class="align-center" style="width: 16.689%; height: 38.1167px;">[9010241043057](https://extrudr.com/de/de/products/pla-nx2-matt/?variant=UHJvZHVjdFZhcmlhbnQ6MTQ0Nw%3D%3D) (RAL 9023)  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/9xV1r6qSwCyBXrg7-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/9xV1r6qSwCyBXrg7-grafik.png)

</td><td class="align-center" style="width: 16.689%; height: 38.1167px; background-color: rgb(155, 155, 155);">\#9b9b9b</td><td class="align-center" style="width: 16.689%; height: 38.1167px; background-color: rgb(153, 153, 153);">\#999999</td><td class="align-center" style="width: 16.689%; height: 38.1167px;">7004 Signalgrau  
[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/scaled-1680-/cYbW5Ufy6h3Rwy4t-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2026-01/cYbW5Ufy6h3Rwy4t-grafik.png)

</td><td class="align-center" style="width: 8.31126%; height: 38.1167px;">MQF31  
PASTEL PURPLE - BLACK INK - LAMINATED  
</td><td class="align-center" style="width: 8.31126%;">226  
grey blue middle</td></tr></tbody></table>

**Hilfreiche Tools**

- [https://www.ralfarbpalette.de/ral-classic](https://www.ralfarbpalette.de/ral-classic)
- [https://rgb.to/ral](https://rgb.to/ral)

Für andere Zwecke reservierte Farben (stehen nicht zur Verfügung bzw. sollten beachtet werden):

- Rettungswege (grün)
- Feuerlöscher, Gefahrenhinweise (rot)
- Arbeitsschutz (blau)
- FabAccess (türkis)
- Warnschilder (gelb)
- Gitsymbole (orange)

# Raumübersicht mit Farbschemazuweisung

Wir verwenden außerdem **\#FF9B00 Melonengelb** <span class="confluence-embedded-file-wrapper confluence-embedded-manual-size">![](https://old.stadtfabrikanten.org/download/thumbnails/78807140/image2021-2-8_14-12-7.png?version=1&modificationDate=1612790784267&api=v2)</span> für Objekte, die beweglich sind bzw. deren Ort nicht festgeschrieben ist (Dinge im ständigen Umlauf oder extern unterwegs).

## Die Vorteile digitaler Inventarverwaltung

- unser eigenes Inventar bereichsmäßig gut und schnell durchsuchbar machen (nach verschiedenen Aspekten wie Tätigkeiten, Bereichen, Ansprechpartnern, Zustand, etc.)
- Konservieren von Dokumentation (Handbücher, Gebrauchsanweisungen, Stückliste, etc.) zu Maschinen und Werkzeugen. Dokumentationen verschwinden mit der Zeit im Internet und sind dann verloren. Insbesondere für ältere Maschinen ist es zunehmend schwer Material zu finden. Außerdem verschwinden auch ausgedruckte Handbücher schnell mal im Abfall. Deshalb ist eine zentrale Speicherquelle bzw. Archivierung praktisch, um die Informationen jederzeit reproduzieren oder zumindest auffinden zu können.
- zentralen Überblick verschaffen → Filtern nach Kriterien wie ausführbaren Tätigkeiten, Ansprechpartnern, Bereichen, Zuständen, etc.
- Ausdrucken von automatisch generierten Inventaraufklebern, welche per Barcode scanbar sind. Ziel ist dabei das direkte Anzeigen des Objekts auf dem Zielgerät (z.B. Smartphone, Tablet)
- Teedy basiert auf einer PostgreSQL-Datenbank, welche mit Hilfe von Grafana leicht angezapft und visualisiert werden kann. Verschiedene Dashboards können so einfach auf Tablets oder in Confluence-Dokumentationen eingebunden werden. So können z.B. DGUV-3 Prüflisten ausgeleitet werden. Siehe auch [Prüfung ortsveränderlicher und ortsfester elektrischer Geräte (DGUV-3)](https://wiki.stadtfabrikanten.org/books/prozesse/page/prufung-ortsveranderlicher-und-ortsfester-elektrischer-gerate-dguv-3 "Prüfung ortsveränderlicher und ortsfester elektrischer Geräte (DGUV-3)")

## Die Nachteile digitaler Inventarverwaltung

- der große initiale, sowie fortlaufende Aufwand in der Pflege der Datensätze (Handbücher, Fotos, Hersteller, Verschlagwortung, sonstige Eigenschaften wie Gewicht oder EAN)
- das schnelle Veralten von Informationen (Objekt hat Ort gewechselt; Objekt wurde wieder verkauft; o.ä.) bzw. Synchronisation der virtuellen Verwaltung und der physischen Werkstatt (digitale Informationen stimmemn nicht überein)
- Objekte, die mehrfach vorkommen, aber gleiche technische Eigenschaften haben: Kopieraufwand der Tags und Metadaten zum Synchronhalten

Zur Kompensation dieser Nachteile müssen möglichst viele Werkstattverantwortliche mitwirken, um den Bestand aktuell zu halten.

## Abgrenzung festes Inventar zu Verbrauchsmittel

Eine Werkstatt ist lebendig. Dinge kommen und gehen. Was in der Regel bleibt sind die festen Infrastrukturen, also Werkzeuge und Maschinen, sowie die Möbel, die sie tragen oder aufbewahren. Im Gegensatz dazu wandern flüchtige Verbrauchsmaterialien wie Schrauben, Holzplatten, Kleber, Öle und Co. durch die Räume und wechseln hin und her. Deshalb pflegen wir Verbrauchsmittel konsequent nicht im Inventarsystem Teedy, sondern ein einer simplen In-Out-Liste (siehe [Verbrauchsmaterialübersicht](https://wiki.stadtfabrikanten.org/books/allgemeine-informationen/page/verbrauchsmaterialubersicht "Verbrauchsmaterialübersicht")).

## Teedy als Inventar-System

Das FabLab Chemnitz betreibt ein protypisches System zur Verwaltung der physischen Werkstattbereiche und deren Inventargegenstände. Dazu nutzen wir das Dokumentenmanagementsystem[ Teedy](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-installation-and-configuration "Teedy Installation and Configuration"), sowie ein paar daran angebundene Peripheriebausteine (Grafana-Monitoring Dashboards, Etikettenaufkleber-Generator für InkScape, 3D-gedruckte Etikettenschilder, usw.). Mit Teedy haben wir ein übersichtliches, aber sehr funktionales Werkzeug mit schneller und intuitiver Suchfunktion, welches blitzschnell reagiert und via API und Datenbank-Anbindung gut automatisierbar ist. In Teedy wird jedes Objekt (das Zeug, die Sache, das Ding, das Werkzeug, die Maschine) als sogenanntes "Dokument" angelegt. Diese Dokumente werden angereichert durch Stichwörter, Metadaten, Beschreibungstexte und Dateien.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/3StHbMuEZNzfuL6h-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/3StHbMuEZNzfuL6h-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/o9b06mEQYEsW4a57-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/o9b06mEQYEsW4a57-grafik.png) [![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/AMYoRqbSpLmodA7b-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/AMYoRqbSpLmodA7b-grafik.png)

<div class="table-wrap" id="bkmrk--1"></div>
*Ein paar Screenshots von der Oberfläche*

## Allgemeine Dokumentation zu Teedy

Wir nutzen Teedy nicht nur selbst, sondern haben auch jede Menge Dokumentation zur Server-Installation, Konfiguration und Nutzung dazu geschrieben. Das ist sozusagen nochmal ein extra Projekt.

[<span class="rwui_content rwui_has_icon  rwui_has_icon_right ">Teedy Documentation Space</span>](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/teedy-installation-and-configuration "Teedy Documentation Space")

## Allgemeine Objektstruktur, Stichworte (Tags) und Berechtigungskonzept

Die virtuelle Werkstattpflege macht das Inventar leicht filterbar. Allerdings gehört dazu ein gewisses Grundverstädnis dazu, um zu wissen, wie man filtern kann. Dazu ist es hilfreich ein paar begriffliche Definitionen auseinander zu halten und die Struktur von Teedy zu verstehen.

In Teedy werden Objekte ("Dokumente") nicht in klassischen Ordnerstrukturen abgebildet. Die Struktur (Ordnung) der Dinge (Objekte, Tools, things, Zeug, Werkzeug, Maschinen, Automaten, ... - es lassen hundert Synonyme hierfür einsetzen) wird hauptsächlich über die mehrfache Verstichwortung erreicht (tagging). Das sind zum Beispiel Eigenschaften wie "defekt", technische Schnittstellen wie "USB" oder "HDMI" oder Orte, an denen sich das Objekt befinden kann (z.B. "Metallwerkstatt"). Es handelt sich damit um das Zuweisen von Merkmalen, Attributen, Eigenschaften, die ein Objekt genauer deklarieren. Mit Hilfe der Stichworte lässt sich so recht einfach ein defekter Monitor mit HDMI- und USB-Anschluss, in der Metallwerkstatt steht, filtern.

Zur Pflege, Durchsuchbarkeit und Vergleichbarkeit nutzen wir die folgenden Stammdaten. Ein grundlegender Objektaufbau (Dokumenteneigenschaften) bildet das Skelett. Falls ein Objekt zwei mal in der Werkstatt vorkommt, wird es in Teedy zweimal angelegt, denn jedes Objekt hat seine eigene Historie (z.B. Reparaturen, Modifikationen, ...)

## Dokumententitel

Wir nutzen folgendes Schema: `<Klarname + sinnvolle Eigenschaften (optional Art.No.)> <Modellnummer> von <Hersteller/Marke> #<eindeutige Unterscheidungsnummer bei mehrfach vorhandenen Exemplaren gleichen Typs>| "<Spitzname/Kosename (z.B. Hildegard, Erika, ...)>"`

Beispiele:

- "Laser-Entfernungsmesser DLE 40 von Bosch"
- "Infrarot Heizpanel KH E-600 WS von Könighaus GmbH #2"

## Erstellungsdatum  


Das Erstellungsdatum ist das Datum, das der Nutzer im Dokument aktiv selbst festlegt. Das Erstellungsdatum entspricht nicht dem Erzeugungsdatum des Dokuments (Initiales Anlegen des Dokuments) - dieses wird automatisch vergeben und spielt keine wichtige Rolle. Das niedrigste Datum in Teedy kann der 01.01.0001 sein. Das Erstellungsdatum kann viele verschiedene Informationen transportieren und ist als solches nicht besonders aussagekräftig. Wir nutzen es schlichtweg als das Datum, wann der Gegenstand ins System eingefügt wurde. Es entspricht **nicht** dem Datum, seit wann der Gegenstand im Verein geführt wird. Letzteres kann nur eindeutig durch Kaufbelege (Rechnungsdatum, Bezahldatum, Lieferdatum, ...) und Leihverträge definiert werden. Diese Daten sind jedoch eher nur für die Buchhaltung und Abschreibung relevant oder aber um zu wissen welche Dinge kürzlich neu in den Verein gelangt sind.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/8r0RJtabBMBwkmZD-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/8r0RJtabBMBwkmZD-grafik.png)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/RsXvL7LrCysfatjY-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/RsXvL7LrCysfatjY-grafik.png)

*Das Erstellungsdatum wird in der Dokumentenansicht wird neben dem Dokumententitel angezeigt. Es ist außerdem auch in der linken Dokumentenliste sichtbar.*

<div class="table-wrap" id="bkmrk--4"></div>## Volltext/Beschreibung  


Jedes Dokument sollte nach Möglichkeit eine gute Beschreibung haben

- Modifikationen (Reparaturen, Pimps)
- Details (z.B. Nutzungshinweise, Energieverbrauch, Handling, Besonderheiten, Leistung, etc.)
- Sonstige Sicherheitshinweise, die durch Merkmale nicht abgedeckt werden
- Links zu ggf. vorhandener Maschinenvorstellungen und Projektvorstellungen
- Links zu externen Quellen (z.B. Hersteller, Quelldateien, etc.)
- ggf. Link zum Wiki, falls es eine ausgiebigere Dokumentation/Tutorials bedarf

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/s098NQaS8DQFWFMg-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/s098NQaS8DQFWFMg-grafik.png)

<div class="table-wrap" id="bkmrk--6"></div><div class="table-wrap" id="bkmrk-die-beschreibung-wir">*Die Beschreibung wird im Reiter "Inhalt" eingefügt*</div>## Kommentare

Kommentare von Nutzern helfen beim Verstehen der Objekthistorie. So lässt sich hier Aktuelles wie Defekte, Veränderungen, Fragen, etc. niederschreiben

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/o7lP2wHn3Xc8Ix5N-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/o7lP2wHn3Xc8Ix5N-grafik.png)

*Kommentare werden am rechten Rand eingeblendet*

## Dateien

- Bilder (Details, Defekte, Reparaturen, Anpassungen ...)
- Skizzen
- Handbücher
- Stücklisten
- ...

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/QeOCLejv0IXAFLjF-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/QeOCLejv0IXAFLjF-grafik.png)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/0W3LK4UnJM7bkIJ2-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/0W3LK4UnJM7bkIJ2-grafik.png)

*Dateien können als Galerie oder Liste angezeigt werden*

<div class="table-wrap" id="bkmrk--10"></div>## Beziehungen (Links/Relationen)

In jedem Dokument können Links zu zu anderen Dokumenten gesetzt werden. Dadurch sind diese untereinander verknüpft und können einfach angeklickt werden. Damit lässt sich direktes Zubehör (z.B. Boxen/Behälter für das eigentliche Werkzeug, zusätzliche Halterungen und Adapter, etc. - z.B. Winkelaufsatz für Festool Akkuschrauber in Systainer-Box) einfach verlinken.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/jEXkYauO4fu5PyyK-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/jEXkYauO4fu5PyyK-grafik.png)

<div class="table-wrap" id="bkmrk--12"></div><div class="table-wrap" id="bkmrk-beziehungen-werden-i">*Beziehungen werden im Dokument direkt unterhalb der "Mitwirkenden" angezeigt*</div>## Stichworte (Tags)

Das schwierige beim Inbetriebnehmen einer Inventarlogik in das Teedy ist das Finden geeigneter Stichworte, um schlüssige Beschreibungen auf den ersten Blick zu erzeugen. Diese sollte eindeutig sein (minimierte Redundanzen) und genügend Unterscheidungsspielraum bieten. Tags sind das Herzstück der Instanz und machen das System intelligent durchsuchbar. Die Übersicht aller Tags in unserer Teedy-Instanz kann gefunden werden unter [https://things.fablabchemnitz.de/#/tag](https://things.fablabchemnitz.de/#/tag) (als eingeloggtes Mitglied ist diese Baumstruktur recht groß und detailreich. Als Gast sieht man hier nur den "**public**" Tag). Tags können unter anderen Tags geschachtelt sein und ergeben somit logische Cluster, wenn man sie auf einer Mind Map grafisch darstellte.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/icNt1oyjJvl5wpSA-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/icNt1oyjJvl5wpSA-grafik.png)

<div class="table-wrap" id="bkmrk--14"></div><div class="table-wrap" id="bkmrk-so-sehen-tags-in-tee">*So sehen Tags in Teedy aus.*</div>### Grundbegriffe wie Maschine, Werkzeug, Automat und das Internet of Things (IoT)

Die folgenden Begriffe sind für die Filterung in Teedy nur bedingt von Bedeutung, helfen jedoch beim Verständnis ebenso.

Der Begriff [ **Maschine**](https://de.wikipedia.org/wiki/Maschine) ist relativ umstritten und kann vielschichtig aufgefasst werden. Eine valide Definition kann zum Beispiel sein: "Eine Maschine ist ein technisches Gebilde mit durch ein Antriebssystem bewegten Teilen". (Wikipedia)

Der Begriff  **[Werkzeug](https://de.wikipedia.org/wiki/Werkzeug)** wird beschrieben als "Ein Werkzeug ist ein nicht zum Körper eines lebenden oder künstlichen Organismus gehörendes Objekt, mit dessen Hilfe die Funktionen des Körpers erweitert werden, um auf diese Weise ein unmittelbares Ziel zu erreichen.". (Wikipedia)

Eine  **[Werkzeugmaschine](https://de.wikipedia.org/wiki/Werkzeugmaschine)**  ist eine Maschine zur Fertigung von Werkstücken mit Werkzeugen, deren Bewegung zueinander durch die Maschine vorgegeben wird. Zu den wichtigsten Vertretern zählen Dreh- und Fräsmaschinen, Erodiermaschinen sowie mechanische Pressen und Maschinenhämmer zum Schmieden. (Wikipedia)

Demnach könnte man schlussfolgern, dass eine **Handmaschine** eine Maschine zur Fertigung von Werkstücken mit Werkzeugen ist, deren Bewegung zueinander durch die menschliche Hand vorgegeben wird und mit Muskelkraft bedient werden. Das können zum Beispiel sein: Akkuschrauber, Bohrmaschine, Tauchsäge, uvm.

Unter dem Begriff [ **Handwerkzeug**](https://de.wikipedia.org/wiki/Kategorie:Handwerkzeug) versteht man alle Werkzeuge, die mit der Hand und mit Muskelkraft bedient werden. Hierzu zählen unter anderem Hämmer, Schraubendreher, Zangen, Feilen, Schaufeln, Äxte, Messer und Scheren. (Wikipedia)

Ein  **[Automat](https://de.wikipedia.org/wiki/Automat)**  ist eine Maschine, die vorbestimmte Abläufe selbsttätig („automatisch“) ausführt. (Wikipedia) Ein Automat kann eine Werkzeugmaschine sein.

Weitere Begriffe, die man definieren kann, sind u.a. **Sondermaschine**, **Kraftmaschine**, **Arbeitsmaschine**, **Anlage**, **Prothese**, **Motor**, **Instrument**, **Apparat**, **Gerät**, und viele mehr (sowie deren Unterbegriffe). Sie stammen historisch betrachtet stark aus dem Bereich Maschinenbau und der Durchindustrialisierung. Bei den Begriffen spalten sich teilweise die Geister über die genaue Definition. Und je genauer man definiert, desto schwieriger wird eine Einordnung der heutigen Dinge, die in unserer Gesellschaft Platz und Anwendung finden. Prinzipiell gibt es Maschinen und Werkzeuge, die ohne Interaktion etwas tun und Geräte, mit denen man manuell etwas tut - mit oder ohne Energie-, Stoff- oder Informationsstransport. In der Regel hat man als Benutzer der Werkstatt früher oder später mit den meisten Geräten die eine oder andere aktive oder passive Form der Interaktion bzw. Informationsvermittlung.

Wie beschreibt man heutzutage beispielsweise ein Smartphone mit all seinen Zwecken, Sensoren, Aktoren, Apps und Möglichkeiten? Wie valide sind diese Definitionen auf die heutige Elektronik im Allgemeinen? Müssen neue Begriffe gefunden werden oder gibt es diese schon? Wäre dies sinnvoll? Wie beschreibt man Dinge, die aus den obigen Begriffskategorien mehreren Beschreibungen gerecht werden? Denn ein Smartphone ist längst mehr als ein Telefon, oder eine Kamera, ein Navigator, ein Buch, ein Sprachassistent, ein Gesundheitsmonitor. Im Prinzip kann ein Smartphone zum Beispiel als eine Kombination aus mehreren Werkzeugen oder Geräten verstanden werden, also ein **Instrumenten-, Geräte und Apparateverbund** oder einer "**Mikro[anlage](https://de.wikipedia.org/wiki/Anlage_(Technik))** " (räumlich betrachtet im kompakten Sinne). Dieser Verbund wird heutzutage zumeist als **Internet of Things (IoT)** verstanden und zusammengefasst.

### Tätitgkeiten, Merkmale und Nutzersichtweisen

Die Tätigkeiten und deren maschinelle Umsetzung sind Grundlage für obige Definitionen Maschine, Werkzeug und Co., spielen aber für den umsetzenden Zweck eine untergeordnete Rolle. Es ist viel einfacher und zweckmäßiger, wenn man nach der Tätigkeit sucht, als einer klassischen Unterteilung zu folgen und sich zu fragen, ob ein Gerät nun eine Werkzeugmaschine, ein Handwerkzeug oder ein Automat ist. Denn durch die feingranulierten Unterscheidungen dieser Gruppierungen kann man den Nutzern allgemein unterstellen, dass diese Unterschiede in der Regel viel zu wenig bekannt sind, um diese allgemeingültig als Filtermerkmale in der Software sinnvoll zu nutzen. Deshalb ist der Praxisbezug wichtiger. Tätigkeiten haben in der Regel etwas mit "Machen" oder "Verrichten" zu tun. Hier geht es um zahlreiche solcher Verben wie "Schneiden", "Bügeln", "Erwärmen", "Beleuchten" und so weiter. Als Nutzer einer Werkstatt versucht man in der Regel ein Problem zu lösen und fragt sich zweckmäßigerweise "wie kann ich ein Loch in dieses Stück Holz **bohren**?", "wie kann ich die Teile **kleben**?", "womit kann ich es am Tisch **befestigen**?", etc.

In aller Regel fragt man sich, ob die Maschine das könnte, was gewünscht ist und ob die Maschine für den Zweck vernünftig nutzbar ist. Dazu prüfen wir als nächstes Befindlichkeiten wie Gewicht, Größe und Strombedarf der Sache. Kann ich die Sache in diesem Werkstattbereich sinnvoll nutzen? Wo kann ich sie anschließen? Wie lange hält der Akku? Welches Zubehör passt dazu? Ist mir das Teil zu schwer und kann ich damit lange stehen? Wer ist dafür eigentlich Ansprechpartner? Es gibt zwei wesentliche Sichtweisen in der gelebten Praxis einer Werkstatt bezogen auf das Finden von Ansprechpartnern:

- Das System filtern und checken, welches Mitglied für welche Objekte Ansprechpartner ist (Suche nach Mitgliedern)
- Das System filtern und checken, welchem Objekt welche Ansprechpartner zugewiesen sind (Suche nach Objekten)

Wahrscheinlicher und sinnvoller ist die letzte Sicht, denn in jeder Regel möchte man für eine konkrete Anwendung den passenden Ansprechpartner finden. In der Praxis ist es relevant zu filtern, ob ein Mitglied für eine bestimmte Maschine eine entsprechende Berechtigung besitzt. Zu schauen, was das Mitglied sonst noch alles darf ist genau genommen für den Nutzer irrelevant bzw. sollte eher für interne Qualifikationszwecke verstanden werden. An der Stelle, wo Personennamen auftauchen, geht es letztlich um das Thema Datenschutz.

In unserer Teedy-Instanz lassen sich Objekte einfach finden, wenn man direkt nach der Tätigkeit sucht. Ein Link als Beispiel, um alle bohrenden Tools im FabLab zu finden: [https://things.fablabchemnitz.de/#/document/search/tag:Bohren](https://things.fablabchemnitz.de/#/document/search/tag:Bohren)

#### Noch mehr Tags? Ja oder nein?

Je mehr Tags eingepflegt werden, desto genauer lässt sich filtern. Allerdings wird das Pflegen <u>nicht unbedingt</u> leichter. Es ließen sich noch viele weitere Eigenschaften in das DMS einpflegen, zum Beispiel:

- Gefahrensymbole/Gefahrenkennzeichen (aus dem Handbuch entnehmbar) - idealerweise müsste man hierzu Tags noch Icons zuweisen können
- Tags für verarbeitbare Halbzeuge wie Kunststoff, Holz, Metall, Glas, Keramik, Textilien, Pappe, Papier, Karton → die Liste wird unendlich lang und damit fast unnutzbar, wenn Objekte damit richtig durchgetaggt würden
- Tag für das Markieren, ob das Objekt bereits gelabelt/beschildert wurde → würde helfen bei der Erstbeschildung des gesamten Equipments, wird danach jedoch nicht benötigt und erfordert einen hohen Synchronisierungsauwand zwischen reeller Beschilderung und dem Datenbestand

### Zugriffe / Sichtbarkeit - Steuerung durch Tags, Benutzer und Gruppen (Benutzerrollen)

Das Inventar soll für verschiedene Nutzerkreise bzw. Szenarien unterschiedlich granular sichtbar sein. Deshalb gibt es mehrere Login-Stufen bzw. Nutzerrollen. Das trifft auch auf die Grafana-Dashboards zu, die weitere Informationen unseres Inventars in anderen Zusammenhängen anzeigen. In Teedy wird die Sichtbarkeit der Objekte mit den Tags festgelegt. Für jedes Stichwort werden Sichtbarkeiten für Nutzer und Gruppen festgelegt. Die Sichtbarkeit hängt also direkt an den Tags:

- Gastlogin für Besucher: Mit diesem Login sieht man die öffentlich gelistetenen Schmankerl unserer Werkstatt, die wir preisgeben wollen. Diese sind verstichwortet mit dem Tag "**public**"
- Allgemeiner FabLab-Nutzer: zur Standardansicht (Tags "**Aktueller\_Zustand**", "**Betriebsmittelart**", "**Diverse\_Attribute**", "**Kategorie**", "**Nutzungsberechtigung**", "**Tätigkeiten**", "**Werkstattbereich**")
- Erweiterte FabLab-Betrachter: damit eingeloggt sehen wir granulare Details, die für den Normalnutzer eher nicht so wichtig sind und somit zur Unübersicht beitragen würden (Tags "**DGUV\_Prüfintervall**" , "**Medium**", "**Schutzklasse**", "**Datenschnittstellen**"). **Hinweis: [Schutzklasse](https://de.wikipedia.org/wiki/Schutzklasse_(Elektrotechnik)) ungleich [Schutzart](https://de.wikipedia.org/wiki/Schutzart)!**
- Editoren: sind Werkstattverantwortliche oder Dokumentoren, die sich um die Datenpflege kümmern

FabLab Mitglieder können sowohl die allgemeine Ansicht, als auch die erweiterte Ansicht verwenden, um das System zu filtern. Editoren-Rechte bekommen nur Werkstattbereichsverantwortliche und Vorstandsmitglieder. Die Logins können in der [Übersicht unserer Web Services](https://wiki.stadtfabrikanten.org/books/software-und-netzwerk/page/ubersicht-unserer-web-services "Übersicht unserer Web Services") gefunden werden.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/vy8OjPR25N1MSOsz-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/vy8OjPR25N1MSOsz-grafik.png)

<div class="table-wrap" id="bkmrk--16"></div><div class="table-wrap" id="bkmrk-berechtigungen-%2F-sic">*Berechtigungen / Sichtbarkeiten für Nutzer und Gruppen können an den Tags eingestellt werden.*</div>## Metadaten

Metadaten sind benutzerdefinierte Felder, die in jedem bestehenden oder neuen Dokument automatisch als ausfüllbare Felder auftauchen. Damit lassen sich Dokumente besser gleichartig strukturieren. Für das Inventar gibt es ein paar Standard-Metadaten, die durchaus sinnvoll sind. Metadaten sind in Teedy leider noch nicht durchsuchbar! Außerdem kann die Daten jeder sehen, der auch das Dokument sehen kann.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/6ulLjGckeNXTPqoK-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/6ulLjGckeNXTPqoK-grafik.png)

<div class="table-wrap" id="bkmrk--18"></div>Wir nutzen die folgenden Metadatensätze:

- id - **string**
    - die ID für das Mapping zwischen dem Inventar-Aufkleber und [things.fablabchemnitz.de.](http://things.fablabchemnitz.de) Jedes Inventar-Objekt erhält eine einzigartige id. Die id wird manuell beim Anlegen vergeben, könnte jedoch auch automatisch vorgeschlagen werden (per SQL-Script Ausgabe). Weitere Details siehe "Verknüpfung digitale Inventarverwaltung mit reeller Nutzung - vom Etikettenschild zum Objekt (Barcodes scannen)"
- Gewicht (kg) - **float**
- EAN - **string**
- Nennleistung (W) **- float**
    - hier wird der grobe Richtwert für die übliche/durchschnittliche oder maximale Leistungsaufnahme/Stromverbrauch angegeben. Der hier eingetragene Wert stammt entweder vom Datenblatt oder wurde manuell mit dem Messgerät erfasst. Die Werte dienen letztlich und einzig der groben Einordnung des Verbrauchers in gewisse Verbrauchsklassen. Insbesondere mit Nutzungsstatistiken kann damit beispielsweise der jährliche Stromverbrauch oder auch der notwendige physische Stromanschluss pro Werkstattbereich hochgerechnet bzw. validiert werden
- Eigentümer - **string**
    - Eigentümer vs Besitzer - was ist der Unterschied? Ein Besitzer ist derjenige, in dessen Einflussbereich sich die Sache befindet und der deshalb auf sie zugreifen kann. Abgrenzung: Besitz ist eine Tatsache, Eigentum dagegen ist das Recht an einer Sache. Oft hat der Eigentümer seine Sache selbst. Dann ist er zugleich Besitzer.
- Pate/Ansprechpartner - **string**
- Letzte DGUV-3 Prüfung **- date**
- Seriennummer - **string**
- Anschaffungswert (€) Brutto **- float** 
    - das ist u.U. eine sensible Angabe, die für die Buchhaltung relevant ist (Abschreibungen)
- Verfügungsnachweis **- string** 
    - Rechnungsnummer/Beschaffungsnachweis (hier ggf. https-Link zum Buchhalterischen Dokument). Entweder ist der Gegenstand ein ...
    - ... regulärer Kauf vom Verein → dann existiert ein Kaufbeleg im Buchhaltungs-DMS, oder
    - ... Bereitstellung durch Dritte → dann existiert im Buchhaltungs-DMS ein Leihvertrag zur Dauerleihe oder zeitlich beschränkten Leihe
    - ... geschenkter Gegenstand / Fundgegenstand / kostenfreie Überlassung → Im Idealfall gibt es eine Schenkungsurkunde oder einen Verschrottungsnachweis
- CE Kennzeichnung - **boolean**
    - hat das Gerät ein CE Siegel oder anderweitigen Nachweis?
- Handbuchkiste - **boolean**
    - Die Dokumentation des Objekts befindet sich in der für den Raum ausgewiesenen Handbuchkiste

**Ansprechpartner/Paten und Eigentümer - Warum als Metadatensatz und nicht als Tag? Ein paar Gedanken dazu ...**

- Teilweise haben Leute Sachen zusammen gekauft. Tagging geht, aber "müllt" das Objekt mit langen Tag-Namen schnell zu.
- Alternativ ließen sich statt den vollen Personennamen auch nur Mitgliedsnummern als Tags eintragen. Das wäre datenschutztechnisch einfach lösbar.
- Es ist besser, wenn der Eigentümer nicht für jeden direkt filterbar ist. Es spielt auch keine direkte Rolle nach einem Nutzer filtern zu können. Siehe auch "Tätitgkeiten, Merkmale und Nutzersichtweisen"
- Pate/Ansprechpartner könnte auch eine Gruppe/Team sein! Dann müsste hierfür jeweils ein neuer Tag angelegt werde

Nicht eingpflegt, sondern nur im Volltext stehend, sind Angaben wie Außendurchmesser, Innendurchmesser, Höhe, Breite, Länge, Tiefe, Dicke. Diese Angaben sind häufig nicht eindeutig, da man nicht genau weiß, ob sie sich auf die Aufbewahrungsbox des Werkzeugs, das Werkzeug selbst oder ein Teil des Werkzeuges beziehen. Praktisch ist der Aufwand viel zu hoch diese Daten penibel einzugeben. Allerdings ließen sich mit diesen Angaben unter Umständen Dichten, Traglasten und Stapelvolumina ausrechnen.

## Historie

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/jwpkfYPlniXMAAVV-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/jwpkfYPlniXMAAVV-grafik.png)

<div class="table-wrap" id="bkmrk--20"></div><div class="table-wrap" id="bkmrk-aus-allen-%C3%84nderungen">*Aus allen Änderungen an den Objekten ergibt sich eine fortlaufende Historie (Audit). Diese ist auf der Startseite zu finden*</div>## Das Kernstück ist die Suchfunktion

Die Suche ist Teedy ermöglicht das schnelle Auffinden von Objekten. Dazu ist es empfehlenswert sich mit dem Suchsyntax auseinanderzusetzen. Details finden sich unter [Searching and Tags](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/searching-and-tags "Searching and Tags"). Je besser verstanden wird, wie man mit Tags, Wildcards und OCR-Suche umgeht, desto exakter werden die Ergebnisse.

Ein Wunsch-Feature, welches Teedy in Sachen Suchfunktion aufwerten würde (und noch nicht programmiert wurde), wäre die Verschlagwortung mit Synonymen und Übersetzungen. Eine Schachtel könnte man z.B. auch unter Stichworten wie Behälter, Box, Container, Etui, Dose, Kasten, Koffer, Kiste, Satz, Set, Schatulle, Tasche, etc. suchen.

Wer nach "Bohren" sucht, könnte auch nach "drill" oder "Aufbohren" suchen. Im aktuellen Fall findet er dann jedoch nur Ergebnisse für "Bohren", obwohl die Tätigkeit diesselbe ist.

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/h7YtzA3VpdPdZgiM-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/h7YtzA3VpdPdZgiM-grafik.png)

*Aufgeklappte Suchmaske*

<div class="table-wrap" id="bkmrk--22"></div>## Löschen (digitales "Entsorgen") von Objekten

In einer Werkstatt muss auch mal aufgeräumt werden. Vollschrott, abgebrochene Schraubendreher oder ein auseinandergefallener Heißluftföhn müssen entsorgt werden. Entsprechend sollte auch das Inventarsystem aufgeräumt werden. Es gibt zwei Möglichkeiten, wie damit umgegangen werden kann:

- das Objekt taggen mit dem einem geeigneten Stichwort wie "entsorgt" ergänzen und dabei Tags löschen wie Werkstattbereich und Prüfintervall.
- Löschen des Objekts (vergebenene Metadaten wie z.B. das als einzigartig im System vergebene Feld "id" bleiben dabei erhalten)

Wir nutzen die letztere Variante und löschen das Objekt. In Grafana wird dann angezeigt, was und wann es gelöscht wurde. So kann die Historie der Entsorgung noch prinzipiell nachvollzogen werden. Leider gibt es in Teedy noch kein Papierkorb-Feature, um Dokumente darin abzulegen. Das wäre eine gute Alternative für den Fall, dass etwas wiederhergestellt werden muss, um nachträglich Apsekte nachzuvollziehen.

## Gastzugang und spezielle Anpassungen (serverseitiger Code/Scripts)

Verschiedene Anpassungen finden sich unter [Gastzugang und spezielle Anpassungen (serverseitiger Code/Scripts).](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/page/gastzugang-und-spezielle-anpassungen-serverseitiger-codescripts "Gastzugang und spezielle Anpassungen (serverseitiger Code/Scripts)")

## Wie kann ich das Inventarsystem des FabLabs mitgestalten?

Durch Aktivwerden an Dingen wie:

- Dateien besser benennen. Beim Einpflegen wurden tausende Dateien migriert und wünschen sich etwas mehr Ordnung
- Inventar kann um Kommentare, Bilder, Handbücher, Metadaten ergänzt werden
- generell kann jedem Inventargegenstand ein geeigneter Spitzname (einzigartig) vergeben werden. Gegenstände mit Namen fühlen sich besser an.

<p class="callout warning">**Schreibrechte**  
Die Pflege des Systems ist allen Administratoren und Editoren möglich. Hauptsächlich sollen Werkstattbereichsverantwortliche das System betreuen.</p>

<p class="callout warning">**Anonyme Nutzer**  
Daten von anonymen Gästen und allgemein geteilten Accounts werden **automatisch** **durch Server-Scripts bereinigt (gelöscht; Erklärung siehe oben)**. Bitte legt also keine Daten an, wenn ihr kein zugewiesener Editor oder Administrator für die DMS-Instanz seid! Metadaten, Beschreibungen, Share-Links werden entfernt, wenn sie vom falschen Benutzer aus angelegt wurden. Es wäre schade, wenn sich jemand viel Mühe macht, dies aber aus Versehen mit dem falschen Account tut.</p>

<p class="callout warning">**Achtung beim Löschen und Modifizieren von Tags** Wie beschrieben definieren Tags die Sichtbarkeiten und die Suchfunktion. Tags sollten nicht leichtfertig umdefiniert oder gelöscht werden. Umbenennen oder Hinzufügen- wie Löschen von Berechtigungen sollten abgesprochen werden.</p>

## Grafana Dashboards

Die Daten aus Teedy können noch weiter aufbereitet werden und viele Informationen aus der Teedy-Datenbank (PostgreSQL) können in anderen Zusammenhängen dargestellt werden. So gibt es verschiedene Dashboard Views, die zusätzliche Dinge anzeigen, zum Beispiel

- Eine Übersicht über die neuesten Kommentare
- Geräte, die Nutzungsgebühren haben (etwas kosten)
- Objekte, die repariert werden sollten oder unkomplett sind (fehlende Teile)
- Übersicht, welche Maschinen speziellen Nutzungsberechtigungen unterliegen (Führerscheine)
- Gegenstände, die noch nicht vollständig eingepflegt worden sind, z.B. die noch keinen festgelegten Ort haben (jedes Objekt sollte seinen festen Platz haben)
- Anzeige des neuesten Inventars
- Übersicht über die vorhandene Literatur
- DGUV-3 Prüfliste (Liste der Geräte, die elektrisch auf Betriebssicherheit untersucht und protokolliert werden sollten)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/PrA3zZWMg4nWjOpb-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/PrA3zZWMg4nWjOpb-grafik.png)

[![grafik.png](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/scaled-1680-/PE416kMgJ2A5sViM-grafik.png)](https://wiki.stadtfabrikanten.org/uploads/images/gallery/2025-05/PE416kMgJ2A5sViM-grafik.png)

## Verknüpfung digitale Inventarverwaltung mit reeller Nutzung - vom Etikettenschild zum Objekt (Barcodes scannen)

Da jedes Objekt im FabLab seine eigene eindeutige Inventarnummer und zugewiesene Eigenschaften bekommt, ist auch jedes Objekt nachvollziehbar in seiner kompletten Historie und Beschaffenheit. Als physischer Gegenpart zur Software steht deshalb als Aufgabe das Labeln der Objekte mit Inventaraufklebern. Die Gegenstände im FabLab Chemnitz erhalten nach und nach alle einen Inventaraufkleber zur Identifizierung. Ein Etikett dient dazu, das Objekt in Teedy anzuzeigen, indem es per Barcode Scanner gescannt wird. Dieser Barcode enthält eine eindeutige URL, die direkt im Browser aufgerufen wird. Dadurch kann der Nutzer schnell herausfinden, um welches Objekt es sich genau handelt (Fotos, Handbücher, Eigenschaften). Falls eine Sache aus mehreren Objekten zusammengesetzt ist (z.B. Gerät mit Fernbedienung), dann sollte möglichst jedes Einzelobjekt etikettiert werden, um die Zusammengehörigkeit wieder herstellen zu können. Inbesondere, wenn Unordentlichkeit herrscht und die Werkzeuge unsortier in der Werkstatt rumliegen.

**Der Ablauf: Siehe [Vom Etikettenschild zum Objekt](https://wiki.stadtfabrikanten.org/books/inventar-und-handbucher/chapter/vom-etikettenschild-zum-objekt "Vom Etikettenschild zum Objekt")**

![(Info)](https://old.stadtfabrikanten.org/s/-6fzg5h/9012/tu5x00/_/images/icons/emoticons/information.svg) Wir nutzen sichtbare und menschenlesbare QR Codes. Theoretisch und praktisch ist jedoch auch das Labeln/Taggen mit RFID oder NFC Chips möglich! Die gleichen Daten können sehr einfach auf diese Medien bespielt werden.

## Weiterführendes

Viele andere kluge Köpfe arbeiten an ähnlichen Werkstattkonzepten für eine sinnvolle Benutzbarkeit ihrer Werkstätten. Ein interessantes Projekt für Werkstatorganisation ist das ["OpenSource-System für Sauberkeit und Ordnung" osSso](https://ossso.de/).