# High-Level vs Low-Level API

Questra Data bietet zwei komplementäre APIs für unterschiedliche Anwendungsfälle.

## Übersicht

| Aspekt | High-Level API | Low-Level API |
|--------|---------------|---------------|
| **Klasse** | `QuestraData` | `QuestraDataCore` |
| **Zielgruppe** | Anwendungsentwickler | Fortgeschrittene Entwickler |
| **Abstraktionslevel** | Hoch - vereinfachte Methoden | Niedrig - direkte GraphQL/REST Calls |
| **Typ-Sicherheit** | Vollständig | Teilweise (Raw Queries: Dict) |
| **pandas Integration** | ✓ Ja | ✗ Nein |
| **Komplexität** | Einfach | Komplex |
| **Flexibilität** | Mittel | Hoch |

## High-Level API (`QuestraData`)

Die High-Level API ist für die meisten Anwendungsfälle **empfohlen**.

### Vorteile

- **Einfach zu verwenden**: Intuitive Methodennamen und Parameter
- **Typsicher**: Vollständige Type Hints und IDE-Unterstützung
- **pandas-Integration**: DataFrame-Methoden für Data Science
- **Weniger Code**: Komplexe Operationen in einer Zeile
- **Best Practices**: Automatische Error Handling und Retry-Logik

### Verwendung

```python
from questra_data import QuestraData

client = QuestraData(
    graphql_url="https://dev.example.com/graphql",
    auth_client=auth_client
)

# Einfach!
items = client.list("Stromzaehler", "Energie", properties=["_id", "name"])
```

### Typische Anwendungsfälle

- **CRUD-Operationen**: Items erstellen, lesen, aktualisieren, löschen
- **Zeitreihen-Daten**: Effiziente Verwaltung von TimeSeries
- **Data Science**: pandas DataFrames für Analysen
- **Prototyping**: Schnelle Entwicklung von Anwendungen

### API-Methoden

#### Inventory Operationen

```python
# Items auflisten
items = client.list(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    properties=["_id", "name"],
    limit=10
)

# Optional: als DataFrame (benötigt pandas)
items = client.list(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    properties=["_id", "name"]
)
df = items.to_df()

# Item erstellen
new_item = client.create(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    properties={"name": "SZ-123", "hersteller": "Test"}
)

# Item aktualisieren
updated = client.update(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    item_id="630...",
    properties={"hersteller": "Neue Firma"}
)

# Item löschen
client.delete(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    item_id="630..."
)
```

#### Zeitreihen Operationen

```python
from datetime import datetime

# Zeitreihen-Werte laden
result = client.list_timeseries_values(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    timeseries_property="messwerte",
    from_time=datetime(2025, 1, 1),
    to_time=datetime(2025, 12, 31)
)

# Optional: als DataFrame (benötigt pandas)
result = client.list_timeseries_values(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    timeseries_property="messwerte",
    from_time=datetime(2025, 1, 1),
    to_time=datetime(2025, 12, 31)
)
df = result.to_df(include_metadata=True)

# Zeitreihen-Werte speichern
from questra_data import TimeSeriesValue, Quality

values = [
    TimeSeriesValue(
        time=datetime(2025, 1, 1, 12, 0),
        value=100.5,
        quality=Quality.VALID
    )
]

client.save_timeseries_values(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    item_id="630...",
    timeseries_property="messwerte",
    values=values
)
```

#### Verwaltung

```python
from questra_data import StringProperty, ConflictAction

# Namespace erstellen
namespace = client.create_namespace(
    name="TestNamespace",
    description="Test"
)

# Inventory erstellen
properties = [
    StringProperty(
        propertyName="Name",
        maxLength=200,
        isRequired=True
    )
]

inventory = client.create_inventory(
    name="TestInventory",
    properties=properties,
    if_exists=ConflictAction.IGNORE
)

# Namespaces auflisten
namespaces = client.list_namespaces()

# Inventories auflisten
inventories = client.list_inventories()

# System-Informationen
system_info = client.get_system_info()
print(f"Version: {system_info.version}")
```

## Low-Level API (`QuestraDataCore`)

Die Low-Level API bietet **direkte Kontrolle** über GraphQL und REST Calls.

### Vorteile

- **Volle Flexibilität**: Direkter Zugriff auf alle API-Features
- **Raw Queries**: Eigene GraphQL Queries ausführen
- **Feinsteuerung**: Kontrolle über Paging, Filtering, etc.
- **Neue Features**: Zugriff auf Features, die noch nicht in High-Level API sind

### Verwendung

```python
from questra_data import QuestraDataCore

# Direkter Zugriff
lowlevel = QuestraDataCore(
    graphql_url="https://dev.example.com/graphql",
    auth_client=auth_client
)

# Oder über High-Level Client
lowlevel = client.lowlevel
```

### Typische Anwendungsfälle

- **Custom Queries**: Spezielle GraphQL Queries
- **Performance**: Optimierte Queries für große Datenmengen
- **Neue Features**: Features, die noch nicht in High-Level API sind
- **Legacy Code**: Migration von altem Code

### API-Struktur

Die Low-Level API ist nach Operationstypen strukturiert:

```python
# GraphQL Queries (Lesen)
lowlevel.queries.get_inventories()
lowlevel.queries.get_namespaces()
lowlevel.queries.get_inventory_items(...)

# GraphQL Mutations (Schreiben)
lowlevel.mutations.create_namespace(...)
lowlevel.mutations.create_inventory(...)
lowlevel.mutations.upsert_item(...)

# REST API - TimeSeries
lowlevel.timeseries.get_data(...)
lowlevel.timeseries.set_data(...)

# REST API - Files
lowlevel.files.upload(...)
lowlevel.files.download(...)

# REST API - Audit
lowlevel.audit.get_logs(...)

# Dynamic Inventory Operations
lowlevel.inventory.list_items(...)
lowlevel.inventory.create_item(...)
lowlevel.inventory.update_item(...)

# Raw GraphQL Query
result = lowlevel.execute_raw("""
    query {
        _timeZones(first: 10) {
            nodes { name }
        }
    }
""")
```

### Beispiele

#### Raw GraphQL Query

```python
# Eigene GraphQL Query ausführen
result = lowlevel.execute_raw("""
    query GetItems($inventoryName: String!, $first: Int) {
        items(inventoryName: $inventoryName, first: $first) {
            nodes {
                _id
                _created
                ... on Stromzaehler {
                    stromzaehlernummer
                    hersteller
                }
            }
        }
    }
""", variables={
    "inventoryName": "Stromzaehler",
    "first": 100
})

items = result["items"]["nodes"]
```

#### Direkte Queries

```python
# Inventories abrufen
inventories = lowlevel.queries.get_inventories()

# Namespace erstellen
namespace = lowlevel.mutations.create_namespace(
    namespace_name="TestNS",
    description="Test"
)

# Items mit Paging
items = lowlevel.inventory.list_items(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    properties=["_id", "name"],
    first=100,
    after="cursor..."
)
```

#### REST API

```python
from datetime import datetime

# TimeSeries Daten abrufen
data = lowlevel.timeseries.get_data(
    inventory_name="Stromzaehler",
    namespace_name="Energie",
    item_id="630...",
    property_name="messwerte",
    from_time=datetime(2025, 1, 1),
    to_time=datetime(2025, 12, 31)
)

# Datei hochladen
lowlevel.files.upload(
    inventory_name="Documents",
    namespace_name="Files",
    item_id="630...",
    property_name="attachment",
    file_path="/path/to/file.pdf"
)
```

## Wann welche API verwenden?

### Verwenden Sie High-Level API wenn:

- ✓ Sie schnell produktiv werden wollen
- ✓ Sie pandas DataFrames verwenden
- ✓ Sie Standard CRUD-Operationen durchführen
- ✓ Sie Best Practices automatisch nutzen wollen

### Verwenden Sie Low-Level API wenn:

- ✓ Sie spezielle GraphQL Queries benötigen
- ✓ Sie Performance optimieren müssen
- ✓ Sie neue, noch nicht abstrahierte Features verwenden
- ✓ Sie volle Kontrolle über API-Calls benötigen

## Kombination beider APIs

Sie können beide APIs **kombinieren**:

```python
from questra_data import QuestraData

# High-Level Client
client = QuestraData(
    graphql_url="https://dev.example.com/graphql",
    auth_client=auth_client
)

# High-Level für Standard-Operationen
items = client.list("Stromzaehler", "Energie")

# Low-Level für spezielle Query
lowlevel = client.lowlevel
custom_result = lowlevel.execute_raw("""
    query { ... }
""")
```

!!! tip "Best Practice"
    Starten Sie mit der High-Level API und wechseln Sie zur Low-Level API nur wenn notwendig.

## Nächste Schritte

- [API Referenz](../api/index.md) - Vollständige API-Dokumentation
- [Best Practices](../guides/best-practices.md) - Empfehlungen für Production
- [TimeSeries Guide](../guides/timeseries.md) - Arbeiten mit Zeitreihen-Daten
