|
|
|
|
@ -0,0 +1,969 @@
|
|
|
|
|
import json
|
|
|
|
|
import httpx
|
|
|
|
|
import base64
|
|
|
|
|
from datetime import datetime, date
|
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
|
|
|
|
|
|
def make_graphql_request(query: str, variables: dict = None) -> dict:
|
|
|
|
|
"""Führt eine GraphQL-Anfrage aus mit Fehlerbehandlung"""
|
|
|
|
|
try:
|
|
|
|
|
response = httpx.post(
|
|
|
|
|
f"{EXTERNAL_BASE_URL}/graphql",
|
|
|
|
|
json={"query": query, "variables": variables or {}},
|
|
|
|
|
headers=AUTH_HEADERS,
|
|
|
|
|
timeout=30
|
|
|
|
|
)
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
data = response.json()
|
|
|
|
|
|
|
|
|
|
if "errors" in data:
|
|
|
|
|
error_msg = "; ".join([err.get("message", "Unbekannter Fehler") for err in data["errors"]])
|
|
|
|
|
return {"error": f"GraphQL Fehler: {error_msg}"}
|
|
|
|
|
|
|
|
|
|
return data.get("data", {})
|
|
|
|
|
except httpx.TimeoutException:
|
|
|
|
|
return {"error": "Timeout bei der Anfrage"}
|
|
|
|
|
except httpx.HTTPStatusError as e:
|
|
|
|
|
return {"error": f"HTTP Fehler {e.response.status_code}: {e.response.text}"}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return {"error": f"Unerwarteter Fehler: {str(e)}"}
|
|
|
|
|
|
|
|
|
|
def search_sensors(meter_search: str) -> List[Dict]:
|
|
|
|
|
"""Sucht Sensoren basierend auf Zählernummer"""
|
|
|
|
|
query = """
|
|
|
|
|
query SearchSensors($meterNumber: String!) {
|
|
|
|
|
sensorsForMeterNumber(meterNumber: $meterNumber) {
|
|
|
|
|
sensorId
|
|
|
|
|
sensorName
|
|
|
|
|
sensorNameExtern
|
|
|
|
|
descr
|
|
|
|
|
measureConcept {
|
|
|
|
|
id
|
|
|
|
|
name
|
|
|
|
|
descr
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
data = make_graphql_request(query, {"meterNumber": meter_search})
|
|
|
|
|
if "error" in data:
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
return data.get("sensorsForMeterNumber", [])
|
|
|
|
|
|
|
|
|
|
def get_variable_units(sensor_id: str) -> List[Dict]:
|
|
|
|
|
"""Holt verfügbare Variablen und Units für einen Sensor"""
|
|
|
|
|
query = """
|
|
|
|
|
query GetVariableUnits($sensorId: ID!) {
|
|
|
|
|
availableVariableUnits(sensorId: $sensorId) {
|
|
|
|
|
variableUnitId
|
|
|
|
|
variableName
|
|
|
|
|
unitName
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
data = make_graphql_request(query, {"sensorId": sensor_id})
|
|
|
|
|
if "error" in data:
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
return data.get("availableVariableUnits", [])
|
|
|
|
|
|
|
|
|
|
def get_last_observation(sensor_id: str, variable_name: str) -> Optional[Dict]:
|
|
|
|
|
"""Holt den letzten Zählerstand für einen Sensor"""
|
|
|
|
|
query = """
|
|
|
|
|
query GetLastObservation($sensorId: ID!, $variableName: String) {
|
|
|
|
|
lastObservation(sensorId: $sensorId, variableName: $variableName) {
|
|
|
|
|
id
|
|
|
|
|
moment
|
|
|
|
|
meterValue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
data = make_graphql_request(query, {"sensorId": sensor_id, "variableName": variable_name})
|
|
|
|
|
if "error" in data:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
return data.get("lastObservation")
|
|
|
|
|
|
|
|
|
|
def record_single_reading(sensor_id: str, moment: str, value: float, variable_name: str, variable_unit: str) -> Dict:
|
|
|
|
|
"""Erfasst einen einzelnen Zählerstand"""
|
|
|
|
|
mutation = """
|
|
|
|
|
mutation RecordSingleReading($input: MeterReadingInput!) {
|
|
|
|
|
recordMeterReading(input: $input) {
|
|
|
|
|
success
|
|
|
|
|
observation {
|
|
|
|
|
id
|
|
|
|
|
moment
|
|
|
|
|
value
|
|
|
|
|
meterValue
|
|
|
|
|
}
|
|
|
|
|
errors {
|
|
|
|
|
code
|
|
|
|
|
message
|
|
|
|
|
details
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
input_data = {
|
|
|
|
|
"sensorId": sensor_id,
|
|
|
|
|
"moment": moment,
|
|
|
|
|
"value": value,
|
|
|
|
|
"variableName": variable_name,
|
|
|
|
|
"variableUnit": variable_unit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return make_graphql_request(mutation, {"input": input_data})
|
|
|
|
|
|
|
|
|
|
def record_ultimo_readings(sensor_id: str, variable_name: str, variable_unit: str, readings: List[Dict]) -> Dict:
|
|
|
|
|
"""Erfasst mehrere Ultimo-Zählerstände"""
|
|
|
|
|
mutation = """
|
|
|
|
|
mutation RecordUltimoReadings($input: UltimoReadingsInput!) {
|
|
|
|
|
recordUltimoReadings(input: $input) {
|
|
|
|
|
success
|
|
|
|
|
created {
|
|
|
|
|
id
|
|
|
|
|
moment
|
|
|
|
|
value
|
|
|
|
|
meterValue
|
|
|
|
|
}
|
|
|
|
|
errors {
|
|
|
|
|
code
|
|
|
|
|
message
|
|
|
|
|
details
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
input_data = {
|
|
|
|
|
"sensorId": sensor_id,
|
|
|
|
|
"variableName": variable_name,
|
|
|
|
|
"variableUnit": variable_unit,
|
|
|
|
|
"readings": readings
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return make_graphql_request(mutation, {"input": input_data})
|
|
|
|
|
|
|
|
|
|
def generate_month_list(start_year: int, start_month: int, end_year: int, end_month: int) -> List[str]:
|
|
|
|
|
"""Generiert eine Liste von Monaten im Format YYYY-MM"""
|
|
|
|
|
months = []
|
|
|
|
|
current_year = start_year
|
|
|
|
|
current_month = start_month
|
|
|
|
|
|
|
|
|
|
while current_year < end_year or (current_year == end_year and current_month <= end_month):
|
|
|
|
|
months.append(f"{current_year:04d}-{current_month:02d}")
|
|
|
|
|
current_month += 1
|
|
|
|
|
if current_month > 12:
|
|
|
|
|
current_month = 1
|
|
|
|
|
current_year += 1
|
|
|
|
|
|
|
|
|
|
return months
|
|
|
|
|
|
|
|
|
|
# Phase Detection
|
|
|
|
|
if "sensor_id" not in PARAMS:
|
|
|
|
|
# PHASE 1: Sensor Suche
|
|
|
|
|
meter_search = PARAMS.get("meter_search", "")
|
|
|
|
|
|
|
|
|
|
if not meter_search:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "error",
|
|
|
|
|
"message": "Kein Suchbegriff angegeben"
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
sensors = search_sensors(meter_search)
|
|
|
|
|
|
|
|
|
|
if not sensors:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
<div style="background: var(--color-warning); color: white; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">⚠️ Keine Sensoren gefunden</h3>
|
|
|
|
|
<p style="margin: 0;">Für die Suche "{meter_search}" wurden keine Sensoren gefunden.</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; border-left: 4px solid var(--color-primary);">
|
|
|
|
|
<h4 style="margin: 0 0 10px 0;">💡 Suchtipps:</h4>
|
|
|
|
|
<ul style="margin: 0; padding-left: 20px;">
|
|
|
|
|
<li>Verwenden Sie Teilstrings (z.B. "NG9" statt "NG9_40006_0_12")</li>
|
|
|
|
|
<li>Probieren Sie verschiedene Suchbegriffe</li>
|
|
|
|
|
<li>Achten Sie auf die korrekte Schreibweise</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<p style="margin-top: 20px;">
|
|
|
|
|
<a href="javascript:history.back()" style="color: var(--color-primary); text-decoration: none;">← Zurück zur Suche</a>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# Erstelle Sensor-Dropdown-Optionen mit Zusatzinformationen
|
|
|
|
|
sensor_options = []
|
|
|
|
|
for sensor in sensors:
|
|
|
|
|
clean_name = sensor["sensorName"].strip()
|
|
|
|
|
measure_concept = sensor["measureConcept"]
|
|
|
|
|
concept_name = measure_concept["name"].strip() if measure_concept["name"] else "Unbekannt"
|
|
|
|
|
concept_desc = measure_concept.get("descr", "") or ""
|
|
|
|
|
|
|
|
|
|
label = f"{clean_name} ({concept_name}"
|
|
|
|
|
if concept_desc and concept_desc.strip():
|
|
|
|
|
label += f" - {concept_desc.strip()}"
|
|
|
|
|
label += ")"
|
|
|
|
|
|
|
|
|
|
sensor_options.append({
|
|
|
|
|
"value": sensor["sensorId"],
|
|
|
|
|
"label": label
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Sortiere Optionen alphabetisch
|
|
|
|
|
sensor_options.sort(key=lambda x: x["label"])
|
|
|
|
|
|
|
|
|
|
result = {
|
|
|
|
|
"type": "form",
|
|
|
|
|
"form_definition": {
|
|
|
|
|
"title": "Sensor Auswahl und Zählerstand Eingabe",
|
|
|
|
|
"description": f"Gefundene Sensoren für Suche: '{meter_search}'",
|
|
|
|
|
"layout": "sections",
|
|
|
|
|
"sections": [
|
|
|
|
|
{
|
|
|
|
|
"title": "Sensor Auswahl",
|
|
|
|
|
"icon": "sensors",
|
|
|
|
|
"field_names": ["sensor_id", "variable_selection"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"title": "Erfassungsart",
|
|
|
|
|
"icon": "input",
|
|
|
|
|
"field_names": ["input_method"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"title": "Einzelne Erfassung",
|
|
|
|
|
"icon": "schedule",
|
|
|
|
|
"field_names": ["single_datetime", "single_value"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"title": "Ultimo Batch-Erfassung",
|
|
|
|
|
"icon": "batch_prediction",
|
|
|
|
|
"field_names": ["batch_start_year", "batch_start_month", "batch_end_year", "batch_end_month", "batch_values"]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"fields": [
|
|
|
|
|
{
|
|
|
|
|
"name": "sensor_id",
|
|
|
|
|
"widget": "dropdown",
|
|
|
|
|
"label": "Sensor auswählen",
|
|
|
|
|
"options": sensor_options,
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte wählen Sie einen Sensor aus"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "variable_selection",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Variable wird automatisch geladen...",
|
|
|
|
|
"read_only": True,
|
|
|
|
|
"initial_value": "Wählen Sie zuerst einen Sensor aus",
|
|
|
|
|
"helper_text": "Die verfügbaren Variablen werden nach der Sensor-Auswahl angezeigt"
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "input_method",
|
|
|
|
|
"widget": "segmented_control",
|
|
|
|
|
"label": "Erfassungsart",
|
|
|
|
|
"initial_value": "single",
|
|
|
|
|
"options": [
|
|
|
|
|
{"value": "single", "label": "Einzeln"},
|
|
|
|
|
{"value": "batch", "label": "Batch (Ultimo)"}
|
|
|
|
|
],
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte wählen Sie eine Erfassungsart"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "single_datetime",
|
|
|
|
|
"widget": "date_time_picker",
|
|
|
|
|
"label": "Datum und Uhrzeit",
|
|
|
|
|
"date_config": {
|
|
|
|
|
"input_type": "both",
|
|
|
|
|
"format": "dd.MM.yyyy HH:mm"
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "single",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte wählen Sie Datum und Uhrzeit"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "single_value",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Zählerstand",
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"keyboard_type": "number"
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "single",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte geben Sie einen Zählerstand ein"},
|
|
|
|
|
{"type": "numeric", "error_text": "Zählerstand muss eine Zahl sein"},
|
|
|
|
|
{"type": "min", "value": 0, "error_text": "Zählerstand darf nicht negativ sein"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_start_year",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Start Jahr (YYYY)",
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"keyboard_type": "number"
|
|
|
|
|
},
|
|
|
|
|
"initial_value": "2024",
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Jahr ist erforderlich"},
|
|
|
|
|
{"type": "integer", "error_text": "Jahr muss eine ganze Zahl sein"},
|
|
|
|
|
{"type": "between", "value": 2020, "value2": 2030, "error_text": "Jahr muss zwischen 2020 und 2030 liegen"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_start_month",
|
|
|
|
|
"widget": "dropdown",
|
|
|
|
|
"label": "Start Monat",
|
|
|
|
|
"initial_value": "1",
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"options": [
|
|
|
|
|
{"value": "1", "label": "Januar"},
|
|
|
|
|
{"value": "2", "label": "Februar"},
|
|
|
|
|
{"value": "3", "label": "März"},
|
|
|
|
|
{"value": "4", "label": "April"},
|
|
|
|
|
{"value": "5", "label": "Mai"},
|
|
|
|
|
{"value": "6", "label": "Juni"},
|
|
|
|
|
{"value": "7", "label": "Juli"},
|
|
|
|
|
{"value": "8", "label": "August"},
|
|
|
|
|
{"value": "9", "label": "September"},
|
|
|
|
|
{"value": "10", "label": "Oktober"},
|
|
|
|
|
{"value": "11", "label": "November"},
|
|
|
|
|
{"value": "12", "label": "Dezember"}
|
|
|
|
|
],
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Start Monat ist erforderlich"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_end_year",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Ende Jahr (YYYY)",
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"keyboard_type": "number"
|
|
|
|
|
},
|
|
|
|
|
"initial_value": "2024",
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Jahr ist erforderlich"},
|
|
|
|
|
{"type": "integer", "error_text": "Jahr muss eine ganze Zahl sein"},
|
|
|
|
|
{"type": "between", "value": 2020, "value2": 2030, "error_text": "Jahr muss zwischen 2020 und 2030 liegen"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_end_month",
|
|
|
|
|
"widget": "dropdown",
|
|
|
|
|
"label": "Ende Monat",
|
|
|
|
|
"initial_value": "12",
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"options": [
|
|
|
|
|
{"value": "1", "label": "Januar"},
|
|
|
|
|
{"value": "2", "label": "Februar"},
|
|
|
|
|
{"value": "3", "label": "März"},
|
|
|
|
|
{"value": "4", "label": "April"},
|
|
|
|
|
{"value": "5", "label": "Mai"},
|
|
|
|
|
{"value": "6", "label": "Juni"},
|
|
|
|
|
{"value": "7", "label": "Juli"},
|
|
|
|
|
{"value": "8", "label": "August"},
|
|
|
|
|
{"value": "9", "label": "September"},
|
|
|
|
|
{"value": "10", "label": "Oktober"},
|
|
|
|
|
{"value": "11", "label": "November"},
|
|
|
|
|
{"value": "12", "label": "Dezember"}
|
|
|
|
|
],
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Ende Monat ist erforderlich"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_values",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Zählerstände (Komma-getrennt)",
|
|
|
|
|
"hint_text": "z.B: 1000.5, 2000.2, 3000.8",
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"max_lines": 3
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"helper_text": "Geben Sie die Zählerstände in chronologischer Reihenfolge an, getrennt durch Kommas",
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte geben Sie die Zählerstände ein"}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"submit_label": "Nächster Schritt"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
# PHASE 2: Variable Auswahl und Verarbeitung
|
|
|
|
|
sensor_id = PARAMS.get("sensor_id")
|
|
|
|
|
|
|
|
|
|
if not sensor_id:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "error",
|
|
|
|
|
"message": "Sensor ID fehlt"
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# Prüfe ob bereits variable_name vorhanden ist (Phase 3)
|
|
|
|
|
if "variable_name" in PARAMS:
|
|
|
|
|
# PHASE 3: Ausführung
|
|
|
|
|
variable_name = PARAMS.get("variable_name")
|
|
|
|
|
variable_unit = PARAMS.get("variable_unit")
|
|
|
|
|
input_method = PARAMS.get("input_method", "single")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if input_method == "single":
|
|
|
|
|
# Einzelne Erfassung
|
|
|
|
|
single_datetime = PARAMS.get("single_datetime")
|
|
|
|
|
single_value = float(PARAMS.get("single_value", 0))
|
|
|
|
|
|
|
|
|
|
# Konvertiere Datetime zu ISO Format
|
|
|
|
|
if isinstance(single_datetime, str):
|
|
|
|
|
# Parse verschiedene Datetime-Formate
|
|
|
|
|
try:
|
|
|
|
|
dt = datetime.fromisoformat(single_datetime.replace('Z', '+00:00'))
|
|
|
|
|
except:
|
|
|
|
|
dt = datetime.strptime(single_datetime, "%Y-%m-%dT%H:%M:%S")
|
|
|
|
|
else:
|
|
|
|
|
dt = single_datetime
|
|
|
|
|
|
|
|
|
|
iso_datetime = dt.isoformat()
|
|
|
|
|
|
|
|
|
|
# Führe Einzelerfassung aus
|
|
|
|
|
response = record_single_reading(
|
|
|
|
|
sensor_id, iso_datetime, single_value, variable_name, variable_unit
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if "error" in response:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
<div style="background: var(--color-danger); color: white; padding: 15px; border-radius: 8px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">❌ Fehler bei der Erfassung</h3>
|
|
|
|
|
<p style="margin: 0;">{response['error']}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
record_result = response.get("recordMeterReading", {})
|
|
|
|
|
if record_result.get("success"):
|
|
|
|
|
obs = record_result.get("observation", {})
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
<div style="background: var(--color-success); color: white; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">✅ Zählerstand erfolgreich erfasst</h3>
|
|
|
|
|
<p style="margin: 0;">Der Zählerstand wurde erfolgreich gespeichert.</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid var(--color-success);">
|
|
|
|
|
<h4 style="margin: 0 0 15px 0;">📊 Erfasste Daten:</h4>
|
|
|
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
|
|
|
<tr><td style="padding: 8px 0; font-weight: bold;">Observation ID:</td><td style="padding: 8px 0;">{obs.get('id', 'N/A')}</td></tr>
|
|
|
|
|
<tr><td style="padding: 8px 0; font-weight: bold;">Zeitpunkt:</td><td style="padding: 8px 0;">{obs.get('moment', 'N/A')}</td></tr>
|
|
|
|
|
<tr><td style="padding: 8px 0; font-weight: bold;">Zählerstand:</td><td style="padding: 8px 0;">{obs.get('meterValue', 'N/A')}</td></tr>
|
|
|
|
|
<tr><td style="padding: 8px 0; font-weight: bold;">Berechneter Wert:</td><td style="padding: 8px 0;">{obs.get('value', 'N/A')}</td></tr>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
errors = record_result.get("errors", [])
|
|
|
|
|
error_html = "<ul style='margin: 10px 0; padding-left: 20px;'>"
|
|
|
|
|
for err in errors:
|
|
|
|
|
error_html += f"<li><strong>{err.get('code', 'ERROR')}:</strong> {err.get('message', 'Unbekannter Fehler')}</li>"
|
|
|
|
|
error_html += "</ul>"
|
|
|
|
|
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
<div style="background: var(--color-danger); color: white; padding: 15px; border-radius: 8px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">❌ Erfassung fehlgeschlagen</h3>
|
|
|
|
|
<p style="margin: 0;">Die Erfassung konnte nicht durchgeführt werden:</p>
|
|
|
|
|
{error_html}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
# Batch-Erfassung
|
|
|
|
|
start_year = int(PARAMS.get("batch_start_year", 2024))
|
|
|
|
|
start_month = int(PARAMS.get("batch_start_month", 1))
|
|
|
|
|
end_year = int(PARAMS.get("batch_end_year", 2024))
|
|
|
|
|
end_month = int(PARAMS.get("batch_end_month", 12))
|
|
|
|
|
batch_values_str = PARAMS.get("batch_values", "")
|
|
|
|
|
|
|
|
|
|
# Parse Batch-Werte
|
|
|
|
|
try:
|
|
|
|
|
values = [float(v.strip()) for v in batch_values_str.split(",") if v.strip()]
|
|
|
|
|
except ValueError:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "error",
|
|
|
|
|
"message": "Ungültige Zahlenwerte in Batch-Eingabe. Verwenden Sie nur Zahlen getrennt durch Kommas."
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# Generiere Monatsliste
|
|
|
|
|
months = generate_month_list(start_year, start_month, end_year, end_month)
|
|
|
|
|
|
|
|
|
|
if len(values) != len(months):
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
<div style="background: var(--color-warning); color: white; padding: 15px; border-radius: 8px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">⚠️ Anzahl Unstimmigkeit</h3>
|
|
|
|
|
<p style="margin: 0;">Anzahl der Werte ({len(values)}) stimmt nicht mit der Anzahl der Monate ({len(months)}) überein.</p>
|
|
|
|
|
<p style="margin: 10px 0 0 0;"><strong>Erwartete Monate:</strong> {', '.join(months)}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# Erstelle Readings-Liste
|
|
|
|
|
readings = []
|
|
|
|
|
for i, month in enumerate(months):
|
|
|
|
|
readings.append({
|
|
|
|
|
"month": month,
|
|
|
|
|
"meterValue": values[i]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Führe Batch-Erfassung aus
|
|
|
|
|
response = record_ultimo_readings(
|
|
|
|
|
sensor_id, variable_name, variable_unit, readings
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if "error" in response:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
<div style="background: var(--color-danger); color: white; padding: 15px; border-radius: 8px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">❌ Fehler bei der Batch-Erfassung</h3>
|
|
|
|
|
<p style="margin: 0;">{response['error']}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
batch_result = response.get("recordUltimoReadings", {})
|
|
|
|
|
created = batch_result.get("created", [])
|
|
|
|
|
errors = batch_result.get("errors", [])
|
|
|
|
|
success = batch_result.get("success", False)
|
|
|
|
|
|
|
|
|
|
html_content = f"""
|
|
|
|
|
<div style="padding: 20px; max-width: 800px;">
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if success and created:
|
|
|
|
|
html_content += f"""
|
|
|
|
|
<div style="background: var(--color-success); color: white; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">✅ Batch-Erfassung erfolgreich</h3>
|
|
|
|
|
<p style="margin: 0;">{len(created)} Zählerstände wurden erfolgreich erfasst.</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; border-left: 4px solid var(--color-success); margin-bottom: 20px;">
|
|
|
|
|
<h4 style="margin: 0 0 15px 0;">📊 Erfasste Readings:</h4>
|
|
|
|
|
<table style="width: 100%; border-collapse: collapse; background: white; border-radius: 4px; overflow: hidden;">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr style="background: var(--color-primary); color: white;">
|
|
|
|
|
<th style="padding: 12px; text-align: left;">ID</th>
|
|
|
|
|
<th style="padding: 12px; text-align: left;">Zeitpunkt</th>
|
|
|
|
|
<th style="padding: 12px; text-align: right;">Zählerstand</th>
|
|
|
|
|
<th style="padding: 12px; text-align: right;">Wert</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
for i, obs in enumerate(created):
|
|
|
|
|
bg_color = "#f8f9fa" if i % 2 == 0 else "white"
|
|
|
|
|
html_content += f"""
|
|
|
|
|
<tr style="background: {bg_color};">
|
|
|
|
|
<td style="padding: 10px; border-bottom: 1px solid #dee2e6;">{obs.get('id', 'N/A')}</td>
|
|
|
|
|
<td style="padding: 10px; border-bottom: 1px solid #dee2e6;">{obs.get('moment', 'N/A')}</td>
|
|
|
|
|
<td style="padding: 10px; text-align: right; border-bottom: 1px solid #dee2e6;">{obs.get('meterValue', 'N/A')}</td>
|
|
|
|
|
<td style="padding: 10px; text-align: right; border-bottom: 1px solid #dee2e6;">{obs.get('value', 'N/A')}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
html_content += """
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if errors:
|
|
|
|
|
html_content += f"""
|
|
|
|
|
<div style="background: var(--color-warning); color: white; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
|
|
|
|
|
<h4 style="margin: 0 0 10px 0;">⚠️ Warnungen/Fehler:</h4>
|
|
|
|
|
<ul style="margin: 0; padding-left: 20px;">
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
for err in errors:
|
|
|
|
|
html_content += f"<li><strong>{err.get('code', 'ERROR')}:</strong> {err.get('message', 'Unbekannter Fehler')}"
|
|
|
|
|
if err.get('details'):
|
|
|
|
|
html_content += f" (Details: {err.get('details')})"
|
|
|
|
|
html_content += "</li>"
|
|
|
|
|
|
|
|
|
|
html_content += "</ul></div>"
|
|
|
|
|
|
|
|
|
|
if not success and not created:
|
|
|
|
|
html_content += f"""
|
|
|
|
|
<div style="background: var(--color-danger); color: white; padding: 15px; border-radius: 8px;">
|
|
|
|
|
<h3 style="margin: 0 0 10px 0;">❌ Batch-Erfassung fehlgeschlagen</h3>
|
|
|
|
|
<p style="margin: 0;">Die Erfassung konnte nicht durchgeführt werden.</p>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
html_content += "</div>"
|
|
|
|
|
|
|
|
|
|
result = {
|
|
|
|
|
"type": "html",
|
|
|
|
|
"content": html_content
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "error",
|
|
|
|
|
"message": f"Fehler bei der Verarbeitung: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
# PHASE 2: Variable Auswahl
|
|
|
|
|
variable_units = get_variable_units(sensor_id)
|
|
|
|
|
|
|
|
|
|
if not variable_units:
|
|
|
|
|
result = {
|
|
|
|
|
"type": "error",
|
|
|
|
|
"message": "Keine Variablen für diesen Sensor gefunden"
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
# Erstelle Variable-Dropdown-Optionen
|
|
|
|
|
variable_options = []
|
|
|
|
|
for vu in variable_units:
|
|
|
|
|
clean_var_name = vu["variableName"].strip()
|
|
|
|
|
clean_unit_name = vu["unitName"].strip()
|
|
|
|
|
label = f"{clean_var_name} ({clean_unit_name})"
|
|
|
|
|
|
|
|
|
|
variable_options.append({
|
|
|
|
|
"value": f"{clean_var_name}|{clean_unit_name}",
|
|
|
|
|
"label": label
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Sortiere Optionen alphabetisch
|
|
|
|
|
variable_options.sort(key=lambda x: x["label"])
|
|
|
|
|
|
|
|
|
|
# Hole letzten Zählerstand für Kontext
|
|
|
|
|
last_obs = None
|
|
|
|
|
if variable_options:
|
|
|
|
|
# Verwende erste Variable für Kontext
|
|
|
|
|
first_var = variable_options[0]["value"].split("|")[0]
|
|
|
|
|
last_obs = get_last_observation(sensor_id, first_var)
|
|
|
|
|
|
|
|
|
|
context_info = ""
|
|
|
|
|
if last_obs:
|
|
|
|
|
context_info = f"Letzter bekannter Zählerstand: {last_obs.get('meterValue', 'N/A')} am {last_obs.get('moment', 'N/A')}"
|
|
|
|
|
else:
|
|
|
|
|
context_info = "Kein vorheriger Zählerstand gefunden"
|
|
|
|
|
|
|
|
|
|
# Übertrage alle Parameter aus der ersten Phase
|
|
|
|
|
result = {
|
|
|
|
|
"type": "form",
|
|
|
|
|
"form_definition": {
|
|
|
|
|
"title": "Variable Auswahl und Zählerstand Eingabe",
|
|
|
|
|
"description": context_info,
|
|
|
|
|
"layout": "sections",
|
|
|
|
|
"sections": [
|
|
|
|
|
{
|
|
|
|
|
"title": "Variable/Einheit",
|
|
|
|
|
"icon": "analytics",
|
|
|
|
|
"field_names": ["variable_selection"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"title": "Erfassungsart",
|
|
|
|
|
"icon": "input",
|
|
|
|
|
"field_names": ["input_method"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"title": "Einzelne Erfassung",
|
|
|
|
|
"icon": "schedule",
|
|
|
|
|
"field_names": ["single_datetime", "single_value"]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"title": "Ultimo Batch-Erfassung",
|
|
|
|
|
"icon": "batch_prediction",
|
|
|
|
|
"field_names": ["batch_start_year", "batch_start_month", "batch_end_year", "batch_end_month", "batch_values"]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"fields": [
|
|
|
|
|
# Versteckte Felder für übertragene Parameter
|
|
|
|
|
{
|
|
|
|
|
"name": "sensor_id",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Sensor ID",
|
|
|
|
|
"initial_value": sensor_id,
|
|
|
|
|
"read_only": True,
|
|
|
|
|
"enabled": False
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "variable_selection",
|
|
|
|
|
"widget": "dropdown",
|
|
|
|
|
"label": "Variable und Einheit auswählen",
|
|
|
|
|
"options": variable_options,
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte wählen Sie eine Variable aus"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "input_method",
|
|
|
|
|
"widget": "segmented_control",
|
|
|
|
|
"label": "Erfassungsart",
|
|
|
|
|
"initial_value": PARAMS.get("input_method", "single"),
|
|
|
|
|
"options": [
|
|
|
|
|
{"value": "single", "label": "Einzeln"},
|
|
|
|
|
{"value": "batch", "label": "Batch (Ultimo)"}
|
|
|
|
|
],
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte wählen Sie eine Erfassungsart"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "single_datetime",
|
|
|
|
|
"widget": "date_time_picker",
|
|
|
|
|
"label": "Datum und Uhrzeit",
|
|
|
|
|
"initial_value": PARAMS.get("single_datetime", datetime.now().isoformat()),
|
|
|
|
|
"date_config": {
|
|
|
|
|
"input_type": "both",
|
|
|
|
|
"format": "dd.MM.yyyy HH:mm"
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "single",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte wählen Sie Datum und Uhrzeit"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "single_value",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Zählerstand",
|
|
|
|
|
"initial_value": PARAMS.get("single_value", ""),
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"keyboard_type": "number"
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "single",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte geben Sie einen Zählerstand ein"},
|
|
|
|
|
{"type": "numeric", "error_text": "Zählerstand muss eine Zahl sein"},
|
|
|
|
|
{"type": "min", "value": 0, "error_text": "Zählerstand darf nicht negativ sein"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_start_year",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Start Jahr (YYYY)",
|
|
|
|
|
"initial_value": PARAMS.get("batch_start_year", "2024"),
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"keyboard_type": "number"
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Jahr ist erforderlich"},
|
|
|
|
|
{"type": "integer", "error_text": "Jahr muss eine ganze Zahl sein"},
|
|
|
|
|
{"type": "between", "value": 2020, "value2": 2030, "error_text": "Jahr muss zwischen 2020 und 2030 liegen"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_start_month",
|
|
|
|
|
"widget": "dropdown",
|
|
|
|
|
"label": "Start Monat",
|
|
|
|
|
"initial_value": PARAMS.get("batch_start_month", "1"),
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"options": [
|
|
|
|
|
{"value": "1", "label": "Januar"},
|
|
|
|
|
{"value": "2", "label": "Februar"},
|
|
|
|
|
{"value": "3", "label": "März"},
|
|
|
|
|
{"value": "4", "label": "April"},
|
|
|
|
|
{"value": "5", "label": "Mai"},
|
|
|
|
|
{"value": "6", "label": "Juni"},
|
|
|
|
|
{"value": "7", "label": "Juli"},
|
|
|
|
|
{"value": "8", "label": "August"},
|
|
|
|
|
{"value": "9", "label": "September"},
|
|
|
|
|
{"value": "10", "label": "Oktober"},
|
|
|
|
|
{"value": "11", "label": "November"},
|
|
|
|
|
{"value": "12", "label": "Dezember"}
|
|
|
|
|
],
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Start Monat ist erforderlich"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_end_year",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Ende Jahr (YYYY)",
|
|
|
|
|
"initial_value": PARAMS.get("batch_end_year", "2024"),
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"keyboard_type": "number"
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Jahr ist erforderlich"},
|
|
|
|
|
{"type": "integer", "error_text": "Jahr muss eine ganze Zahl sein"},
|
|
|
|
|
{"type": "between", "value": 2020, "value2": 2030, "error_text": "Jahr muss zwischen 2020 und 2030 liegen"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_end_month",
|
|
|
|
|
"widget": "dropdown",
|
|
|
|
|
"label": "Ende Monat",
|
|
|
|
|
"initial_value": PARAMS.get("batch_end_month", "12"),
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"options": [
|
|
|
|
|
{"value": "1", "label": "Januar"},
|
|
|
|
|
{"value": "2", "label": "Februar"},
|
|
|
|
|
{"value": "3", "label": "März"},
|
|
|
|
|
{"value": "4", "label": "April"},
|
|
|
|
|
{"value": "5", "label": "Mai"},
|
|
|
|
|
{"value": "6", "label": "Juni"},
|
|
|
|
|
{"value": "7", "label": "Juli"},
|
|
|
|
|
{"value": "8", "label": "August"},
|
|
|
|
|
{"value": "9", "label": "September"},
|
|
|
|
|
{"value": "10", "label": "Oktober"},
|
|
|
|
|
{"value": "11", "label": "November"},
|
|
|
|
|
{"value": "12", "label": "Dezember"}
|
|
|
|
|
],
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Ende Monat ist erforderlich"}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "batch_values",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Zählerstände (Komma-getrennt)",
|
|
|
|
|
"initial_value": PARAMS.get("batch_values", ""),
|
|
|
|
|
"hint_text": "z.B: 1000.5, 2000.2, 3000.8",
|
|
|
|
|
"text_field_config": {
|
|
|
|
|
"max_lines": 3
|
|
|
|
|
},
|
|
|
|
|
"conditional": {
|
|
|
|
|
"field_name": "input_method",
|
|
|
|
|
"operator": "equals",
|
|
|
|
|
"value": "batch",
|
|
|
|
|
"action": "show"
|
|
|
|
|
},
|
|
|
|
|
"helper_text": "Geben Sie die Zählerstände in chronologischer Reihenfolge an, getrennt durch Kommas",
|
|
|
|
|
"validators": [
|
|
|
|
|
{"type": "required", "error_text": "Bitte geben Sie die Zählerstände ein"}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"submit_label": "Zählerstände erfassen"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Erweitere das Form um die variable_name und variable_unit basierend auf variable_selection
|
|
|
|
|
if "variable_selection" in PARAMS:
|
|
|
|
|
var_selection = PARAMS["variable_selection"]
|
|
|
|
|
if "|" in var_selection:
|
|
|
|
|
var_name, var_unit = var_selection.split("|", 1)
|
|
|
|
|
# Füge versteckte Felder hinzu
|
|
|
|
|
result["form_definition"]["fields"].extend([
|
|
|
|
|
{
|
|
|
|
|
"name": "variable_name",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Variable Name",
|
|
|
|
|
"initial_value": var_name,
|
|
|
|
|
"read_only": True,
|
|
|
|
|
"enabled": False
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "variable_unit",
|
|
|
|
|
"widget": "text_field",
|
|
|
|
|
"label": "Variable Unit",
|
|
|
|
|
"initial_value": var_unit,
|
|
|
|
|
"read_only": True,
|
|
|
|
|
"enabled": False
|
|
|
|
|
}
|
|
|
|
|
])
|