Metadata-Version: 2.4
Name: etiket_sync_agent_qcodes
Version: 0.3.0b1
Summary: QCoDeS backend for eTiKeT sync agent
Author: QHarbor team
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://qharbor.nl
Project-URL: Documentation, https://docs.qharbor.nl
Keywords: etiket,sync,backend,qcodes,quantum
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENCE
Requires-Dist: etiket_sync_agent>=0.3.0b1
Requires-Dist: qcodes
Requires-Dist: xarray
Dynamic: license-file

# eTiKeT Sync Agent - QCoDeS Backend

Backend for synchronizing QCoDeS datasets with the eTiKeT platform. This backend reads measurements from QCoDeS SQLite databases and syncs them to the cloud.

## Installation

Install the QCoDeS backend using the eTiKeT Sync SDK:

```python
from etiket_sdk.sync import Backends

# Install the latest version
Backends.install_from_pypi("etiket-sync-agent-qcodes")
```

> ⚠️ **Important:** This backend installs a specific version of QCoDeS. If the installed version is newer than what your main environment uses, it may upgrade your database schema, making it unreadable by older QCoDeS versions. See [Version Pinning](#version-pinning) below.

The package is automatically discovered by `etiket_sync_agent` through the entry-point system. Once installed, you can verify with:

```python
# List installed backends
print(Backends.list())

# Get details for the QCoDeS backend
backend = Backends.get("etiket_sync_agent_qcodes")
print(backend)
```

### Version Pinning

By default, installing the backend also installs the latest version of `qcodes`. If you need a specific QCoDeS version to match your main working environment, you can install it separately:

**Why this matters:** Newer QCoDeS versions may upgrade your SQLite database schema. Once upgraded, older QCoDeS versions may not be able to read the database. This is primarily a concern for very old QCoDeS versions.

```python
# Check your current QCoDeS version in your working environment
import qcodes
print(qcodes.__version__)

# Then install a specific QCoDeS version in the sync agent
Backends.install_from_pypi("qcodes", version="x.x.x")
```

### Updating the Backend

```python
# Update to the latest version
Backends.update_from_pypi("etiket-sync-agent-qcodes")
```

## What Gets Synchronized

When a QCoDeS measurement is synced, the following data is extracted and uploaded:

| QCoDeS Data | eTiKeT Field | Description |
|-------------|--------------|-------------|
| `guid` | `alt_uid` | Unique identifier for the measurement |
| Measurement name | `name` | Name of the dataset |
| `run_timestamp` | `collected` | When the measurement was taken |
| Parameter labels | `tags` | Extracted from instrument/parameter metadata |
| Snapshot instruments | `attributes` | Instrument settings stored in the snapshot |
| xarray dataset | HDF5 file | The actual measurement data |


### Metadata Sources

The backend extracts metadata from multiple sources:

1. **QCoDeS Snapshot**: Instrument labels and parameter values used in the measurement are added as tags.
2. **QHMetaData Instrument**: Custom tags and attributes (see below)
3. **`inspectr_tag` metadata**: Ranking (`star` = +1, `cross` = -1)
4. **Config `static_attributes`**: Static attributes defined in the sync source configuration

## Configuration

The QCoDeS backend requires a `QCoDeSConfigData` configuration with the following fields:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `database_path` | `Path` or `str` | Yes | Path to the QCoDeS SQLite database file (`.db`) |
| `set_up` | `str` | Yes | Name of the experimental setup (added as `set-up` attribute) |
| `static_attributes` | `dict[str, str]` | No | Optional static key-value pairs added to every dataset |

### Example Configuration

Example using the `etiket-sdk` package:

```python
from etiket_sdk.sync import SyncSources

SyncSources.create(
    name="my_qcodes_source",
    backend_identifier="etiket_sync_agent_qcodes",
    config_data={
        "database_path": "/path/to/experiments.db", 
        "set_up": "dilution_fridge_1",
        "static_attributes": { # optional
            "project": "qubit_calibration",
            "lab": "Building A - Room 101"
        }
    },
    default_scope="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
)
```

---

## Adding QCoDeS Metadata to a Dataset

You can attach tags and attributes to a dataset by adding the `QHMetaData` instrument to your QCoDeS Station. Because the instrument is recorded in the QCoDeS snapshot, the sync agent extracts the tags and attributes and adds them to the QHarbor dataset.

You can add both **static** and **dynamic** metadata:

- **Static metadata**: Stays the same for all measurements (e.g., project, lab PC)
- **Dynamic metadata**: Changes per run (e.g., calibration type, measurement type)

### Initializing the Instrument

There are two ways to define the instrument: directly in Python or via a Station YAML file.

#### Using Python

```python
from etiket_sync_agent_qcodes.qcodes_metadata import QHMetaData
from qcodes import Station

station = Station()

# Create and register the instrument
qh_meta = QHMetaData(
    "qh_meta",
    static_tags=["cool_experiment"],
    static_attributes={"project": "resonator project"},
)
station.add_component(qh_meta)
```

> **Note**: The instrument name **must** be `qh_meta`. Do not change this.

#### Using a YAML Configuration File

Add the following to your Station YAML config:

```yaml
instruments:
  qh_meta:
    type: etiket_sync_agent_qcodes.qcodes_metadata.QHMetaData
    init:
      static_tags:
        - "cool_experiment"
      static_attributes:
        project: "resonator project"
        measurement_computer: "LAB-A-PC-5"
```

> **Note**: The key **must** be `qh_meta`. Do not change this.

Load the instrument from the config:

```python
from qcodes import Station

# Replace with your config file name
station = Station(config_file='qc_config.yaml')
station.load_instrument("qh_meta")
```

For more information, see the QCoDeS docs: [Configuring the Station by using a YAML configuration file](https://microsoft.github.io/Qcodes/examples/basic_examples/Station.html#Configuring-the-Station-by-using-a-YAML-configuration-file).

### Runtime Usage

Access the instrument and set per-run metadata:

```python
# If defined via Station YAML
qh_meta = station.components["qh_meta"]

# Start of run: clear dynamic values, then set new ones
qh_meta.reset()
qh_meta.add_tags(["testing", "calibration"])
qh_meta.add_attributes({
    "calibration": "ALLXY",
    "qubit": "Q1",
})

# ... measurement happens here ...
```

### Behavior Notes

| Aspect | Behavior |
|--------|----------|
| **Static vs Dynamic** | Static tags/attributes are set at construction and persist across `reset()`. Dynamic ones are cleared by `reset()`. |
| **Attribute Overrides** | Dynamic attributes with the same key as a static attribute will override the static value. |
| **Tag Duplicates** | Tags are deduplicated using a `set()`. |

---

## Features

- Direct integration with QCoDeS datasets
- Automatic metadata extraction from snapshots
- Live sync support for running measurements
- xarray dataset conversion
- Custom metadata via `QHMetaData` instrument

## Requirements

- Python >= 3.10
- QCoDeS >= 0.52

## License

Copyright © 2025 QHarbor. All Rights Reserved. See [LICENCE](LICENCE) for details.
