Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ The client exposes four resource groups — all endpoints, parameters, and DTOs
| `client.environments` | `list()`, `get()`, `create()`, `delete()`, `create_entry()`, `delete_entry()` |
| `client.calculations` | `daily_values()`, `extrapolations()` |

Starting with version 3.0.0, meter-reading CSV exports default to semicolon (`;`).
To preserve the comma-separated output of version 2.x, explicitly set
`delimiter=CsvDelimiter.COMMA` in `ExportMeterReadingsDto`.

## Configuration

```python
Expand Down
2 changes: 2 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ test and release checks. No API token, live backend or sibling checkout is neede
header names case-insensitively; additional transport headers are allowed.
Query key order and JSON object key order are irrelevant; extra query/body
fields and duplicate query keys are not allowed.
- Meter-reading CSV exports default to `semicolon`. Explicit `comma`, `semicolon`
and `tab` selections override the default.
- `response` is the local server's synthetic HTTP response. Bodies use exactly
one of `json`, UTF-8 `text`, or `base64`; `{}` means no body. A `json: null`
body is distinct from no body. Base64 preserves exact CSV bytes, including BOM
Expand Down
4 changes: 2 additions & 2 deletions contracts/cases/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@
"body": {
"json": {
"includeHeader": true,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
"columns": [
"date",
Expand Down Expand Up @@ -1285,7 +1285,7 @@
"body": {
"json": {
"includeHeader": true,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
"columns": [
"date",
Expand Down
104 changes: 100 additions & 4 deletions contracts/cases/meter-readings.json
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@
"body": {
"json": {
"includeHeader": true,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
"columns": [
"date",
Expand All @@ -367,12 +367,12 @@
"Content-Type": "text/csv; charset=utf-8"
},
"body": {
"base64": "77u/ZGF0ZSx2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaLDEyMy40NQ0K"
"base64": "77u/ZGF0ZTt2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaOzEyMy40NQ0K"
}
},
"expected": {
"result": {
"base64": "77u/ZGF0ZSx2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaLDEyMy40NQ0K"
"base64": "77u/ZGF0ZTt2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaOzEyMy40NQ0K"
}
}
},
Expand Down Expand Up @@ -462,7 +462,7 @@
"body": {
"json": {
"includeHeader": false,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
"columns": [
"value"
Expand All @@ -484,6 +484,102 @@
"base64": "MTIz"
}
}
},
{
"id": "readings-export-explicit-comma",
"operation": "meterReadings.export",
"input": {
"deviceId": "11111111-1111-4111-8111-111111111111",
"config": {
"columns": [
"date",
"value"
],
"delimiter": "comma"
}
},
"request": {
"method": "POST",
"path": "/v3/devices/standard/11111111-1111-4111-8111-111111111111/meter-readings/export",
"query": {},
"headers": {
"Authorization": "Bearer contract-test-token",
"Content-Type": "application/json"
},
"body": {
"json": {
"includeHeader": true,
"delimiter": "comma",
"dateFormat": "iso",
"columns": [
"date",
"value"
]
}
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "text/csv; charset=utf-8"
},
"body": {
"base64": "77u/ZGF0ZSx2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaLDEyMy40NQ0K"
}
},
"expected": {
"result": {
"base64": "77u/ZGF0ZSx2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaLDEyMy40NQ0K"
}
}
},
{
"id": "readings-export-explicit-tab",
"operation": "meterReadings.export",
"input": {
"deviceId": "11111111-1111-4111-8111-111111111111",
"config": {
"columns": [
"date",
"value"
],
"delimiter": "tab"
}
},
"request": {
"method": "POST",
"path": "/v3/devices/standard/11111111-1111-4111-8111-111111111111/meter-readings/export",
"query": {},
"headers": {
"Authorization": "Bearer contract-test-token",
"Content-Type": "application/json"
},
"body": {
"json": {
"includeHeader": true,
"delimiter": "tab",
"dateFormat": "iso",
"columns": [
"date",
"value"
]
}
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "text/csv; charset=utf-8"
},
"body": {
"base64": "77u/ZGF0ZQl2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaCTEyMy40NQ0K"
}
},
"expected": {
"result": {
"base64": "77u/ZGF0ZQl2YWx1ZQ0KMjAyNi0wOS0wMVQxMDozNDo1Ni4xMjNaCTEyMy40NQ0K"
}
}
}
]
}
4 changes: 2 additions & 2 deletions energy_tracker_api/models/meter_readings.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,13 @@ class ExportMeterReadingsDto:
Attributes:
columns: Columns and their order in the exported CSV file.
include_header: Whether to include the header row (default: True).
delimiter: CSV delimiter (default: comma).
delimiter: CSV delimiter (default: semicolon).
date_format: Date format in the export (default: iso).
"""

columns: list[ExportColumn]
include_header: bool = True
delimiter: CsvDelimiter = CsvDelimiter.COMMA
delimiter: CsvDelimiter = CsvDelimiter.SEMICOLON
date_format: DateFormat = DateFormat.ISO

def _to_dict(self) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "energy-tracker-api"
version = "2.1.0"
version = "3.0.0"
description = "Async Python client for Energy Tracker API"
readme = "README.md"
requires-python = ">=3.14"
Expand Down
6 changes: 3 additions & 3 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def test_to_dict_with_defaults(self):
assert result == {
"columns": ["date", "value"],
"includeHeader": True,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
}

Expand All @@ -235,7 +235,7 @@ def test_to_dict_with_custom_options(self):
dto = ExportMeterReadingsDto(
columns=[ExportColumn.DATE, ExportColumn.VALUE, ExportColumn.NOTE],
include_header=False,
delimiter=CsvDelimiter.SEMICOLON,
delimiter=CsvDelimiter.COMMA,
date_format=DateFormat.UNIX,
)

Expand All @@ -246,7 +246,7 @@ def test_to_dict_with_custom_options(self):
assert result == {
"columns": ["date", "value", "note"],
"includeHeader": False,
"delimiter": "semicolon",
"delimiter": "comma",
"dateFormat": "unix",
}

Expand Down
8 changes: 4 additions & 4 deletions tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,23 +369,23 @@ class TestMeterReadingResourceExport:
async def test_export_minimal(self):
# Arrange
client = Mock(spec=EnergyTrackerClient)
client._make_request = AsyncMock(return_value=b"date,value\n2024-01-15,123.45\n")
client._make_request = AsyncMock(return_value=b"date;value\n2024-01-15;123.45\n")
resource = MeterReadingResource(client)
config = ExportMeterReadingsDto(columns=[ExportColumn.DATE, ExportColumn.VALUE])

# Act
result = await resource.export(device_id="device-123", export_config=config)

# Assert
assert result == b"date,value\n2024-01-15,123.45\n"
assert result == b"date;value\n2024-01-15;123.45\n"
client._make_request.assert_called_once_with(
method="POST",
endpoint="/v3/devices/standard/device-123/meter-readings/export",
response_type="bytes",
json={
"columns": ["date", "value"],
"includeHeader": True,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
},
params=None,
Expand Down Expand Up @@ -417,7 +417,7 @@ async def test_export_with_filters(self):
json={
"columns": ["date", "value"],
"includeHeader": True,
"delimiter": "comma",
"delimiter": "semicolon",
"dateFormat": "iso",
},
params={
Expand Down