# Python SDK for Pigello API

A Python SDK for querying, writing, deleting, and constructing data against the Pigello API. It is fully typed and can be a practical alternative to reading the raw documentation.

## Introduction

The SDK helps you with:

- **Querying** — Fetch entities and lists with filters and ordering.
- **Writing** — Create and update data via the API.
- **Deleting** — Remove entities where the API allows.
- **Constructing data** — Build request payloads with typed entities.


Because it is completely typed, your editor and type checker can help you use the API correctly without constantly checking the docs.

## Requirements

- **Python:** ≥ 3.14.
- **Dependencies:** Only the `requests` package.


There are no restrictions on architecture or OS.

## Installation

1. Obtain the built source bundle for the Pigello Python SDK from [here](https://docs.api.pigello.io/sdk/pigello-sdk.tar.gz?download).
2. Install it in your Python environment with pip:


```bash
source my_py_env/bin/activate   # or your env
python -m pip install 'downloads/pigello-sdk.tar.gz'
```

## Usage

### Common: attribute values

Attributes on an entity can have one of three kinds of values:

- **`None`**
- **`NotSet`** — Indicates the attribute was deferred or not loaded from the API.
- **A value** — Any value allowed by the type of the field.


The `NotSet` value (from `pigello_sdk.fields`) means the attribute has not been collected from the API. Both `NotSet()` and `NOT_SET` evaluate to false in a boolean context and are equal to each other.

```python
from pigello_sdk.fields import NotSet, NOT_SET

assert bool(NotSet) is bool(NOT_SET) is bool(None) is False
assert NotSet() == NotSet()
assert NOT_SET == NOT_SET
```

### Session: authentication

All communication with the Pigello API through the SDK requires authentication. You provide a **session** object that handles it.

```python
from pigello_sdk.session import APIUserAgentSession

session: APIUserAgentSession = APIUserAgentSession(
    application_id,
    service_id,
    target_customer_id,
    target_content_type,
    target_object_id,
    integration_user_agent_secret,
    service_version_id,
)
```

You can verify the session by calling **`collect_and_set_profile`** or by accessing the **`user_agent`** property. When you access `user_agent` for the first time, the SDK authenticates with the credentials you provided and stores the profile (including the `IntegrationUserAgent` entity).

```python
user_agent = session.user_agent
print(user_agent.organization)  # Organization entity connected to the user
```

### Querying data

Most of the time you will want to **bulk query** data. The **`QueryHelper`** is the main tool for this. You can create it manually or via a helper on the entity class you want to query (e.g. `Building.get_query_helper(session)`).

Add filters and ordering, then run the query to collect data.

```python
from pigello_sdk.query import FILTER_OPERATIONS, FilterInstruction, OrderInstruction
from pigello_sdk.entities.objects.building import Building
from pigello_sdk.entities.objects.realestate import Realestate
from pigello_sdk.fields import NotSet

query_helper = Building.get_query_helper(session)

# Add filters
query_helper.add_filters([
    FilterInstruction("registered_bi_area", FILTER_OPERATIONS.GTE, 500),
    FilterInstruction("name", FILTER_OPERATIONS.IN, ["name1", "name2"]),
])

# Add ordering
query_helper.add_ordering([
    OrderInstruction("registered_bi_area", ascending=False),
    OrderInstruction("registered_usable_area", ascending=True),
])

# Run the query
result = query_helper.collect_query()

# result is QueryResult[Building] — iterable, with metadata
result_list = result.instances  # bulk.EntitiesList (subclass of list)
```

**Relational attributes:** When you access a relation (e.g. `building.realestate`), only the related entity’s `id` may be set; other attributes can be `NotSet` until you resolve them. Use **`resolve_unresolved_relations`** on the list to load related data in bulk instead of one request per item:

```python
# Only the realestate id may be set; other attributes NotSet
assert isinstance(result_list[0].realestate, Realestate)
assert bool(result_list[0].realestate.id)
assert result_list[0].realestate.name == NotSet

# Resolve the realestate relation for all buildings in the list
result_list.resolve_unresolved_relations(restrict_to_field_names=["realestate"])

# Now realestate attributes are loaded
assert bool(result_list[0].realestate.name)

# Resolve all unresolved relations (or restrict by entity type)
result_list.resolve_unresolved_relations(restrict_to_entity_types=[Realestate])
```

## Best practices

- **Resolve relations in bulk** — Prefer `resolve_unresolved_relations` on lists instead of loading relations one-by-one.
- **Check for `NotSet`** — When reading attributes, remember that deferred/optional fields may be `NotSet` or `None`.


Pigello does not guarantee an update lifecycle for this SDK (e.g. new features, deprecations, or bug fixes).