Commit da9e7246 authored by Marco Schmiedel's avatar Marco Schmiedel

fix

parent 92796ceb
{
"fileId": "12a942d7-6f13-4c57-9f5c-1c6fd3baaf21",
"originalPath": "work/manager/OpenAiManager.py",
"currentPath": "work/manager/OpenAiManager.py",
"hash": "a5d54b395c9edf435ee5ec0a2f3637058b54bab058827175487268f4dc07304a",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1745314592645,
"lastFileModificationTimestamp": 1745314218432.6475
}
{
"fileId": "1e5daa93-a29c-4db7-98ac-3457b779f0d1",
"originalPath": "work/manager/S3Manager.py",
"currentPath": "work/manager/S3Manager.py",
"hash": "3626bb3b00d9142ba6d471dfdd2d7a0f54d72afca4b908843c9cf6d483d66754",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1745314595210,
"lastFileModificationTimestamp": 1745314341957.45
}
......@@ -2,9 +2,9 @@
"fileId": "22983490-9c01-4bd1-8649-dfe87c659225",
"originalPath": "work/config/MauiConfig.py",
"currentPath": "work/config/MauiConfig.py",
"hash": "7749ff881e9fac6aaeffe5274aa9566b2b5692a1ec4e7f47e20b79671a0788a3",
"hash": "6e627f3800fd413c6dbde92ad2e274d5e3047af0f906de4d75fc826cc129631e",
"docContent": "<p>In this configuration, you’ll find the credentials required to log in to Freenet-Maui.</p>",
"checkedStatus": "done",
"checkedStatus": "todo",
"comments": [
{
"commentId": "3bc16f5e-4032-44a8-9012-4b632849ba50",
......@@ -12,6 +12,6 @@
"timestamp": 1744614418809
}
],
"lastCheckedTimestamp": 1744614348029,
"lastFileModificationTimestamp": 1744614095522.7373
"lastCheckedTimestamp": 1745314578450,
"lastFileModificationTimestamp": 1745313945182.1555
}
......@@ -2,11 +2,11 @@
"fileId": "24784b38-54dc-4000-9d2a-f59082ebbc1c",
"originalPath": "work/models/base_base.py",
"currentPath": "work/models/base_base.py",
"hash": "19b6bd8fd24ccac708053a39f013632721ed03ec35663efd7d7ec222cf0be934",
"hash": "a13647e2879a37dfcb76bf95c143fc42b0da2eac67a471884afd5c314dc46f8f",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1744805211278,
"lastFileModificationTimestamp": 1744787773143.9746,
"lastCheckedTimestamp": 1745314601207,
"lastFileModificationTimestamp": 1745313922231.5488,
"flaggedForCopy": false
}
......@@ -2,9 +2,9 @@
"fileId": "38b9eebe-955e-4052-a0f6-29c69b1242b3",
"originalPath": "work/config/MysqlConfig.py",
"currentPath": "work/config/MysqlConfig.py",
"hash": "f9c624640584ecc2400d5053b7b366a6b2305ecdf836221aa95e3e169254af79",
"hash": "8eeae892f7c5f5aa1e894ca9ff7b8c66ea2891bc37c0167c404cd6e0cb95f858",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"checkedStatus": "todo",
"comments": [
{
"commentId": "56c5adba-20f4-4524-a894-41f81ab7ca55",
......@@ -12,6 +12,6 @@
"timestamp": 1744622354948
}
],
"lastCheckedTimestamp": 1744624735475,
"lastFileModificationTimestamp": 1744624729595.99
"lastCheckedTimestamp": 1745314583521,
"lastFileModificationTimestamp": 1745313973064.8933
}
......@@ -2,10 +2,10 @@
"fileId": "543b791f-9a45-4b53-906e-c49f79ac95d7",
"originalPath": "work/notebooks/ImportCsvToDatabase.ipynb",
"currentPath": "work/notebooks/ImportCsvToDatabase.ipynb",
"hash": "6cdb9455f0658ca804b4beb6201bdfa4ae721451c9fa7c99e0bebe9f5a0dbf09",
"hash": "c6e6e4c70653fbfc4d43ae89e4f05fc1a8d2804eb2f515ad52c41637bd5b0e14",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"checkedStatus": "changed",
"comments": [],
"lastCheckedTimestamp": 1744806594117,
"lastFileModificationTimestamp": 1744806589225.9287
"lastFileModificationTimestamp": 1745313435812.8953
}
......@@ -2,11 +2,11 @@
"fileId": "766dc461-001e-4901-8faf-263820ad96cd",
"originalPath": "work/manager/MysqlManager.py",
"currentPath": "work/manager/MysqlManager.py",
"hash": "c016d5cb9c9b391d19e41323196715c0e57b5838846232d2bdf761f53293e1b4",
"hash": "0506c7ebbfff68bce628902018e66ba50936a52e08aa595cadc8d5324c48d46f",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1744624751868,
"lastFileModificationTimestamp": 1744624748400.9556,
"lastCheckedTimestamp": 1745314589383,
"lastFileModificationTimestamp": 1745314105678.9277,
"flaggedForCopy": false
}
{
"fileId": "7a3a246b-fc0e-4c80-b748-96b941efab5c",
"originalPath": "work/config/AWSConfig.py",
"currentPath": "work/config/AWSConfig.py",
"hash": "5a6654cb1cd77f8d531fcc1541d31261ea02c4e8cb126f2cc43a217c9c6920aa",
"docContent": "<p><br></p>",
"checkedStatus": "todo",
"comments": [],
"lastCheckedTimestamp": 1745314580866,
"lastFileModificationTimestamp": 1745311719614.9841
}
......@@ -2,10 +2,10 @@
"fileId": "d470fd53-8f95-47d0-a63b-5851586c0eda",
"originalPath": "work/manager/SeleniumManager.py",
"currentPath": "work/manager/SeleniumManager.py",
"hash": "be7dde51af30a8ea1f80ea3090f39d2e9a84168cf7d3e15086c16bdbbfde2c26",
"hash": "f29307970d124e55c7066e71ddf682f55e043d4f925195bdf320ff9da1311e27",
"docContent": "<p><br></p>",
"checkedStatus": "done",
"comments": [],
"lastCheckedTimestamp": 1744620379152,
"lastFileModificationTimestamp": 1744547804595.8647
"lastCheckedTimestamp": 1745314597597,
"lastFileModificationTimestamp": 1745314531223.019
}
{
"fileId": "f645f6dc-6831-4020-a6ae-8b5a572eed54",
"originalPath": "work/config/OpenAiConfig.py",
"currentPath": "work/config/OpenAiConfig.py",
"hash": "3da3805934b36b3ab8c21e10d8babe3cc8b6c9acb7b2b1446f782612b76cf2c4",
"docContent": "<p><br></p>",
"checkedStatus": "todo",
"comments": [],
"lastCheckedTimestamp": 1745314586062,
"lastFileModificationTimestamp": 1745313976355.6653
}
AWS_ACCESS_KEY_ID = "AKIATXO2FKVK3BSS3DMT"
AWS_SECRET_ACCESS_KEY = "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"
REGION = "eu-central-1"
BUCKET_NAME = "freenetflyer"
# This variable stores the username for accessing Maui.
MAUI_USERNAME = "28009594-198"
# This variable stores the password for accessing Maui.
MAUI_PASSWORD = "8v#5YeeQyh"
# This variable stores the authentication code (2FA code) for accessing Maui.
MAUI_AUTHCODE = "2D3JJNG3WWGSWRDI5KRW2MZKL3NJEZXJ"
# The MySQL host address.
MYSQL_HOST = "itmax-backoffice-prod-aurora-r3dbcluster-1e8hysitdwijk.cluster-crb5tahpiszg.eu-central-1.rds.amazonaws.com"
# The MySQL username for the connection.
MYSQL_USER = "labor-itmaxmaster"
# The MySQL password for the connection.
MYSQL_PASSWORD = "floz09sx3dTyx144gy"
# The MySQL database name.
MYSQL_DATABASE = "itmax_tarifs"
# The MySQL port number.
MYSQL_PORT = 3306
# If set to True, the MysqlManager will establish an SSH tunnel when connecting to MySQL.
USE_SSH_TUNNEL = True
# The SSH host address (the remote SSH server to tunnel through).
SSH_HOST = "jumphost.bugsmasher.online"
# The SSH port number (default is usually 22).
SSH_PORT = 22
# The SSH username for the tunnel.
SSH_USERNAME = "root"
# The SSH password for the tunnel.
SSH_PASSWORD = "7dHz2xO8ct1143T"
\ No newline at end of file
secret = "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"
......@@ -4,15 +4,14 @@ from sqlalchemy.orm import sessionmaker
import config.MysqlConfig as DatabaseConfig
from sshtunnel import SSHTunnelForwarder
# We create a new class called MysqlManager.
# In dieser Klasse wird die Verwaltung einer MySQL-Verbindung umgesetzt.
class MysqlManager:
# The following constructor initializes the MySQL connection.
# In diesem Konstruktor werden die Verbindungskonfigurationen aus dem Config-Modul geladen und der SSH-Tunnel bei Bedarf gestartet.
def __init__(self):
# Instead of reading a configuration file, we import the Python configuration.
# We construct the configuration dictionary using values from the DatabaseConfig module.
self.config = {
# In dieser Variablen werden die Konfigurationsdaten für den Datenbankzugriff gesammelt.
self.dbConfig = {
"host": DatabaseConfig.MYSQL_HOST,
"user": DatabaseConfig.MYSQL_USER,
"password": DatabaseConfig.MYSQL_PASSWORD,
......@@ -20,45 +19,46 @@ class MysqlManager:
"port": DatabaseConfig.MYSQL_PORT
}
# Check if an SSH tunnel should be used.
# In dieser Abzweigung wird geprüft, ob ein SSH-Tunnel verwendet werden soll.
if getattr(DatabaseConfig, "USE_SSH_TUNNEL", False):
# Initialize the SSH tunnel using SSHTunnelForwarder.
self.tunnel = SSHTunnelForwarder(
# In dieser Variablen wird ein SSH-Tunnel erstellt, der den Datenverkehr zu einem lokalen Port umleitet.
self.sshTunnel = SSHTunnelForwarder(
(DatabaseConfig.SSH_HOST, DatabaseConfig.SSH_PORT),
ssh_username=DatabaseConfig.SSH_USERNAME,
ssh_password=DatabaseConfig.SSH_PASSWORD,
remote_bind_address=(self.config["host"], self.config["port"])
remote_bind_address=(self.dbConfig["host"], self.dbConfig["port"])
)
# Start the SSH tunnel.
self.tunnel.start()
# Set the host to localhost and port to the tunnel's local bind port.
host = "127.0.0.1"
port = self.tunnel.local_bind_port
# Hier wird der SSH-Tunnel gestartet, damit die Weiterleitung aktiv wird.
self.sshTunnel.start()
# In diesen Variablen werden Host und Port auf die lokalen Tunnel-Daten gesetzt.
dbHost = "127.0.0.1"
dbPort = self.sshTunnel.local_bind_port
else:
# No SSH tunnel is used.
self.tunnel = None
host = self.config["host"]
port = self.config["port"]
# In dieser Abzweigung wird kein SSH-Tunnel verwendet.
self.sshTunnel = None
dbHost = self.dbConfig["host"]
dbPort = self.dbConfig["port"]
# Construct the MySQL engine using the connection details.
engine = create_engine(
f"mysql+pymysql://{self.config['user']}:{self.config['password']}@{host}:{port}/{self.config['database']}",
# In dieser Variablen wird das SQLAlchemy-Engine-Objekt mit den Verbindungsdaten erzeugt.
dbEngine = create_engine(
f"mysql+pymysql://{self.dbConfig['user']}:{self.dbConfig['password']}@{dbHost}:{dbPort}/{self.dbConfig['database']}",
echo=False
)
# Start the database session.
self.session = sessionmaker(bind=engine)()
# In dieser Variablen wird eine neue SessionFactory erzeugt und eine Session erstellt.
self.dbSession = sessionmaker(bind=dbEngine)()
# "getSession" provides the database connection to other modules.
# In dieser Methode wird eine Session-Instanz zurückgegeben, um Datenbankaktionen durchzuführen.
def getSession(self):
return self.session
return self.dbSession
# The following method closes the database connection and stops the SSH tunnel if active.
# In dieser Methode wird die bestehende Session geschlossen und der SSH-Tunnel (falls vorhanden) gestoppt.
def close(self):
self.session.close()
if self.tunnel:
self.tunnel.stop()
self.dbSession.close()
if self.sshTunnel:
self.sshTunnel.stop()
import sys; sys.path.append("..")
import requests
import json
import re
import time
import config.OpenAiConfig as Config
# In dieser Klasse wird die Kommunikation mit der OpenAI-API verwaltet.
class OpenAiManager:
# In diesem Konstruktor werden die Konfiguration und der API-Key geladen und grundlegende Parameter für die Anfragen gesetzt.
def __init__(self):
# In dieser Variablen wird die Konfiguration abgelegt, wobei der Key aus dem externen Config-Modul stammt.
self.config = {
"secret": Config.secret
}
# In dieser Variablen wird der API-Key aus dem Konfigurationsdictionary entnommen.
self.apiKey = self.config["secret"]
# In dieser Variablen wird die URL für Chat-Completions hinterlegt.
self.openAiUrl = "https://api.openai.com/v1/chat/completions"
# In dieser Variablen werden die Header für die HTTP-Anfragen an die OpenAI-API definiert.
self.requestHeaders = {
"Authorization": f"Bearer {self.apiKey}",
"Content-Type": "application/json"
}
# In dieser Methode wird ein Prompt an die OpenAI-API geschickt, um eine Chat-Antwort zu erhalten.
def chat(self, prompt, model="o1-mini"):
# In dieser Variablen wird das JSON-Objekt für die Anfrage an die OpenAI-API aufgebaut.
requestData = {
"model": model,
"messages": [{"role": "user", "content": prompt}]
}
# In diesem Schritt wird ein POST-Request an die definierte Chat-Completion-URL gesendet.
response = requests.post(self.openAiUrl, headers=self.requestHeaders, json=requestData)
# In dieser Variablen wird die Antwort als Python-Objekt interpretiert.
responseData = response.json()
# In dieser Abzweigung wird geprüft, ob in der Antwort mindestens ein Eintrag unter 'choices' existiert.
if "choices" in responseData:
# Hier wird der Inhalt der ersten Choice zurückgegeben, wenn vorhanden.
return responseData["choices"][0]["message"]["content"]
# In dieser Abzweigung wird None zurückgegeben, falls kein verwertbarer Inhalt existiert.
else:
return None
import sys; sys.path.append("..")
import config.AWSConfig as Config
import boto3
import json
import re
# In dieser Klasse werden die Interaktionen mit AWS S3 verwaltet, wie zum Beispiel das Hochladen von Dateien.
class S3Manager:
# In diesem Konstruktor wird ein S3-Client erzeugt und der Bucketname aus den Konfigurationswerten hinterlegt.
def __init__(self):
self.s3Client = boto3.client(
's3',
aws_access_key_id=Config.AWS_ACCESS_KEY_ID,
aws_secret_access_key=Config.AWS_SECRET_ACCESS_KEY,
region_name=Config.REGION
)
self.bucketName = Config.BUCKET_NAME
# In dieser Methode wird eine Datei in den konfigurierten S3-Bucket hochgeladen, wobei standardmäßig eine ACL für public-read gesetzt wird.
def uploadFile(self, filePath, s3Key, acl="public-read"):
# In diesem try-Block wird versucht, die Datei auf S3 hochzuladen und anschließend der öffentliche Link zurückgegeben.
try:
self.s3Client.upload_file(filePath, self.bucketName, s3Key, ExtraArgs={'ACL': acl})
return self.getPublicUrl(s3Key)
# In dieser Abzweigung wird eine Exception abgefangen, falls das Hochladen fehlschlägt, und None zurückgegeben.
except Exception as e:
print(f"Fehler beim Hochladen der Datei: {e}")
return None
# In dieser Methode wird der öffentliche URL-Link generiert, anhand des Bucketnamens und des S3-Schlüssels.
def getPublicUrl(self, s3Key):
return f"https://{self.bucketName}.s3.amazonaws.com/{s3Key}"
This diff is collapsed.
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, JSON
from sqlalchemy.orm import relationship
from models._system import Base
from models.basegroup_bgro import BasegroupBgro
......@@ -7,13 +7,16 @@ from models.option_opti import OptionOpti
class BaseBase(Base):
__tablename__ = 'base_base'
id_base = Column(Integer, primary_key=True, autoincrement=True)
basegroup_base = Column(Integer, ForeignKey('basegroup_bgro.id_bgro'))
basegroup_base = Column(Integer, ForeignKey("basegroup_bgro.id_bgro"))
provider_base = Column(String(255), nullable=False)
providercode_base = Column(String(255))
providercode_base= Column(String(255))
name_base = Column(String(255), nullable=False)
alias_base = Column(String(255))
network_base = Column(Integer, nullable=False)
type_base = Column(Integer, nullable=False)
flyerurl_base = Column(String(255))
piburl_base = Column(String(255))
details_base = Column(JSON) # enthält das von GPT extrahierte Tarif‑JSON
created_base = Column(DateTime, nullable=False)
updated_base = Column(DateTime, nullable=False)
basegroup = relationship("BasegroupBgro", back_populates="bases")
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
**Aufgabe:**
Du bist eine hochpräzise API zur Extraktion spezifischer Mobilfunktarif-Merkmale aus Dokumentenpaaren. Deine Eingabe besteht immer aus zwei PDF-Dateien: einem **Produktdetailblatt/Flyer** und einem **Produktinformationsblatt (PIB)**, die gemeinsam *einen* spezifischen Tarif beschreiben.
Deine Aufgabe ist es, beide Dokumente sorgfältig und vergleichend zu analysieren, um die unten definierten **relevanten Tarifbestandteile** mit höchstmöglicher Genauigkeit zu extrahieren. Das Ergebnis **muss ausschließlich** ein einzelnes JSON-Objekt sein, das exakt die vorgegebenen Schlüsselnamen und das flache Key-Value-Format verwendet. Ignoriere irrelevante Informationen wie Anbieteradressen, AGB-Verweise oder allgemeine Marketingtexte.
**Extraktionsanweisungen und Felddefinitionen:**
1. **`tariff_name` (String):** Extrahiere den vollständigen, exakten Namen des Tarifs.
2. **`marketing_start_date` (String oder null):** Extrahiere das Vermarktungsdatum (aus PIB). Formatiere als `JJJJ-MM-TT` oder `null`.
3. **`network_operator` (String oder null):** Identifiziere den Netzbetreiber (z.B. "Telekom", "Vodafone", "O2").
4. **`network_technology` (String oder null):** Identifiziere die primär beworbene/höchste Netztechnologie (z.B. "5G", "LTE").
5. **`is_data_only_tariff` (Boolean):** Ermittle präzise, ob es ein reiner Datentarif ist (`true`/`false`). Ein Kriterium ist, wenn Telefonie nicht als Flat inkludiert ist oder explizit als "nicht möglich" gilt. Achte darauf, Tarife mit "Data" im Namen, die dennoch Telefonie/SMS-Flats haben könnten, nicht fälschlicherweise als reine Datentarife zu klassifizieren. Prüfe die Inklusivleistungen und Preise sorgfältig.
6. **`inclusive_internet_flat` (Boolean oder null):** Prüfe auf explizite Nennung einer Internet-Flat (`true`/`false`).
7. **`inclusive_telephony_flat` (Boolean oder null):** Prüfe auf explizite Nennung einer Telefonie-Flat oder Preis pro Minute von 0,00 € (`true`/`false`). Bei `is_data_only_tariff: true` setze auf `false`.
8. **`telephony_price_per_minute_eur_brutto` (Number oder null):** Wenn `inclusive_telephony_flat: false` und Telefonie möglich ist, extrahiere den Brutto-Preis pro Minute. Sonst `0.0` (bei Flat) oder `null` (wenn nicht möglich/gefunden).
9. **`telephony_price_per_minute_eur_netto` (Number oder null):** Wenn `telephony_price_per_minute_eur_brutto` ein Wert größer als 0 ist, berechne den Nettopreis (Bruttopreis / 1.19). Sonst `0.0` (bei Flat) oder `null`. Runde auf 4 Nachkommastellen.
10. **`inclusive_sms_flat` (Boolean oder null):** Prüfe auf explizite Nennung einer SMS-Flat oder Preis pro SMS von 0,00 € (`true`/`false`).
11. **`sms_price_per_unit_eur_brutto` (Number oder null):** Wenn `inclusive_sms_flat: false`, extrahiere den Brutto-Preis pro SMS. Sonst `0.0` (bei Flat) oder `null` (wenn nicht gefunden).
12. **`sms_price_per_unit_eur_netto` (Number oder null):** Wenn `sms_price_per_unit_eur_brutto` ein Wert größer als 0 ist, berechne den Nettopreis (Bruttopreis / 1.19). Sonst `0.0` (bei Flat) oder `null`. Runde auf 4 Nachkommastellen.
13. **`inclusive_volte_wlan_call` (Boolean oder null):** Prüfe auf explizite Nennung von VoLTE/WLAN-Call. Setze `true`, wenn erwähnt und `is_data_only_tariff: false`. Sonst `false`.
14. **`data_volume_gb` (Number oder null):** Extrahiere das Datenvolumen in GB.
15. **`data_download_max_mbps` (Number oder null):** Extrahiere max. Download in Mbit/s.
16. **`data_upload_max_mbps` (Number oder null):** Extrahiere max. Upload in Mbit/s.
17. **`data_download_throttled_kbps` (Number oder null):** Extrahiere Drossel-Download in kbit/s.
18. **`data_upload_throttled_kbps` (Number oder null):** Extrahiere Drossel-Upload in kbit/s.
19. **`data_billing_increment_kb` (Number oder null):** Extrahiere Datentaktung in KB.
20. **`telephony_billing_increment_seconds` (String oder null):** Extrahiere Telefontaktung (z.B. "60/60"). Setze `null` bei Datentarifen.
21. **`pricing_connection_fee_eur_brutto` (Number oder null):** Extrahiere den einmaligen Anschlusspreis (Brutto).
22. **`pricing_connection_fee_eur_netto` (Number oder null):** Berechne den Netto-Anschlusspreis (Bruttopreis / 1.19), falls Brutto vorhanden. Runde auf 4 Nachkommastellen.
23. **`pricing_monthly_initial_eur_brutto` (Number oder null):** Extrahiere den monatlichen Bruttopreis (initiale Periode).
24. **`pricing_monthly_initial_eur_netto` (Number oder null):** Extrahiere den monatlichen Nettopreis (initiale Periode), falls explizit genannt (oft in Klammern). Falls nicht genannt, berechne ihn (Bruttopreis / 1.19). Runde auf 4 Nachkommastellen.
25. **`pricing_monthly_after_period_eur_brutto` (Number oder null):** Extrahiere den monatlichen Bruttopreis nach der initialen Periode.
26. **`pricing_monthly_after_period_eur_netto` (Number oder null):** Berechne den monatlichen Nettopreis nach der initialen Periode (Bruttopreis / 1.19). Runde auf 4 Nachkommastellen.
27. **`contract_min_duration_months` (Number oder null):** Extrahiere die Mindestlaufzeit in Monaten.
28. **`contract_cancellation_notice_period_months` (Number oder null):** Extrahiere die Kündigungsfrist als Zahl der Monate (z.B. `1` aus "1 Monat").
**Wichtige Hinweise:**
* Verwende `null` für Werte, die nicht zuverlässig extrahiert werden können.
* Stelle sicher, dass numerische Werte als Zahlen (Number) im JSON erscheinen.
* Runde berechnete Nettopreise auf 4 Nachkommastellen.
* Die Ausgabe darf **nur das JSON-Objekt** enthalten, ohne jeglichen erläuternden Text davor oder danach.
**Gewünschtes JSON-Ausgabeformat (zur Referenz):**
{
"tariff_name": "...",
"marketing_start_date": "...",
"network_operator": "...",
"network_technology": "...",
"is_data_only_tariff": ...,
"inclusive_internet_flat": ...,
"inclusive_telephony_flat": ...,
"telephony_price_per_minute_eur_brutto": ...,
"telephony_price_per_minute_eur_netto": ...,
"inclusive_sms_flat": ...,
"sms_price_per_unit_eur_brutto": ...,
"sms_price_per_unit_eur_netto": ...,
"inclusive_volte_wlan_call": ...,
"data_volume_gb": ...,
"data_download_max_mbps": ...,
"data_upload_max_mbps": ...,
"data_download_throttled_kbps": ...,
"data_upload_throttled_kbps": ...,
"data_billing_increment_kb": ...,
"telephony_billing_increment_seconds": "...",
"pricing_connection_fee_eur_brutto": ...,
"pricing_connection_fee_eur_netto": ...,
"pricing_monthly_initial_eur_brutto": ...,
"pricing_monthly_initial_eur_netto": ...,
"pricing_monthly_after_period_eur_brutto": ...,
"pricing_monthly_after_period_eur_netto": ...,
"contract_min_duration_months": ...,
"contract_cancellation_notice_period_months": ...
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment