First production ready product

This commit is contained in:
2026-06-22 15:29:36 +02:00
parent fe0d85db55
commit 51ca73c68f
14 changed files with 709 additions and 5677 deletions
+1
View File
@@ -8,3 +8,4 @@ gaga*
*.log*
*.bak
*.backup
logic/node-red-data/size_by_ownergroup_and_number_of_copies_*
+8 -4
View File
@@ -3,10 +3,12 @@
## 📊 Streamlit Frontend
- [x] Erstes Tabellen-Layout mit Pandas erstellen
- [x] Navigation über die Sidebar implementieren
- [ ] Uebersichtsteil erstellen
- [ ] Bereichsteil erstellen
- [ ] Teil ueber nicht zuweisbare Daten
- [ ] Kosten auf allen Seiten einblenden
- [x] Uebersichtsteil erstellen
- [ ] Umgebungsvariablen in app.py verwenden
- [x] Bereichsteil erstellen
- [x] Teil ueber nicht zuweisbare Daten
- [x] Kosten auf allen Seiten einblenden
- [ ] app.py Code dokumentieren, PEP 8, verschoenern
- [ ] Uebersicht ueber single, double Kopien
- [ ] Benutzerseite, wo er selber Filter erstellen kann, z.B. Anzahl Datasets groesser als ...
- [ ] Diagramm für die Department-Verteilung einbauen (Plotly)
@@ -16,6 +18,8 @@
- [x] Python-Skript auf lokale Datei-Pipes umstellen
- [x] Zweistufige p-Gruppen-Abfrage stabilisieren
- [ ] Cronjob/Intervall in Node-RED für tägliches Update einrichten
- [ ] Fehlerhandling
- [ ] Zeit wird falsch angegeben (Zeit fuer Filenamen mit $now)
## 🐳 Docker & Deployment
- [ ] Docker-Volumes für Produktion prüfen
+132 -18
View File
@@ -1,6 +1,7 @@
import streamlit as st
import pandas as pd
import plotly.express as px
import numpy as np
# Umrechnungsfaktor von Terabytes nach Bytes
TB2B = 1024**4
@@ -10,7 +11,7 @@ st.set_page_config(layout="wide")
# 1. Daten laden (Pfad aus deinem Docker-Setup)
FILE_PATH = "/data/json_cache.json"
PRICE_PER_TB = 8.1 # CHF / TB
@st.cache_data
def load_data():
@@ -27,6 +28,12 @@ def load_data():
df = load_data()
# Initialisierung des Session State für die Bereichs-Analyse
if "department" in df.columns:
depts = sorted(df["department"].unique().tolist())
if 'gewaehltes_dept' not in st.session_state:
st.session_state.gewaehltes_dept = depts[0] if depts else None
# 2. NAVIGATION LINKS (Sidebar)
st.sidebar.title("Navigation")
st.sidebar.markdown("Wählen Sie eine Ansicht:")
@@ -34,7 +41,12 @@ st.sidebar.markdown("Wählen Sie eine Ansicht:")
# Ein Radio-Button verhält sich wie eine klickbare Liste
auswahl = st.sidebar.radio(
label="Ansichten",
options=["Übersicht & Metriken", "Bereichs-Analyse", "Nicht zuweisbare Daten", "Rohdaten"],
options=[
"Übersicht & Metriken",
"Bereichs-Analyse",
"Nicht zuweisbare Daten",
"Rohdaten"
],
label_visibility="collapsed" # Versteckt das Label für eine sauberere Optik
)
@@ -45,38 +57,140 @@ if auswahl == "Übersicht & Metriken":
st.title("Übersicht")
# Beispiel für schnelle Metriken rechts
total_vol = df["size"].sum()
total_packed_vol = df["packedSize"].sum()
col1, col2 = st.columns(2)
col1.metric("Gesamtvolumen (TB)", f'{total_vol/TB2B:.2f}')
col1.metric("Gesamtes packetiertes Volumen (TB)", f'{total_packed_vol/TB2B:.2f}')
col1.metric("Anzahl Archivgruppen", len(df))
col2.metric("Gesamtvolumen (TB)", f'{df["size"].sum()/TB2B:.2f}' if "size" in df else "0.00")
df_department_overview = df.groupby('department')['packedSize'].sum().to_frame()
df_department_overview["Anteil [%]"] = (
100 * df_department_overview["packedSize"] / total_packed_vol
)
df_department_overview['Kosten [CHF]'] = (
(df_department_overview['packedSize'] / TB2B) * PRICE_PER_TB
)
df_department_overview['packedSize'] = df_department_overview['packedSize']/TB2B
# Spalten/Indexnamen nur für die Anzeige anpassen
df_department_overview.index.name = "Bereich"
df_department_overview.rename(
columns={"packedSize": "Totales packetiertes Volumen [TB]"},
inplace=True
)
# st.table ist besser für kleine, statische Tabellen, die den Inhalt voll anzeigen sollen
col2.table(
df_department_overview.style.format({
"Totales packetiertes Volumen [TB]": "{:.4f}",
"Anteil [%]": "{:.2f}",
"Kosten [CHF]": "{:.2f}"
})
)
# col2.metric("Abteilungen", f'{df.groupby('department')['packedSize'].sum().to_frame()}', use_container_width=True)
# Hier könnte eine Grafik hin
if "department" in df:
fig = px.pie(df, names="department", title="Verteilung nach Departments")
st.plotly_chart(fig, use_container_width=True)
fig = px.pie(
df,
names="department",
values="packedSize",
title="Verteilung nach Bereichen"
)
# Legende explizit positionieren, um den Abstand zu verringern
fig.update_layout(legend=dict(x=0.7, y=0.5))
st.plotly_chart(fig)
elif auswahl == "Bereichs-Analyse":
st.title("Bereichs-Analyse")
st.write("Filtern Sie die Daten gezielt nach einzelnen Abteilungen.")
# Ein zusätzlicher Filter, der nur in dieser Ansicht erscheint
if "department" in df:
depts = sorted(df["department"].unique().tolist())
gewaehltes_dept = st.selectbox("Wählen Sie ein Department:", depts)
if "department" in df and depts:
# Finde den Index des gespeicherten Departments
try:
default_index = depts.index(st.session_state.gewaehltes_dept)
except ValueError:
default_index = 0
filtered_df = df[df["department"] == gewaehltes_dept]
st.metric(f"Gruppen in Department {gewaehltes_dept}", len(filtered_df))
st.dataframe(filtered_df, use_container_width=True)
# Erstelle die Selectbox und speichere die neue Auswahl
neue_auswahl = st.selectbox(
"Bereich auswählen:",
depts,
index=default_index
)
st.session_state.gewaehltes_dept = neue_auswahl
filtered_df = df[df["department"] == st.session_state.gewaehltes_dept].copy()
# Werte in der Spalte 'copies' durch Zahlen ersetzen
if "copies" in filtered_df.columns:
conditions = [
filtered_df['copies'].str.startswith('one', na=False),
filtered_df['copies'].str.startswith('two', na=False)
]
choices = [1, 2]
filtered_df['copies'] = np.select(conditions, choices, default=filtered_df['copies'])
# DataFrame nach 'packedSize' absteigend sortieren
if "packedSize" in filtered_df.columns:
filtered_df = filtered_df.sort_values(by="packedSize", ascending=False)
# Spaltennamen für die Anzeige anpassen
rename_mapping = {
"ownerGroup": "Gruppe",
"copies": "Anzahl Kopien",
"size": "unpacketierte Grösse",
"packedSize": "packetierte Grösse",
"beamline": "Beamline",
"department": "Bereich"
}
filtered_df.rename(columns=rename_mapping, inplace=True)
# Neue Spalte 'Kosten' hinzufügen
if "packetierte Grösse" in filtered_df.columns:
filtered_df["Kosten [CHF]"] = (filtered_df["packetierte Grösse"] / TB2B) * PRICE_PER_TB
st.metric(f"Anzahl Gruppen in Department {st.session_state.gewaehltes_dept}", len(filtered_df))
st.dataframe(filtered_df, use_container_width=True, hide_index=True)
elif auswahl == "Nicht zuweisbare Daten":
st.title("Nicht zuweisbare Daten")
# Die Tabelle, die du schon gebaut hast
st.dataframe(df, use_container_width=True)
# Filtere Zeilen, bei denen 'department' nicht in eine Zahl umgewandelt werden kann
no_department_df = df[pd.to_numeric(df['department'], errors='coerce').isna()].copy()
no_department_vol = no_department_df['packedSize'].sum()
st.write(f'Gesamtvolumen der nicht zuweisbaren Daten: {no_department_vol/TB2B:.2f} TB')
st.write(f'Anzahl der nicht zuweisbaren Gruppen: {len(no_department_df)}')
no_department_df = no_department_df.sort_values(by="packedSize", ascending=False)
no_department_df['unpacketierte Grösse [GB]'] = no_department_df['size'] / (TB2B/1024)
no_department_df['packetierte Grösse [GB]'] = no_department_df['packedSize'] / (TB2B/1024)
no_department_df["Kosten [CHF]"] = (no_department_df["packedSize"] / TB2B) * PRICE_PER_TB
del(no_department_df['size'])
del(no_department_df['packedSize'])
del(no_department_df['beamline'])
# Werte in der Spalte 'copies' durch Zahlen ersetzen
if "copies" in no_department_df.columns:
conditions = [
no_department_df['copies'].str.startswith('one', na=False),
no_department_df['copies'].str.startswith('two', na=False)
]
choices = [1, 2]
no_department_df['copies'] = np.select(conditions, choices, default=no_department_df['copies'])
# Spaltennamen für die Anzeige anpassen
rename_mapping = {
"ownerGroup": "Gruppe",
"copies": "Anzahl Kopien",
"department": "Bereich"
}
no_department_df.rename(columns=rename_mapping, inplace=True)
# Index zurücksetzen, damit er nicht als Spalte angezeigt wird
st.table(no_department_df.reset_index(drop=True))
elif auswahl == "Rohdaten":
st.title("Rohdaten")
st.write("Durchsuchen und filtern Sie die vollständige Liste der Gruppen.")
# Die Tabelle, die du schon gebaut hast
st.dataframe(df, use_container_width=True)
st.write("Durchsuchen und filtern Sie die vollständige Liste der Gruppen. Grössenangaben in Bytes.")
# Die Tabelle ohne Index und mit voller Höhe anzeigen
st.dataframe(df, use_container_width=True) #, hide_index=True)
+2
View File
@@ -7,6 +7,7 @@ services:
- ./logic/scripts:/data/scripts
environment:
- TZ=Europe/Zurich
- GROUPINFO_JSON_LNK_NAME=archivegroup_information.json
- GROUPINFO_JSON_LNK_NAME_WITH_PATH=/data/archivegroup_information.json
- NON_SPECIFIED_GROUPS=/data/non_specified_groups.csv
- BEGIN_NAME_GROUPINFO_JSON_FILE=size_by_ownergroup_and_number_of_copies_
@@ -16,6 +17,7 @@ services:
- AD_SEARCH_BASE=DC=d,DC=psi,DC=ch
- AD_USER=${AD_USER}
- AD_PASSWORD=${AD_PASSWORD}
- METABASE_API_KEY=${METABASE_API_KEY}
ports:
- "1880:1880"
@@ -1,430 +0,0 @@
{
"node-red": {
"name": "node-red",
"version": "4.1.8",
"local": false,
"user": false,
"nodes": {
"junction": {
"name": "junction",
"types": [
"junction"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/05-junction.js"
},
"inject": {
"name": "inject",
"types": [
"inject"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/20-inject.js"
},
"debug": {
"name": "debug",
"types": [
"debug"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/21-debug.js"
},
"complete": {
"name": "complete",
"types": [
"complete"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/24-complete.js"
},
"catch": {
"name": "catch",
"types": [
"catch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/25-catch.js"
},
"status": {
"name": "status",
"types": [
"status"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/25-status.js"
},
"link": {
"name": "link",
"types": [
"link in",
"link out",
"link call"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/60-link.js"
},
"comment": {
"name": "comment",
"types": [
"comment"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/90-comment.js"
},
"global-config": {
"name": "global-config",
"types": [
"global-config"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/91-global-config.js"
},
"unknown": {
"name": "unknown",
"types": [
"unknown"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/common/98-unknown.js"
},
"function": {
"name": "function",
"types": [
"function"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/10-function.js"
},
"switch": {
"name": "switch",
"types": [
"switch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/10-switch.js"
},
"change": {
"name": "change",
"types": [
"change"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/15-change.js"
},
"range": {
"name": "range",
"types": [
"range"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/16-range.js"
},
"template": {
"name": "template",
"types": [
"template"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/80-template.js"
},
"delay": {
"name": "delay",
"types": [
"delay"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/89-delay.js"
},
"trigger": {
"name": "trigger",
"types": [
"trigger"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/89-trigger.js"
},
"exec": {
"name": "exec",
"types": [
"exec"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/90-exec.js"
},
"rbe": {
"name": "rbe",
"types": [
"rbe"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/function/rbe.js"
},
"tls": {
"name": "tls",
"types": [
"tls-config"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/05-tls.js"
},
"httpproxy": {
"name": "httpproxy",
"types": [
"http proxy"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/06-httpproxy.js"
},
"mqtt": {
"name": "mqtt",
"types": [
"mqtt in",
"mqtt out",
"mqtt-broker"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/10-mqtt.js"
},
"httpin": {
"name": "httpin",
"types": [
"http in",
"http response"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/21-httpin.js"
},
"httprequest": {
"name": "httprequest",
"types": [
"http request"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/21-httprequest.js"
},
"websocket": {
"name": "websocket",
"types": [
"websocket in",
"websocket out",
"websocket-listener",
"websocket-client"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/22-websocket.js"
},
"tcpin": {
"name": "tcpin",
"types": [
"tcp in",
"tcp out",
"tcp request"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/31-tcpin.js"
},
"udp": {
"name": "udp",
"types": [
"udp in",
"udp out"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/network/32-udp.js"
},
"CSV": {
"name": "CSV",
"types": [
"csv"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/parsers/70-CSV.js"
},
"HTML": {
"name": "HTML",
"types": [
"html"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/parsers/70-HTML.js"
},
"JSON": {
"name": "JSON",
"types": [
"json"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/parsers/70-JSON.js"
},
"XML": {
"name": "XML",
"types": [
"xml"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/parsers/70-XML.js"
},
"YAML": {
"name": "YAML",
"types": [
"yaml"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/parsers/70-YAML.js"
},
"split": {
"name": "split",
"types": [
"split",
"join"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/sequence/17-split.js"
},
"sort": {
"name": "sort",
"types": [
"sort"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/sequence/18-sort.js"
},
"batch": {
"name": "batch",
"types": [
"batch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/sequence/19-batch.js"
},
"file": {
"name": "file",
"types": [
"file",
"file in"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/storage/10-file.js"
},
"watch": {
"name": "watch",
"types": [
"watch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/src/node-red/node_modules/@node-red/nodes/core/storage/23-watch.js"
}
}
}
}
+54 -3
View File
@@ -6,15 +6,66 @@
"view-store-position": false,
"view-show-grid": true,
"view-snap-grid": true,
"view-grid-size": 20,
"view-grid-size": "20",
"view-node-status": true,
"view-node-info-icon": true,
"view-node-show-label": true,
"view-show-tips": true,
"view-show-tips": false,
"view-show-welcome-tours": true
},
"tours": {
"welcome": "4.1.8"
"welcome": "5.0.0"
},
"dialog": {
"export": {
"pretty": true,
"json-view": true
}
},
"sidebar": {
"state": {
"v": 4,
"tabs": [],
"primary": {
"width": 300,
"top": {
"tabs": [
"info",
"help",
"config",
"context"
],
"height": 142,
"hidden": false,
"active": "info"
},
"bottom": {
"tabs": [
"debug"
],
"hidden": false,
"active": "debug"
}
},
"secondary": {
"width": 210,
"top": {
"tabs": [
"explorer"
],
"height": 302,
"hidden": false,
"active": "explorer"
},
"bottom": {
"tabs": [
"palette"
],
"hidden": false,
"active": "palette"
}
}
}
}
}
}
-464
View File
@@ -1,464 +0,0 @@
[
{
"id": "f6f2187d.f17ca8",
"type": "tab",
"label": "Data Processing",
"disabled": false,
"info": " - Reads the ingested/downloaded data\n - Adds division information to each group\n - Calculates the total archive volume for a division\n - Handles error in a detailed way"
},
{
"id": "2d3b86fbe21570a6",
"type": "tab",
"label": "Data Ingest",
"disabled": false,
"info": " - Downloads the group data from the metabase server in form of a json file\n - Sets a link to this downloaded file for further reference\n - Handles errors that occur during the download process",
"env": []
},
{
"id": "c87ef57b2517bbd5",
"type": "global-config",
"env": [],
"modules": {}
},
{
"id": "3cc11d24.ff01a2",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "WARNING: please check you have started this container with a volume that is mounted to /data\\n otherwise any flow changes are lost when you redeploy or upgrade the container\\n (e.g. upgrade to a more recent node-red docker image).\\n If you are using named volumes you can ignore this warning.\\n Double click or see info side panel to learn how to start Node-RED in Docker to save your work",
"info": "\nTo start docker with a bind mount volume (-v option), for example:\n\n```\ndocker run -it -p 1880:1880 -v /home/user/node_red_data:/data --name mynodered nodered/node-red\n```\n\nwhere `/home/user/node_red_data` is a directory on your host machine where you want to store your flows.\n\nIf you do not do this then you can experiment and redploy flows, but if you restart or upgrade the container the flows will be disconnected and lost. \n\nThey will still exist in a hidden data volume, which can be recovered using standard docker techniques, but that is much more complex than just starting with a named volume as described above.",
"x": 350,
"y": 80,
"wires": []
},
{
"id": "732389b81410a14f",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Inputdefinition",
"info": "# Definition der Inputquellen",
"x": 130,
"y": 260,
"wires": []
},
{
"id": "10a15fe29b7bbd63",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get p-Groupinfos",
"info": "Read p-group information from metabase. \nThis is ownerGroup and copies. You get\nit in json format.",
"x": 340,
"y": 260,
"wires": []
},
{
"id": "6bb0a9c575f23c40",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get a-Groupinfos",
"info": "Read a-group information from metabase. \nThis is ownerGroup and copies. You get\nit in json format.",
"x": 580,
"y": 260,
"wires": []
},
{
"id": "d8e7dea75d135eef",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Total Archived Volume",
"info": "Calculate the total volume of all the \narchived data.",
"x": 160,
"y": 380,
"wires": []
},
{
"id": "44bed35c7d56824d",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get p-Group Division",
"info": "To each p-group get the according division\nwhich is the same as the payment unit.",
"x": 440,
"y": 380,
"wires": []
},
{
"id": "6d8993194484d836",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get a-Group Division",
"info": "To each a-group get the according division\nwhich is the same as the payment unit.",
"x": 720,
"y": 380,
"wires": []
},
{
"id": "339116af234ce323",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Assign Division",
"info": "To each archive group assign the\nright division.",
"x": 160,
"y": 500,
"wires": []
},
{
"id": "b01709df41cdc5a4",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Calculate Division Archivevolume",
"info": "For each division calculate the total\nvolume of the archived data.",
"x": 430,
"y": 500,
"wires": []
},
{
"id": "9e16c8b2a2782747",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Store Result",
"info": "Store the result in a json file by\npreserving previous calculated values.",
"x": 730,
"y": 500,
"wires": []
},
{
"id": "ab458afa33e6630e",
"type": "file in",
"z": "f6f2187d.f17ca8",
"name": "Read archivegroup information JSON file",
"filename": "\"/data/\" & $env(\"GROUPINFO_JSON_LNK_NAME\")",
"filenameType": "jsonata",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"allProps": false,
"x": 780,
"y": 760,
"wires": [
[]
]
},
{
"id": "e06ddee93749c977",
"type": "debug",
"z": "f6f2187d.f17ca8",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 560,
"y": 1100,
"wires": []
},
{
"id": "a918100ba6063585",
"type": "inject",
"z": "f6f2187d.f17ca8",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 540,
"y": 600,
"wires": [
[
"fb9ff82281dc83e0"
]
]
},
{
"id": "e8f1570441f300ee",
"type": "json",
"z": "f6f2187d.f17ca8",
"name": "Convert JSON text to JSON obejct",
"property": "payload",
"action": "",
"pretty": false,
"x": 1280,
"y": 740,
"wires": [
[
"426e46f53063d368"
]
]
},
{
"id": "69cad106adee2e2e",
"type": "function",
"z": "f6f2187d.f17ca8",
"name": "function 2",
"func": "if (Array.isArray(msg.payload)) {\n msg.payload = msg.payload.map(item => {\n return {\n ...item,\n // Wir nutzen 'ownerGroup', weil 'id' in deinen Daten fehlt\n \"tralala\": \"tralala-\" + item.ownerGroup || \"n/a\" \n };\n });\n}\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 340,
"y": 1100,
"wires": [
[
"e06ddee93749c977"
]
]
},
{
"id": "fb9ff82281dc83e0",
"type": "exec",
"z": "f6f2187d.f17ca8",
"command": "python3 /data/scripts/ad_lookup.py",
"addpay": "",
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "",
"x": 960,
"y": 620,
"wires": [
[
"e8f1570441f300ee"
],
[
"444c29de997bd1b6"
],
[]
]
},
{
"id": "426e46f53063d368",
"type": "debug",
"z": "f6f2187d.f17ca8",
"name": "debug 3",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1480,
"y": 840,
"wires": []
},
{
"id": "444c29de997bd1b6",
"type": "debug",
"z": "f6f2187d.f17ca8",
"name": "debug 4",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1480,
"y": 460,
"wires": []
},
{
"id": "ae2dd22abd1a018a",
"type": "inject",
"z": "f6f2187d.f17ca8",
"name": "Inject Node mit dem Text: [{\"ownerGroup\":\"a-35479\"}]",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "[{\"ownerGroup\":\"a-35479\"}]",
"payloadType": "str",
"x": 480,
"y": 720,
"wires": [
[]
]
},
{
"id": "363ce76bec0e6649",
"type": "comment",
"z": "2d3b86fbe21570a6",
"name": "TODO: Download groupinformation",
"info": "- Download the available information for each archive group from the metabase server as json file",
"x": 340,
"y": 280,
"wires": []
},
{
"id": "5c0cb04a9df89849",
"type": "exec",
"z": "2d3b86fbe21570a6",
"command": "cd /data; ln -sf $(ls $BEGIN_NAME_GROUPINFO_JSON_FILE*.json | sort | tail -n 1) $GROUPINFO_JSON_LNK_NAME",
"addpay": "",
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "create link to newest archivegroup information json",
"x": 630,
"y": 400,
"wires": [
[
"639948c846411d05"
],
[
"78de4d0de4d2af52"
],
[
"639948c846411d05"
]
]
},
{
"id": "3a5dd77cc1bc7e89",
"type": "inject",
"z": "2d3b86fbe21570a6",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 240,
"y": 400,
"wires": [
[
"5c0cb04a9df89849"
]
]
},
{
"id": "639948c846411d05",
"type": "debug",
"z": "2d3b86fbe21570a6",
"name": "end of workflow",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1000,
"y": 360,
"wires": []
},
{
"id": "becf6cea4027743f",
"type": "file",
"z": "2d3b86fbe21570a6",
"name": "write log error",
"filename": "\"/data/\" & $env(\"ERROR_LOGFILE\")",
"filenameType": "jsonata",
"appendNewline": false,
"createDir": false,
"overwriteFile": "false",
"encoding": "none",
"x": 840,
"y": 540,
"wires": [
[
"2349d802aa58a575"
]
]
},
{
"id": "78de4d0de4d2af52",
"type": "change",
"z": "2d3b86fbe21570a6",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$now() & \" - ERROR in node: 'create link to newest archivegroup information'\" & \" - message: \" & payload",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 620,
"y": 540,
"wires": [
[
"becf6cea4027743f"
]
]
},
{
"id": "2349d802aa58a575",
"type": "debug",
"z": "2d3b86fbe21570a6",
"name": "data ingest error debug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1090,
"y": 540,
"wires": []
},
{
"id": "02e10d7f4b7f6f40",
"type": "function",
"z": "2d3b86fbe21570a6",
"name": "function 1",
"func": "node.error(\"Test-Alarm!\", msg);\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 640,
"y": 300,
"wires": [
[]
]
}
]
@@ -1 +1 @@
size_by_ownergroup_and_number_of_copies_2026-05-06T09_29_25.230010801Z.json
size_by_ownergroup_and_number_of_copies_2026-06-22T13_25_38.096Z.json
+1
View File
@@ -1,3 +1,4 @@
2026-05-07T15:50:03.349Z - ERROR in node: create link to newest archivegroup informationUnbekannter Node - message: manual test error
2026-05-07T15:51:14.151Z - ERROR in node: create link to newest archivegroup information - message: manual test error
2026-05-07T15:51:48.982Z - ERROR in node: 'create link to newest archivegroup information' - message: manual test error
2026-06-18T09:06:21.303Z - ERROR in node: 'create link to newest archivegroup information' - message: ls: size_by_ownergroup_and_number_of_copies_2026-06-18T09_03_11.261Z.json: Symbolic link loop
+500 -324
View File
@@ -1,17 +1,25 @@
[
{
"id": "5a9dd923b7459820",
"type": "tab",
"label": "Get New Groupinformation",
"disabled": false,
"info": "",
"env": []
},
{
"id": "f6f2187d.f17ca8",
"type": "tab",
"label": "Data Processing",
"label": "Process Groupinformation",
"disabled": false,
"info": " - Reads the ingested/downloaded data\n - Adds division information to each group\n - Calculates the total archive volume for a division\n - Handles error in a detailed way"
},
{
"id": "2d3b86fbe21570a6",
"id": "1593c8c9a04da215",
"type": "tab",
"label": "Data Ingest",
"label": "Run",
"disabled": false,
"info": " - Downloads the group data from the metabase server in form of a json file\n - Sets a link to this downloaded file for further reference\n - Handles errors that occur during the download process",
"info": "",
"env": []
},
{
@@ -20,6 +28,368 @@
"env": [],
"modules": {}
},
{
"id": "eaa3eb4a201883c0",
"type": "inject",
"z": "5a9dd923b7459820",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 120,
"y": 360,
"wires": [
[
"metabase_api_flow"
]
]
},
{
"id": "metabase_api_flow",
"type": "change",
"z": "5a9dd923b7459820",
"name": "Metabase configuration of request",
"rules": [
{
"t": "set",
"p": "method",
"pt": "msg",
"to": "POST",
"tot": "str"
},
{
"t": "set",
"p": "headers",
"pt": "msg",
"to": "{\t \"x-api-key\": $env('METABASE_API_KEY'),\t \"Content-Type\": \"application/x-www-form-urlencoded;charset=UTF-8\",\t \"Accept\": \"application/json\"\t}",
"tot": "jsonata"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "parameters=%5B%5D&format_rows=true&pivot_results=false",
"tot": "str"
},
{
"t": "set",
"p": "requestTimeout",
"pt": "msg",
"to": "300000",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 380,
"y": 360,
"wires": [
[
"metabase_http_node"
]
]
},
{
"id": "metabase_http_node",
"type": "http request",
"z": "5a9dd923b7459820",
"name": "Metabase request",
"method": "use",
"ret": "obj",
"paytoqs": "ignore",
"url": "https://metabase.psi.ch/api/card/94/query/json",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 670,
"y": 360,
"wires": [
[
"b8cc9c157460e2c5"
]
]
},
{
"id": "metabase_output_debug",
"type": "debug",
"z": "5a9dd923b7459820",
"name": "Written File",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 930,
"y": 460,
"wires": []
},
{
"id": "b8cc9c157460e2c5",
"type": "json",
"z": "5a9dd923b7459820",
"name": "Convert JSON object to JSON string",
"property": "payload",
"action": "",
"pretty": false,
"x": 210,
"y": 460,
"wires": [
[
"91e9f1c0e52a0592"
]
]
},
{
"id": "cc064f55fd438347",
"type": "file",
"z": "5a9dd923b7459820",
"name": "Write json to disk",
"filename": "filename",
"filenameType": "msg",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 690,
"y": 460,
"wires": [
[
"metabase_output_debug",
"63ed8c262fa7753b"
]
]
},
{
"id": "91e9f1c0e52a0592",
"type": "change",
"z": "5a9dd923b7459820",
"name": "Set Filename",
"rules": [
{
"t": "set",
"p": "filename",
"pt": "msg",
"to": "\"/data/size_by_ownergroup_and_number_of_copies_\" & $replace($now(), \":\", \"_\") & \".json\"",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 490,
"y": 460,
"wires": [
[
"cc064f55fd438347"
]
]
},
{
"id": "cdbf736ee149ac8f",
"type": "comment",
"z": "5a9dd923b7459820",
"name": "ZWECK: Kontaktiert metabase und laedt eine Liste von JSON Dictionaires herunter,\\n welche Informationen ueber die einzelnen Archivgruppen enthalten. \\n Diese JSON-Struktur wird dann in ein File abgespeichert.",
"info": "",
"x": 350,
"y": 100,
"wires": []
},
{
"id": "63ed8c262fa7753b",
"type": "exec",
"z": "5a9dd923b7459820",
"command": "cd /data/; ln -sf $(ls $BEGIN_NAME_GROUPINFO_JSON_FILE*.json | sort | tail -n 1) $GROUPINFO_JSON_LNK_NAME_WITH_PATH",
"addpay": "",
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "create link to newest archivegroup information json",
"x": 310,
"y": 600,
"wires": [
[
"0957c917da9bc8b3",
"3e09805d0a8e88f9"
],
[
"2d8f3b73dc80bf4a"
],
[
"0957c917da9bc8b3"
]
]
},
{
"id": "0957c917da9bc8b3",
"type": "debug",
"z": "5a9dd923b7459820",
"name": "end of workflow",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 740,
"y": 560,
"wires": []
},
{
"id": "7fa57e2c57d1e6eb",
"type": "file",
"z": "5a9dd923b7459820",
"name": "write log error",
"filename": "\"/data/\" & $env(\"ERROR_LOGFILE\")",
"filenameType": "jsonata",
"appendNewline": false,
"createDir": false,
"overwriteFile": "false",
"encoding": "none",
"x": 580,
"y": 740,
"wires": [
[
"394f5382cb6182dd"
]
]
},
{
"id": "2d8f3b73dc80bf4a",
"type": "change",
"z": "5a9dd923b7459820",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$now() & \" - ERROR in node: 'create link to newest archivegroup information'\" & \" - message: \" & payload",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 360,
"y": 740,
"wires": [
[
"7fa57e2c57d1e6eb"
]
]
},
{
"id": "394f5382cb6182dd",
"type": "debug",
"z": "5a9dd923b7459820",
"name": "data ingest error debug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 830,
"y": 740,
"wires": []
},
{
"id": "913d320bfbe13635",
"type": "link in",
"z": "5a9dd923b7459820",
"name": "link in Get New Groupinformation",
"links": [],
"x": 125,
"y": 280,
"wires": [
[
"metabase_api_flow"
]
]
},
{
"id": "3e09805d0a8e88f9",
"type": "link out",
"z": "5a9dd923b7459820",
"name": "link out Get New Groupinformation",
"mode": "return",
"links": [],
"x": 825,
"y": 640,
"wires": []
},
{
"id": "c62bf4e1cfd12c03",
"type": "status",
"z": "5a9dd923b7459820",
"name": "workflow status",
"scope": [
"metabase_api_flow",
"metabase_http_node",
"metabase_output_debug",
"b8cc9c157460e2c5",
"cc064f55fd438347",
"91e9f1c0e52a0592",
"63ed8c262fa7753b"
],
"x": 720,
"y": 260,
"wires": [
[
"dfd30c3d6f180d27"
]
]
},
{
"id": "dfd30c3d6f180d27",
"type": "debug",
"z": "5a9dd923b7459820",
"name": "debug workflow",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 940,
"y": 260,
"wires": []
},
{
"id": "3cc11d24.ff01a2",
"type": "comment",
@@ -30,112 +400,6 @@
"y": 80,
"wires": []
},
{
"id": "732389b81410a14f",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Inputdefinition",
"info": "# Definition der Inputquellen",
"x": 130,
"y": 260,
"wires": []
},
{
"id": "10a15fe29b7bbd63",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get p-Groupinfos",
"info": "Read p-group information from metabase. \nThis is ownerGroup and copies. You get\nit in json format.",
"x": 340,
"y": 260,
"wires": []
},
{
"id": "6bb0a9c575f23c40",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get a-Groupinfos",
"info": "Read a-group information from metabase. \nThis is ownerGroup and copies. You get\nit in json format.",
"x": 580,
"y": 260,
"wires": []
},
{
"id": "d8e7dea75d135eef",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Total Archived Volume",
"info": "Calculate the total volume of all the \narchived data.",
"x": 160,
"y": 380,
"wires": []
},
{
"id": "44bed35c7d56824d",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get p-Group Division",
"info": "To each p-group get the according division\nwhich is the same as the payment unit.",
"x": 440,
"y": 380,
"wires": []
},
{
"id": "6d8993194484d836",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Get a-Group Division",
"info": "To each a-group get the according division\nwhich is the same as the payment unit.",
"x": 720,
"y": 380,
"wires": []
},
{
"id": "339116af234ce323",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Assign Division",
"info": "To each archive group assign the\nright division.",
"x": 160,
"y": 500,
"wires": []
},
{
"id": "b01709df41cdc5a4",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Calculate Division Archivevolume",
"info": "For each division calculate the total\nvolume of the archived data.",
"x": 430,
"y": 500,
"wires": []
},
{
"id": "9e16c8b2a2782747",
"type": "comment",
"z": "f6f2187d.f17ca8",
"name": "Store Result",
"info": "Store the result in a json file by\npreserving previous calculated values.",
"x": 730,
"y": 500,
"wires": []
},
{
"id": "e06ddee93749c977",
"type": "debug",
"z": "f6f2187d.f17ca8",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 560,
"y": 1100,
"wires": []
},
{
"id": "a918100ba6063585",
"type": "inject",
@@ -157,50 +421,14 @@
"topic": "",
"payload": "",
"payloadType": "date",
"x": 240,
"y": 620,
"x": 120,
"y": 340,
"wires": [
[
"fb9ff82281dc83e0"
]
]
},
{
"id": "e8f1570441f300ee",
"type": "json",
"z": "f6f2187d.f17ca8",
"name": "Convert JSON text to JSON obejct",
"property": "payload",
"action": "",
"pretty": false,
"x": 840,
"y": 880,
"wires": [
[
"8f50bc0cb9009461"
]
]
},
{
"id": "69cad106adee2e2e",
"type": "function",
"z": "f6f2187d.f17ca8",
"name": "function 2",
"func": "if (Array.isArray(msg.payload)) {\n msg.payload = msg.payload.map(item => {\n return {\n ...item,\n // Wir nutzen 'ownerGroup', weil 'id' in deinen Daten fehlt\n \"tralala\": \"tralala-\" + item.ownerGroup || \"n/a\" \n };\n });\n}\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 340,
"y": 1100,
"wires": [
[
"e06ddee93749c977"
]
]
},
{
"id": "fb9ff82281dc83e0",
"type": "exec",
@@ -213,8 +441,8 @@
"winHide": false,
"oldrc": false,
"name": "",
"x": 540,
"y": 620,
"x": 380,
"y": 340,
"wires": [
[
"dd3574493b97a5f4"
@@ -237,8 +465,8 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1240,
"y": 620,
"x": 1020,
"y": 260,
"wires": []
},
{
@@ -253,8 +481,8 @@
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 920,
"y": 700,
"x": 700,
"y": 420,
"wires": []
},
{
@@ -268,38 +496,78 @@
"createDir": false,
"overwriteFile": "true",
"encoding": "utf8",
"x": 970,
"y": 620,
"x": 750,
"y": 320,
"wires": [
[
"426e46f53063d368"
"426e46f53063d368",
"dbc196df1931ee06"
]
]
},
{
"id": "5c695a90a30b61d5",
"type": "file in",
"id": "eb6d443e97fedbee",
"type": "link in",
"z": "f6f2187d.f17ca8",
"name": "Read cached json file",
"filename": "\"/data/results/\" & $env(\"JSON_CACHE\")",
"filenameType": "jsonata",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "utf8",
"allProps": false,
"x": 480,
"y": 880,
"name": "link in Process Groupinformation",
"links": [],
"x": 115,
"y": 260,
"wires": [
[
"e8f1570441f300ee"
"fb9ff82281dc83e0"
]
]
},
{
"id": "6c8e4b6960b242ec",
"id": "dbc196df1931ee06",
"type": "link out",
"z": "f6f2187d.f17ca8",
"name": "link out Process Groupinformation",
"mode": "return",
"links": [],
"x": 1015,
"y": 400,
"wires": []
},
{
"id": "845b63e247001e57",
"type": "status",
"z": "f6f2187d.f17ca8",
"name": "workflow status",
"scope": [
"fb9ff82281dc83e0",
"dd3574493b97a5f4"
],
"x": 640,
"y": 220,
"wires": [
[
"1e2a3c41555c2d9b"
]
]
},
{
"id": "1e2a3c41555c2d9b",
"type": "debug",
"z": "f6f2187d.f17ca8",
"name": "debug workflow",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 880,
"y": 220,
"wires": []
},
{
"id": "44c678d93545a6f5",
"type": "inject",
"z": "f6f2187d.f17ca8",
"z": "1593c8c9a04da215",
"name": "",
"props": [
{
@@ -317,191 +585,99 @@
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 880,
"x": 140,
"y": 280,
"wires": [
[
"5c695a90a30b61d5"
"b994acfe29648a35"
]
]
},
{
"id": "8f50bc0cb9009461",
"id": "b994acfe29648a35",
"type": "link call",
"z": "1593c8c9a04da215",
"name": "",
"links": [
"913d320bfbe13635"
],
"linkType": "static",
"timeout": "300",
"x": 440,
"y": 280,
"wires": [
[
"5dab821150d36d52"
]
]
},
{
"id": "5dab821150d36d52",
"type": "link call",
"z": "1593c8c9a04da215",
"name": "",
"links": [
"eb6d443e97fedbee"
],
"linkType": "static",
"timeout": "300",
"x": 820,
"y": 280,
"wires": [
[
"4d7bd4a079e1c50d"
]
]
},
{
"id": "4d7bd4a079e1c50d",
"type": "debug",
"z": "f6f2187d.f17ca8",
"name": "debug 1",
"z": "1593c8c9a04da215",
"name": "End of workflow",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1120,
"y": 880,
"wires": []
},
{
"id": "363ce76bec0e6649",
"type": "comment",
"z": "2d3b86fbe21570a6",
"name": "TODO: Download groupinformation",
"info": "- Download the available information for each archive group from the metabase server as json file",
"x": 340,
"y": 280,
"wires": []
},
{
"id": "5c0cb04a9df89849",
"type": "exec",
"z": "2d3b86fbe21570a6",
"command": "cd /data; ln -sf $(ls $BEGIN_NAME_GROUPINFO_JSON_FILE*.json | sort | tail -n 1) $GROUPINFO_JSON_LNK_NAME",
"addpay": "",
"append": "",
"useSpawn": "false",
"timer": "",
"winHide": false,
"oldrc": false,
"name": "create link to newest archivegroup information json",
"x": 630,
"y": 400,
"wires": [
[
"639948c846411d05"
],
[
"78de4d0de4d2af52"
],
[
"639948c846411d05"
]
]
},
{
"id": "3a5dd77cc1bc7e89",
"type": "inject",
"z": "2d3b86fbe21570a6",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
"id": "cd1bcf72369d7e25",
"type": "status",
"z": "1593c8c9a04da215",
"name": "workflow status",
"scope": [
"b994acfe29648a35",
"5dab821150d36d52"
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 240,
"x": 420,
"y": 400,
"wires": [
[
"5c0cb04a9df89849"
"a613cbef5aae458b"
]
]
},
{
"id": "639948c846411d05",
"id": "a613cbef5aae458b",
"type": "debug",
"z": "2d3b86fbe21570a6",
"name": "end of workflow",
"z": "1593c8c9a04da215",
"name": "debug workflow",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1000,
"y": 360,
"x": 660,
"y": 400,
"wires": []
},
{
"id": "becf6cea4027743f",
"type": "file",
"z": "2d3b86fbe21570a6",
"name": "write log error",
"filename": "\"/data/\" & $env(\"ERROR_LOGFILE\")",
"filenameType": "jsonata",
"appendNewline": false,
"createDir": false,
"overwriteFile": "false",
"encoding": "none",
"x": 840,
"y": 540,
"wires": [
[
"2349d802aa58a575"
]
]
},
{
"id": "78de4d0de4d2af52",
"type": "change",
"z": "2d3b86fbe21570a6",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$now() & \" - ERROR in node: 'create link to newest archivegroup information'\" & \" - message: \" & payload",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 620,
"y": 540,
"wires": [
[
"becf6cea4027743f"
]
]
},
{
"id": "2349d802aa58a575",
"type": "debug",
"z": "2d3b86fbe21570a6",
"name": "data ingest error debug",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1090,
"y": 540,
"wires": []
},
{
"id": "02e10d7f4b7f6f40",
"type": "function",
"z": "2d3b86fbe21570a6",
"name": "function 1",
"func": "node.error(\"Test-Alarm!\", msg);\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 640,
"y": 300,
"wires": [
[]
]
}
]
+1 -1
View File
@@ -1,3 +1,3 @@
{
"$": "4a8443cf524c38238a091c8c8d93cb3eLnc="
"$": "68c90eb90d1acc6274830943ad479b8eKrbLkbHL0GfF6fVrZHmdbyba8snkuQR/Lg=="
}
File diff suppressed because one or more lines are too long
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
KEY=<MY_METABASE_KEY>
curl -X POST \
-H "x-api-key: $KEY" \
-H "Content-Type: application/json" \
https://metabase.psi.ch/api/card/94/query/json