diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index 774c119..ea3e173 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -1,11 +1,44 @@
name: Continuous Integration and Deployment
on:
+ pull_request:
+ push:
+ branches: [master, offline-test-suite] # TEMP: remove `offline-test-suite` before merge — used to bootstrap CI while the pull_request trigger isn't yet on master
release:
types: [created]
workflow_dispatch:
jobs:
+ # Offline test suite — runs on every PR and push to master.
+ # test_performance_stats.py covers `custom_tables` and still hits AEMO
+ # + references local files that aren't in CI. Parked until ported.
+ # Everything else runs against the in-process dummy server from
+ # tests/conftest.py and is fully offline.
+ test:
+ name: Tests (Python ${{ matrix.python-version }}, ${{ matrix.os }})
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Install uv
+ uses: astral-sh/setup-uv@v3
+ with:
+ enable-cache: true
+ - name: Set up Python ${{ matrix.python-version }}
+ run: uv python install ${{ matrix.python-version }}
+ - name: Sync dependencies
+ run: uv sync --python ${{ matrix.python-version }}
+ - name: Run tests
+ run: >
+ uv run pytest tests/
+ --ignore=tests/test_performance_stats.py
+ -v
+
# Publishes to PyPi if tests are passed and release is created
publish:
if: github.event_name == 'release' && github.event.action == 'created'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index aa61ef5..45d99cc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -51,19 +51,22 @@ Ready to contribute? Here's how to set up `ispypsa` for local development.
$ git checkout -b name-of-your-bugfix-or-feature
```
-6. When you're done making changes, check that your changes pass any tests.
-
- - Configure an environment variable called "NEMOSIS_TEST_CACHE" specifying the location of the raw data cache to use
- while testing
- - Run all tests by running `uv run -m unittest discover tests`
- - Run a subset of tests using the name of the test class or method, for example
- `uv run -m unittest discover tests -v -k TestDynamicDataCompilerWithSettlementDateFiltering`
- - All tests should pass before a PR is considered good to merge into main. However, the tests are very long running
- so it is best to test on a small subset of tests that are likely to fail, before running the full suite.
- Additionally, the full suite should be run twice, once with an empty cache, and then again with cache prefilled.
- - We acknowledge the testing process is currently a bit cumbersome and slow, any contributions to improve this
- process are welcomed :)
- - A bit more info to come on testing once I get them all working again . . .
+6. When you're done making changes, check that your changes pass the tests:
+
+ ```console
+ $ uv run pytest tests/ \
+ --ignore=tests/test_data_fetch_methods.py \
+ --ignore=tests/test_errors_and_warnings.py \
+ --ignore=tests/test_format_options.py \
+ --ignore=tests/test_performance_stats.py \
+ --ignore=tests/test_processing_info_maps.py
+ ```
+
+ The suite is fully offline and runs in under a minute. The five ignored files are legacy
+ network-hitting tests slated for removal. CI runs the same invocation on every PR.
+
+ For details on how the test suite is structured, how to add tests for new tables, and how
+ fixtures are maintained, see [testing_and_maintenance.md](testing_and_maintenance.md).
7. Commit your changes and open a pull request.
diff --git a/pyproject.toml b/pyproject.toml
index dbeb9c7..71904c3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,7 +16,7 @@ dependencies = [
"openpyxl>=3.1.5",
]
readme = "README.md"
-requires-python = ">= 3.9"
+requires-python = ">= 3.10"
[build-system]
requires = ["hatchling"]
diff --git a/src/nemosis/defaults.py b/src/nemosis/defaults.py
index 5a24442..e52126e 100644
--- a/src/nemosis/defaults.py
+++ b/src/nemosis/defaults.py
@@ -1,7 +1,7 @@
import os
names = {
- "FCAS Providers": "NEM Registration and Exemption List.xls",
+ "FCAS Providers": "NEM Registration and Exemption List.xlsx",
"DISPATCHLOAD": "PUBLIC_DVD_DISPATCHLOAD",
"NEXT_DAY_DISPATCHLOAD": "PUBLIC_NEXT_DAY_DISPATCHLOAD",
"DUDETAILSUMMARY": "PUBLIC_DVD_DUDETAILSUMMARY",
@@ -22,7 +22,7 @@
"FCAS_4_SECOND": "FCAS",
"ELEMENTS_FCAS_4_SECOND": "Elements_FCAS.csv",
"VARIABLES_FCAS_4_SECOND": "Ancillary Services Market Causer Pays Variables File.csv",
- "Generators and Scheduled Loads": "NEM Registration and Exemption List.xls",
+ "Generators and Scheduled Loads": "NEM Registration and Exemption List.xlsx",
"MNSP_INTERCONNECTOR": "PUBLIC_DVD_MNSP_INTERCONNECTOR",
"MNSP_PEROFFER": "PUBLIC_DVD_MNSP_PEROFFER",
"INTERCONNECTOR": "PUBLIC_DVD_INTERCONNECTOR",
@@ -133,8 +133,8 @@
static_table_url = {
"ELEMENTS_FCAS_4_SECOND": "https://www.nemweb.com.au/Reports/Current/Causer_Pays_Elements/",
"VARIABLES_FCAS_4_SECOND": "https://aemo.com.au/-/media/files/electricity/nem/settlements_and_payments/settlements/auction-reports/archive/ancillary-services-market-causer-pays-variables-file.csv",
- "Generators and Scheduled Loads": "https://www.aemo.com.au/-/media/Files/Electricity/NEM/Participant_Information/NEM-Registration-and-Exemption-List.xls",
- "_downloader.download_xl": "https://www.aemo.com.au/-/media/Files/Electricity/NEM/Participant_Information/NEM-Registration-and-Exemption-List.xls",
+ "Generators and Scheduled Loads": "https://www.aemo.com.au/-/media/files/electricity/nem/Participant_Information/NEM-Registration-and-Exemption-List.xlsx",
+ "_downloader.download_xl": "https://www.aemo.com.au/-/media/files/electricity/nem/Participant_Information/NEM-Registration-and-Exemption-List.xlsx",
}
aemo_mms_url = "http://www.nemweb.com.au/Data_Archive/Wholesale_Electricity/MMSDM/{}/MMSDM_{}_{}/MMSDM_Historical_Data_SQLLoader/DATA/{}.zip"
diff --git a/testing_and_maintenance.md b/testing_and_maintenance.md
new file mode 100644
index 0000000..2dd01d8
--- /dev/null
+++ b/testing_and_maintenance.md
@@ -0,0 +1,224 @@
+# Testing and maintenance
+
+This document covers how the NEMOSIS test suite is structured, how to run it, and how to extend or
+maintain it as AEMO's data formats evolve.
+
+## Overview
+
+The end-to-end suite runs against a local dummy HTTP server (defined in `tests/conftest.py`) that
+serves pre-filtered AEMO data committed to `tests/fixtures/data/`. The server is a bare
+`SimpleHTTPServer` rooted at the fixture tree, which mirrors AEMO's URL paths verbatim — at test
+time NEMOSIS's hostname is swapped for the local server and everything else is real.
+
+The whole suite is fast (under a minute) and network-free.
+
+## Running tests
+
+```console
+$ uv run pytest tests/ --ignore=tests/test_performance_stats.py
+```
+
+`test_performance_stats.py` is the last legacy network-hitting file. It covers the
+`custom_tables` module (VWAP, capacity factors, plant stats) which hasn't been ported offline
+yet — most of its assertions run on synthetic DataFrames and are trivially portable, but a
+few depend on local plant-stats files (`E:\plants_stats_test_data\`) that exist only on one
+developer's machine. CI uses the same invocation (see `.github/workflows/cicd.yml`).
+
+To run a single file:
+
+```console
+$ uv run pytest tests/end_to_end_table_tests/test_dispatch_price.py -v
+```
+
+## Suite layout
+
+```
+tests/
+ conftest.py # Dummy HTTP server fixture
+ fixtures/
+ spec.py # What to download for each table
+ build.py # Downloads + filters + writes fixture files
+ data/ # Committed fixture tree (mirrors AEMO URLs)
+ end_to_end_table_tests/
+ _boundaries.py # Shared boundary-test helper
+ test_
.py # One file per AEMO table
+ DECISIONS.md # Internal design notes (transitional)
+ test_filters.py # Pure unit tests
+ test_date_generators.py
+ test_query_wrappers.py
+ test_errors.py # Argument validation + NoDataToReturn + cache idempotency
+ test_processing_info_maps.py # Cross-table validation of search_type classification
+ test_performance_stats.py # Legacy custom_tables tests — ignored in CI
+```
+
+Each test file in `end_to_end_table_tests/` covers one AEMO table. Files start with a module
+docstring explaining the table's shape and which eras the fixture covers.
+
+## The fixture system
+
+`tests/fixtures/data/` is a verbatim mirror of AEMO's URL tree. For example:
+
+```
+http://www.nemweb.com.au/Data_Archive/Wholesale_Electricity/MMSDM/2018/...
+ ↓
+tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/...
+```
+
+This means the dummy server doesn't need any clever routing — it just serves static files at the
+matching paths.
+
+Fixtures are filtered down (~50× smaller than raw AEMO archives) by:
+
+- **Row filter**: each table keeps only two DUIDs / two REGIONIDs / one INTERCONNECTORID. The
+ selection lives in `tests/fixtures/spec.py` (`DUIDS`, `REGIONS`, etc.).
+- **Time filter**: only the first 3 and last 2 days of each month, controlled by
+ `KEEP_FIRST_DAYS` / `KEEP_LAST_DAYS` in `build.py`.
+- **Section filter** (scrape-pattern files only): for files like `PUBLIC_DAILY` that bundle many
+ logical tables in one CSV, the build script keeps only the section NEMOSIS reads and strips the
+ D-rows from the rest, preserving the I-row markers NEMOSIS counts against.
+
+Total fixture tree: ~12 MB across ~200 files.
+
+## Eras
+
+An era is a single month (or day, for scrape-pattern tables) chosen because it straddles a known
+AEMO format change or sits on a year boundary. Each table's fixture covers one or more eras. The
+full list lives in `tests/fixtures/spec.py::ERAS`:
+
+| Era | Purpose |
+|-----------|---------------------------------------------------------------------------|
+| `2018-05` | Pre-5-min-trading baseline |
+| `2020-01` | Year boundary, pre-5-min, monthly bidding |
+| `2021-02` | Last viable MMS month for bidding before the 3-year gap |
+| `2021-05` | First month of the new 5-min NEM and daily bidding layouts |
+| `2022-01` | Year boundary, 5-min dispatch, 30-min trading legacy |
+| `2022-06` | Legacy era (kept to avoid fixture churn — could be retired) |
+| `2024-09` | First month after `PUBLIC_DVD_` → `PUBLIC_ARCHIVE#` rename |
+| `2025-01` | Year boundary, `PUBLIC_ARCHIVE#` format, post-bidding-gap |
+| `recent` | Pinned to a date inside AEMO's rolling current-data window |
+
+The three Jan-1 eras (2020-01, 2022-01, 2025-01) exist primarily for the boundary test matrix —
+year wraps are exercised by anchoring boundary cases on Jan 1.
+
+## The boundary test matrix
+
+`_boundaries.py` generates a parametrised matrix of (era × flavour × time-of-day) cases for every
+dynamic time-series table. Each case asserts entity-set match, exact per-entity row count,
+first/last timestamp, no duplicates, and regular stride.
+
+**Vocabulary:**
+
+- **Era** — a tagged anchor month (e.g. `2021-05`).
+- **T** — time of day for a probe point (e.g. `00:00`, `04:00`, `04:05`).
+- **Stride** — the table's native interval, in minutes (5, 30, etc.).
+- **Boundary** — midnight on day 1 of the era month — the natural fence between fixture files.
+- **Flavour** — how the query window is positioned relative to the boundary:
+ - `at` — 1h forward window starting at T on day 1.
+ - `before` — 1h backward window ending at T on day 1.
+ - `into` — variable window from `last-day-of-prev-month@23:00` to `day1@T`, straddles the
+ boundary at every T.
+
+**Tables not covered by the helper** (with reasons, in `_boundaries.py::_HELPER_DOES_NOT_COVER`):
+
+- `BIDDAYOFFER_D`, `MNSP_DAYOFFER` — daily stride.
+- `BIDPEROFFER_D` — partitioned by AEMO trading day (04:05 → 04:00 next day) not calendar day.
+- `MNSP_PEROFFER` — multi-dimensional rows + known bug (see Known quirks).
+- `DISPATCHCONSTRAINT` — variable per-interval presence.
+
+These tables keep their per-era smoke tests; only the matrix is skipped.
+
+## Adding a test for a new table
+
+1. **Pick eras** the table should cover. Refer to the era table above. Most dynamic time-series
+ tables use the dispatch set: `2018-05`, `2020-01`, `2021-05`, `2022-01`, `2024-09`, `2025-01`
+ (the three Jan-1 eras give year-boundary coverage for the matrix).
+2. **Add the table to `tests/fixtures/spec.py::DYNAMIC_TABLES`** with its filter spec — which
+ eras, which row filter columns and values, which columns to keep, etc.
+3. **Run `uv run python tests/fixtures/build.py`** to download, filter, and write the fixture
+ files into `tests/fixtures/data/`.
+4. **Write `test_.py`** in `tests/end_to_end_table_tests/`, following the style of
+ an existing file. Use the `nemosis_fixture` fixture (from `conftest.py`) to get a fresh cache
+ directory. If the table is suitable for the boundary matrix, parametrise on
+ `boundary_cases("")` and assert with `assert_boundary_shape(...)`.
+5. **Run** `uv run pytest tests/end_to_end_table_tests/test_.py -v` and iterate.
+
+## Refreshing fixtures
+
+Most fixtures are stable and don't need refreshing — the eras are anchored to historical months
+that AEMO doesn't change. The exception is the `recent` era, which is pinned to a date inside
+AEMO's rolling current-data window (`/Reports/Current/...`). That window only retains the last
+few months of data, so the `recent` fixtures age out periodically.
+
+The committed fixtures keep working indefinitely — staleness only bites when you next try to
+*rebuild* (e.g. adding a new table that uses the `recent` era, or refiltering an existing one).
+At that point AEMO will return 404s for the scrape-pattern tables, which is the signal to refresh.
+
+To refresh:
+
+1. Update `spec.ERAS["recent"]` to a date inside AEMO's current window (within the last ~2
+ months is safe).
+2. Re-run `uv run python tests/fixtures/build.py`.
+3. Commit the updated fixtures.
+
+Tables that use the `recent` era: `DAILY_REGION_SUMMARY`, `NEXT_DAY_DISPATCHLOAD`,
+`INTERMITTENT_GEN_SCADA`.
+
+## Adding a new era
+
+When AEMO changes a data format, add an era to capture the transition:
+
+1. Pick a single month (or a Jan-1 month if you also want year-boundary coverage) that straddles
+ the change.
+2. Add it to `spec.ERAS`.
+3. Add the era key to the affected tables' `eras` lists in `spec.DYNAMIC_TABLES`.
+4. Re-run `uv run python tests/fixtures/build.py` to fetch the new fixtures.
+5. If the format change affects the table's row count, columns, or stride, adjust the test file
+ accordingly (era-parametrise where behaviour differs).
+
+## Known quirks
+
+These have bitten contributors before. Worth a read before adding a test.
+
+- **`filter_cols` must be a subset of `select_columns`** when calling `dynamic_data_compiler`.
+- **`select_columns` for tables wrapped by `drop_duplicates_by_primary_key`** (MNSP_PEROFFER,
+ MNSP_DAYOFFER, the effective-date config tables) must include every column in
+ `defaults.table_primary_keys[table]`, or NEMOSIS errors with a `KeyError` on the missing dedupe
+ columns. SPDCONNECTIONPOINTCONSTRAINT is a particularly easy one to trip on — its primary key
+ includes BIDTYPE.
+- **Prev-month / prev-day buffering** in NEMOSIS's date generator only fires when `start_time`
+ lands on a natural boundary (first of month, midnight). Tests that probe interior points don't
+ see those extra buffer requests.
+- **`filter_on_effective_date` runs before `most_recent_records_before_start_time`** and drops any
+ row with `EFFECTIVEDATE >= end_time`. Effective-date fixture entities therefore need at least
+ one record dated *before* the test's `start_time`, or the test sees an empty frame.
+- **Effective-date config tables** have `search_type = "all"` in NEMOSIS, which by default scans
+ every month from `nem_data_model_start_time` (2009-07) through `end_time` — ~180 HTTP requests
+ per call. Tests narrow this to a single month by monkeypatching
+ `defaults.nem_data_model_start_time` to the era date, and opt out of the build-time time trim
+ via `keep_full_month: True` in `spec.py`.
+- **Year-straddle queries aren't covered.** A true year-boundary crossing for a non-Jan era —
+ e.g. `[2024/08/15, 2025/01/15]` — would exercise `year_and_month_gen`'s `start_year < end_year`
+ branch with many months of iteration. The fixture doesn't include Sep/Oct/Nov 2024, so such
+ queries 404 on intervening months. If a future contributor needs this coverage, extend the
+ fixture to include the intervening months (cost: fixture size) or add a unit test against
+ `year_and_month_gen` directly.
+- **Mid-month queries return empty frames.** The fixture builder keeps only the first 3 and last
+ 2 days of each era month (see `KEEP_FIRST_DAYS` / `KEEP_LAST_DAYS` in `build.py`). A test that
+ queries, say, `[2021/05/15, 2021/05/16]` will get back a non-erroring empty DataFrame from every
+ covered table — NEMOSIS's own code path is fine, the fixture just doesn't have those rows. If
+ you're writing a test whose window lands in the kept-days gap, either anchor it to day 1–3 / the
+ last 2 days of the era, or `assert not data.empty` explicitly so the empty case fails loudly
+ rather than sliding past a soft subset check.
+- **`MNSP_PEROFFER` is effectively unreadable past ~April 2021**. AEMO began writing
+ `MNSP_BIDOFFERPERIOD` data into the existing `PUBLIC_DVD_MNSP_PEROFFER_*` archives, then
+ renamed the archive stub outright in Aug-2024. NEMOSIS's defaults know neither transition.
+ Pre-existing bug, tracked in the test file and `spec.py`. `MNSP_DAYOFFER` is unaffected.
+
+## Parked tables
+
+- **FCAS_4_SECOND** — AEMO's Causer Pays archive is empty at every URL NEMOSIS tries (existing
+ upstream issue #64). No fixture, no test.
+- **FCAS_4s_SCADA_MAP** — built by `custom_tables.py::fcas4s_scada_match`, which consumes
+ `FCAS_4_SECOND` values to find lowest-error element↔DUID matches. Blocked on the same upstream
+ outage. A standalone fixture would mean fabricating the 4-second values the algorithm exists to
+ match — no meaningful test.
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..ad5471a
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,94 @@
+"""Shared pytest fixtures.
+
+`nemosis_fixture` is the workhorse: it spins up a local HTTP server that
+serves tests/fixtures/data/ (the committed tree built by build.py), swaps
+every AEMO URL in `nemosis.defaults` to point at it, and gives the test
+a fresh cache directory. Tests that use this fixture behave exactly as
+if they were hitting nemweb.com.au, but in milliseconds and offline.
+"""
+from __future__ import annotations
+
+import http.server
+import logging
+import socket
+import threading
+from pathlib import Path
+
+import pytest
+
+from nemosis import defaults
+
+
+# NEMOSIS's own INFO/DEBUG logs leak into test output and clutter failure
+# context. Silence anything below WARNING — genuinely surprising conditions
+# (e.g. a 404 on a file we expected to fixture) still surface.
+logging.getLogger("nemosis").setLevel(logging.WARNING)
+
+FIXTURE_ROOT = Path(__file__).parent / "fixtures" / "data"
+
+
+# ---------------------------------------------------------------------------
+# Local HTTP server (session-scoped — started once, reused across all tests)
+# ---------------------------------------------------------------------------
+
+class _QuietHandler(http.server.SimpleHTTPRequestHandler):
+ def log_message(self, *_args, **_kwargs) -> None:
+ pass # silence the default per-request stderr logging
+
+
+def _pick_free_port() -> int:
+ with socket.socket() as s:
+ s.bind(("127.0.0.1", 0))
+ return s.getsockname()[1]
+
+
+@pytest.fixture(scope="session")
+def aemo_mock_server() -> str:
+ port = _pick_free_port()
+
+ def make_handler(*args, **kwargs):
+ return _QuietHandler(*args, directory=str(FIXTURE_ROOT), **kwargs)
+
+ server = http.server.ThreadingHTTPServer(("127.0.0.1", port), make_handler)
+ thread = threading.Thread(target=server.serve_forever, daemon=True)
+ thread.start()
+ try:
+ yield f"http://127.0.0.1:{port}"
+ finally:
+ server.shutdown()
+ thread.join(timeout=5)
+
+
+# ---------------------------------------------------------------------------
+# URL redirection + isolated cache dir
+# ---------------------------------------------------------------------------
+
+def _redirect_url(url: str, base: str) -> str:
+ """Rewrite `https://whatever.com/foo/bar` → `{base}/foo/bar`."""
+ after_scheme = url.index("://") + 3
+ path_start = url.find("/", after_scheme)
+ if path_start < 0:
+ return base + "/"
+ return base + url[path_start:]
+
+
+@pytest.fixture
+def nemosis_fixture(aemo_mock_server, tmp_path, monkeypatch) -> Path:
+ """Point nemosis at the local mock server and supply a clean cache dir.
+
+ Returns the cache directory (a tmp_path) so tests can pass it to
+ dynamic_data_compiler / cache_compiler / static_table.
+ """
+ base = aemo_mock_server
+
+ monkeypatch.setattr(defaults, "nem_web_domain_url", base + "/")
+ monkeypatch.setattr(defaults, "aemo_mms_url", _redirect_url(defaults.aemo_mms_url, base))
+ monkeypatch.setattr(defaults, "fcas_4_url", _redirect_url(defaults.fcas_4_url, base))
+ monkeypatch.setattr(defaults, "fcas_4_url_hist", _redirect_url(defaults.fcas_4_url_hist, base))
+ monkeypatch.setattr(
+ defaults, "static_table_url",
+ {k: _redirect_url(v, base) for k, v in defaults.static_table_url.items()},
+ )
+ monkeypatch.setattr(defaults, "raw_data_cache", str(tmp_path))
+
+ return tmp_path
diff --git a/tests/end_to_end_table_tests/_boundaries.py b/tests/end_to_end_table_tests/_boundaries.py
new file mode 100644
index 0000000..769bca5
--- /dev/null
+++ b/tests/end_to_end_table_tests/_boundaries.py
@@ -0,0 +1,410 @@
+"""Boundary-test helpers — readable rewrite of `_boundaries.py`.
+
+Same external API: `boundary_cases(table)` returns a list of `BoundaryCase`,
+`assert_boundary_shape(...)` checks a returned frame against a case.
+
+This file is laid out top-down: the two public entry points come first,
+followed by helpers in roughly the order they're called.
+
+----------------------------------------------------------------------------
+Vocabulary used throughout this file:
+
+ era A single calendar month chosen as a test anchor — listed in
+ `spec.ERAS`. Picked to straddle known AEMO format transitions
+ (e.g. 2021-05 is the first 5-min-NEM month) or to sit on a
+ year boundary (2020-01, 2022-01, 2025-01) for the year-wrap
+ coverage in this matrix. Each era covered by a table
+ contributes its own (flavour × T) sub-matrix.
+
+ T A time-of-day on day 1 of the era month. The matrix probes
+ three values per table — see "Three time-of-day probe points"
+ below. Used to anchor each flavour's window.
+
+ stride The per-row interval, in minutes, of the table at the era in
+ question. 5 for dispatch tables, 30 for trading tables before
+ 2021-10 and for ROOFTOP_PV_ACTUAL throughout. `stride_for(table,
+ era_date)` returns the right value, including the 30→5 switch
+ TRADINGPRICE/TRADINGINTERCONNECT made on 2021-10-01.
+
+ boundary The calendar boundary between (era_month − 1) and era_month —
+ i.e. midnight on day 1 of the era. At a January era this IS
+ the year boundary, which is how Dec→Jan stitch and the date-
+ generator's `month == 1` buffer-wrap branch get exercised
+ without a separate flavour family.
+
+ flavour One of three window shapes (`at`, `before`, `into`) — see
+ below for the full definition of each.
+
+----------------------------------------------------------------------------
+Three flavours, anchored on T on day 1 of the era month:
+
+ - at [day1@T, day1@T + 1h]
+ Forward 1h from T. At T == 00:00 this exercises NEMOSIS's
+ buffer-back branch in `year_and_month_gen` (and buffer-wrap to
+ December of the prior year at January eras). At T == 04:00 /
+ 04:05 it's an intra-day stride/clip check on day 1.
+
+ - before [day1@T - 1h, day1@T]
+ Backward 1h ending at T. Mirror of `at`. At T == 00:00 the
+ window lands entirely in the previous month (1h before midnight
+ IS last-day@23:00) and ends exactly at the boundary. At higher
+ T values the window is intra-day on day 1.
+
+ - into [last-day@23:00, day1@T]
+ Variable-length window from the last hour of the previous day
+ into day 1 of the era month. The start is anchored on the
+ previous day regardless of T, so for T > 00:00 the window
+ genuinely straddles the boundary and ends at / just past the
+ market-day-end on day 1.
+
+Three time-of-day probe points: 00:00, 04:00, and 04:05 for 5-min-stride
+tables (or 04:30 for 30-min tables, since :05 isn't a valid stride point).
+
+----------------------------------------------------------------------------
+Internally this version trades brevity for explicitness:
+ * `boundary_cases` uses `itertools.product` so the matrix walk is one
+ flat loop instead of three nested ones.
+ * Each flavour has its own builder function that computes window and
+ expected values inline with plain arithmetic — no shared `pd.date_range`
+ helper to context-switch into. The per-stride math is duplicated three
+ times (once per flavour) on purpose; each instance is two lines.
+"""
+from __future__ import annotations
+
+import sys
+from dataclasses import dataclass
+from datetime import date, datetime, time, timedelta
+from itertools import product
+from pathlib import Path
+
+import pandas as pd
+
+_FIXTURES_DIR = Path(__file__).resolve().parent.parent / "fixtures"
+if str(_FIXTURES_DIR) not in sys.path:
+ sys.path.insert(0, str(_FIXTURES_DIR))
+import spec # noqa: E402
+
+
+# ===========================================================================
+# Public API
+# ===========================================================================
+
+def boundary_cases(table: str) -> list[BoundaryCase]:
+ """Generate every (era × flavour × T) case for `table`, based on the
+ eras listed in `spec.DYNAMIC_TABLES[table]["eras"]`.
+
+ Skipped:
+ * the `recent` scrape era (handled by separate scrape-pattern tests)
+ * tables in `_HELPER_DOES_NOT_COVER` (return empty list)
+ * `flavour=into, T=00:00` cases (identical to `flavour=before, T=00:00`)
+ """
+ if table in _HELPER_DOES_NOT_COVER:
+ return []
+
+ # Pre-compute the (era_key, era_date, stride, T) axis for this table.
+ # This is the only place where stride/era logic lives. Everything below
+ # this point treats it as opaque coordinates.
+ matrix = []
+ for era_key in spec.DYNAMIC_TABLES[table]["eras"]:
+ if era_key == "recent":
+ continue
+ era_date = spec.ERAS[era_key]
+ stride = stride_for(table, era_date)
+ for T in time_points_for_stride(stride):
+ matrix.append((era_key, era_date, stride, T))
+
+ # Cartesian product flattens the (era × T) axis against flavours, giving
+ # one explicit triple per case in a single flat loop. Per-flavour dispatch
+ # is an inline if-chain so the reader doesn't have to chase a dict lookup.
+ cases = []
+ for (era_key, era_date, stride, T), flavour in product(matrix, FLAVOURS):
+ if flavour == "into" and T == time(0, 0):
+ continue # duplicate of `before` at T=00:00
+
+ if flavour == "at":
+ case = _build_at_case(table, era_key, era_date, stride, T)
+ elif flavour == "before":
+ case = _build_before_case(table, era_key, era_date, stride, T)
+ else: # "into"
+ case = _build_into_case(table, era_key, era_date, stride, T)
+
+ cases.append(case)
+ return cases
+
+
+def assert_boundary_shape(
+ data: pd.DataFrame,
+ case: BoundaryCase,
+ *,
+ date_col: str,
+ entities_col: str,
+ expected_entities: set,
+) -> None:
+ """Assert `data` matches what `case` predicts. Reads top-down as a
+ checklist of properties the returned frame must have."""
+ # Dtype checks — guard against a regression where the datetime column
+ # comes back as object/string. `sorted()` and `==` on the timestamp
+ # list below happen to work on strings for AEMO's date format, so
+ # downstream breakage would be silent without this.
+ if not data.empty:
+ assert data[date_col].dtype == "datetime64[ns]", (
+ f"{case.id}: {date_col} dtype is {data[date_col].dtype}, "
+ f"expected datetime64[ns]"
+ )
+ assert data[entities_col].dtype == "object", (
+ f"{case.id}: {entities_col} dtype is {data[entities_col].dtype}, "
+ f"expected object"
+ )
+
+ _assert_entity_set_matches(data, case, entities_col, expected_entities)
+
+ for entity in sorted(expected_entities):
+ per_entity = data[data[entities_col] == entity].sort_values(date_col)
+ timestamps = per_entity[date_col].tolist()
+
+ _assert_row_count(timestamps, case, entity)
+ if case.expected_count == 0:
+ continue
+ _assert_first_and_last_timestamps(timestamps, case, entity)
+ _assert_no_duplicate_timestamps(timestamps, case, entity)
+ _assert_stride_is_regular(timestamps, case, entity)
+
+
+# ===========================================================================
+# Configuration constants
+# ===========================================================================
+
+FLAVOURS = ("at", "before", "into")
+
+
+# Tables this helper does NOT cover. Why each is excluded:
+#
+# BIDDAYOFFER_D, MNSP_DAYOFFER — daily stride, market-day-keyed rows
+# that don't fit the per-interval shape.
+# BIDPEROFFER_D — bids are partitioned by trading day
+# (04:05 → 04:00) not calendar day, so
+# a query in calendar 00:00–04:00 lands
+# in the previous trading-day archive
+# and the helper's row-count assumptions
+# don't hold.
+# MNSP_PEROFFER — multi-dimensional rows (PERIODID etc.)
+# per (LINKID, SETTLEMENTDATE) plus the
+# schema-drift bug (issue #68) caps it
+# at low coverage anyway.
+#
+# DISPATCHCONSTRAINT was previously excluded on the theory that a
+# constraint may not bind every interval. Probing every era fixture
+# showed the pinned CONSTRAINTID `DATASNAP_DFS_Q_CLST` binds all 288
+# intervals on day 1 across 2018–2025, so the matrix shape assumptions
+# DO hold for this specific fixture.
+_HELPER_DOES_NOT_COVER = {
+ "BIDDAYOFFER_D",
+ "MNSP_DAYOFFER",
+ "BIDPEROFFER_D",
+ "MNSP_PEROFFER",
+}
+
+
+# ===========================================================================
+# BoundaryCase data model
+# ===========================================================================
+
+@dataclass(frozen=True)
+class BoundaryCase:
+ """One concrete (era × flavour × T) test case for a given table.
+
+ Fields fall into three groups:
+ * inputs — which slot in the matrix this case represents
+ * query — the literal datetimes & stride passed to NEMOSIS
+ * expected — what the returned frame should look like *per entity*
+ (per region, per DUID, etc — whichever column the table
+ is filtered on)
+ """
+ # --- inputs ---
+ table: str
+ era_key: str
+ era_date: date
+ flavour: str
+ time_point: time
+
+ # --- query ---
+ start: datetime
+ end: datetime
+ stride_minutes: int
+
+ # --- expected per-entity frame ---
+ expected_count: int
+ expected_first: datetime
+ expected_last: datetime
+
+ @property
+ def id(self) -> str:
+ return f"{self.era_key}-T{self.time_point.strftime('%H%M')}-{self.flavour}"
+
+ @property
+ def start_str(self) -> str:
+ return self.start.strftime("%Y/%m/%d %H:%M:%S")
+
+ @property
+ def end_str(self) -> str:
+ return self.end.strftime("%Y/%m/%d %H:%M:%S")
+
+
+# ===========================================================================
+# Stride per (table, era)
+# ===========================================================================
+
+# AEMO's 5-Minute Settlement reform went live on 2021-10-01 — at that point
+# TRADINGPRICE & TRADINGINTERCONNECT switched from 30-min to 5-min rows.
+_TRADING_5MIN_FROM = date(2021, 10, 1)
+
+
+def stride_for(table: str, era_date: date) -> int:
+ """Return the per-row stride in minutes for `table` in `era_date`'s era."""
+ if table in ("TRADINGPRICE", "TRADINGINTERCONNECT"):
+ return 5 if era_date >= _TRADING_5MIN_FROM else 30
+ if table in ("TRADINGLOAD", "TRADINGREGIONSUM"):
+ return 30
+ if table == "ROOFTOP_PV_ACTUAL":
+ return 30
+ # All dispatch tables — BIDPEROFFER_D / MNSP_PEROFFER are skipped by the
+ # matrix (see `_HELPER_DOES_NOT_COVER`) so `stride_for` is never called
+ # for them.
+ return 5
+
+
+def time_points_for_stride(stride: int) -> list[time]:
+ """Three probe times per day. Midnight, market-day-end (04:00), and just
+ past it. The post-end probe is 04:05 for 5-min strides; for 30-min
+ tables we use 04:30 since :05 isn't a valid stride point there.
+
+ All returned T values must be stride-aligned — the flavour builders rely
+ on `window_minutes // stride` producing an exact count, which only holds
+ when the window endpoints sit on stride boundaries. Enforced here so a
+ future contributor adding an off-stride T value fails loud, not silent.
+ """
+ if stride == 30:
+ points = [time(0, 0), time(4, 0), time(4, 30)]
+ else:
+ points = [time(0, 0), time(4, 0), time(4, 5)]
+ for T in points:
+ assert (T.hour * 60 + T.minute) % stride == 0, (
+ f"time point {T} not aligned to {stride}-min stride"
+ )
+ return points
+
+
+# ===========================================================================
+# Per-flavour case builders
+# ===========================================================================
+# Each function builds one BoundaryCase from the matrix coordinates, computing
+# the window and expected first/last/count inline. Period-ending convention:
+# the first returned row sits at start + stride minutes (the first stride
+# boundary strictly after start); the last sits at end (which always lands
+# on a stride boundary in our matrix).
+
+def _build_at_case(
+ table: str, era_key: str, era_date: date, stride: int, T: time,
+) -> BoundaryCase:
+ """`at` flavour: 1h window starting at T on day 1 of the era month."""
+ start = datetime.combine(era_date, T)
+ end = start + timedelta(hours=1)
+ return BoundaryCase(
+ table=table, era_key=era_key, era_date=era_date,
+ flavour="at", time_point=T,
+ start=start, end=end, stride_minutes=stride,
+ expected_count=60 // stride,
+ expected_first=start + timedelta(minutes=stride),
+ expected_last=end,
+ )
+
+
+def _build_before_case(
+ table: str, era_key: str, era_date: date, stride: int, T: time,
+) -> BoundaryCase:
+ """`before` flavour: 1h window ending at T on day 1 of the era month."""
+ end = datetime.combine(era_date, T)
+ start = end - timedelta(hours=1)
+ return BoundaryCase(
+ table=table, era_key=era_key, era_date=era_date,
+ flavour="before", time_point=T,
+ start=start, end=end, stride_minutes=stride,
+ expected_count=60 // stride,
+ expected_first=start + timedelta(minutes=stride),
+ expected_last=end,
+ )
+
+
+def _build_into_case(
+ table: str, era_key: str, era_date: date, stride: int, T: time,
+) -> BoundaryCase:
+ """`into` flavour: window from last-day@23:00 of previous month to T on
+ day 1 of the era month. Window length varies with T:
+ T = 00:00 → 1h (= `before` at T=00:00; deduplicated upstream)
+ T = 04:00 → 5h
+ T = 04:05 → 5h 5min (5-min-stride tables)
+ T = 04:30 → 5h 30min (30-min-stride tables)
+ """
+ start = datetime.combine(era_date, time(0, 0)) - timedelta(hours=1) # last-day@23:00
+ end = datetime.combine(era_date, T)
+ window_minutes = int((end - start).total_seconds() // 60)
+ return BoundaryCase(
+ table=table, era_key=era_key, era_date=era_date,
+ flavour="into", time_point=T,
+ start=start, end=end, stride_minutes=stride,
+ expected_count=window_minutes // stride,
+ expected_first=start + timedelta(minutes=stride),
+ expected_last=end,
+ )
+
+
+# ===========================================================================
+# Assertion sub-checks
+# ===========================================================================
+
+def _assert_entity_set_matches(data, case, entities_col, expected):
+ actual = set(data[entities_col])
+ assert actual == expected, (
+ f"{case.id}: entity set {actual!r} != expected {expected!r}"
+ )
+
+
+def _assert_row_count(timestamps, case, entity):
+ assert len(timestamps) == case.expected_count, (
+ f"{case.id} [{entity}]: got {len(timestamps)} rows, "
+ f"expected {case.expected_count}"
+ )
+
+
+def _assert_first_and_last_timestamps(timestamps, case, entity):
+ assert timestamps[0] == case.expected_first, (
+ f"{case.id} [{entity}]: first {timestamps[0]} != "
+ f"expected {case.expected_first}"
+ )
+ assert timestamps[-1] == case.expected_last, (
+ f"{case.id} [{entity}]: last {timestamps[-1]} != "
+ f"expected {case.expected_last}"
+ )
+
+
+def _assert_no_duplicate_timestamps(timestamps, case, entity):
+ assert len(set(timestamps)) == len(timestamps), (
+ f"{case.id} [{entity}]: duplicate timestamps in returned frame"
+ )
+
+
+def _assert_stride_is_regular(timestamps, case, entity):
+ """Every consecutive pair of timestamps should differ by exactly
+ `case.stride_minutes` — no gaps, no irregular spacing. A case with
+ fewer than 2 timestamps can't probe stride, so treat it as a
+ matrix-construction error rather than silently passing."""
+ assert case.expected_count >= 2, (
+ f"{case.id}: stride regularity needs >=2 rows, got expected_count="
+ f"{case.expected_count} — case builder is dropping a stride point"
+ )
+ consecutive_diffs = {b - a for a, b in zip(timestamps, timestamps[1:])}
+ expected = timedelta(minutes=case.stride_minutes)
+ assert consecutive_diffs == {expected}, (
+ f"{case.id} [{entity}]: irregular stride {consecutive_diffs}, "
+ f"expected single {expected}"
+ )
diff --git a/tests/end_to_end_table_tests/_boundaries_deprecated.py b/tests/end_to_end_table_tests/_boundaries_deprecated.py
new file mode 100644
index 0000000..9606f09
--- /dev/null
+++ b/tests/end_to_end_table_tests/_boundaries_deprecated.py
@@ -0,0 +1,366 @@
+"""Boundary-test helpers shared across per-table test files.
+
+Generates a parametrised matrix of (era × flavour × time-of-day) cases for
+each dynamic time-series table, and provides an assertion helper that
+checks shape, count, first/last timestamps, and absence of gaps or
+duplicates across the boundary.
+
+Three flavours, each anchored on T (a time-of-day) on day 1 of the era month:
+
+ - at [day1@T, day1@T + 1h]
+ Forward 1h from T. At T == 00:00 this exercises NEMOSIS's
+ buffer-back branch in `year_and_month_gen` (and buffer-wrap to
+ December of the prior year at January eras). At T == 04:00 /
+ 04:05 it's an intra-day stride/clip check on day 1.
+
+ - before [day1@T - 1h, day1@T]
+ Backward 1h ending at T. Mirror of `at`. At T == 00:00 the
+ window lands entirely in the previous month (1h before midnight
+ IS last-day@23:00) and ends exactly at the boundary. At higher
+ T values the window is intra-day on day 1.
+
+ - into [last-day@23:00, day1@T]
+ Variable-length window from the last hour of the previous day
+ into day 1 of the era month. The start is anchored on the
+ previous day regardless of T, so for T > 00:00 the window
+ genuinely straddles the boundary and ends at / just past the
+ market-day-end on day 1.
+
+Time-of-day axis: three probe points. Calendar midnight (00:00), market-day
+end (04:00), and just past market-day end (04:05 for 5-min-stride tables;
+04:30 for 30-min-stride tables since :05 isn't a valid stride point there).
+Daily-stride bid tables (BIDDAYOFFER_D, MNSP_DAYOFFER) aren't handled here
+— they have different row-count semantics and live in their own per-table
+tests.
+
+Year-wrap coverage falls out for free: the boundary at a January era IS the
+year boundary, so flavours at eras 2020-01, 2022-01, and 2025-01 exercise
+the Dec→Jan stitch and the date-generator's `month == 1` buffer-wrap branch
+without needing a separate flavour family.
+
+----------------------------------------------------------------------------
+Worked example — what `boundary_cases("DISPATCHPRICE")` generates at the
+2020-01 era (one of six eras for that table):
+
+ flavour=at T=00:00 query [2020-01-01 00:00, 2020-01-01 01:00]
+ 12 rows; buffer-back triggered → fetches Dec 2019
+ flavour=at T=04:00 query [2020-01-01 04:00, 2020-01-01 05:00]
+ 12 rows; intra-day on Jan 1
+ flavour=at T=04:05 query [2020-01-01 04:05, 2020-01-01 05:05]
+ 12 rows; intra-day, off-stride start
+ flavour=before T=00:00 query [2019-12-31 23:00, 2020-01-01 00:00]
+ 12 rows; entirely Dec, ends at boundary
+ flavour=before T=04:00 query [2020-01-01 03:00, 2020-01-01 04:00]
+ 12 rows; intra-day on Jan 1
+ flavour=before T=04:05 query [2020-01-01 03:05, 2020-01-01 04:05]
+ 12 rows; intra-day, off-stride start
+ flavour=into T=04:00 query [2019-12-31 23:00, 2020-01-01 04:00]
+ 60 rows; straddles year boundary
+ flavour=into T=04:05 query [2019-12-31 23:00, 2020-01-01 04:05]
+ 61 rows; straddles year boundary
+
+ (`into` at T=00:00 is identical to `before` at T=00:00 — skipped.)
+
+ 8 cases per era × 6 eras = 48 cases for DISPATCHPRICE.
+"""
+from __future__ import annotations
+
+import sys
+from dataclasses import dataclass
+from datetime import date, datetime, time, timedelta
+from pathlib import Path
+
+import pandas as pd
+
+_FIXTURES_DIR = Path(__file__).resolve().parent.parent / "fixtures"
+if str(_FIXTURES_DIR) not in sys.path:
+ sys.path.insert(0, str(_FIXTURES_DIR))
+import spec # noqa: E402
+
+
+# ===========================================================================
+# Stride per (table, era)
+# ===========================================================================
+
+# TRADINGPRICE & TRADINGINTERCONNECT switched 30-min → 5-min when AEMO's
+# 5-Minute Settlement reform went live on 2021-10-01.
+_TRADING_5MIN_FROM = date(2021, 10, 1)
+
+
+def stride_for(table: str, era_date: date) -> int:
+ """Return the per-row stride in minutes for `table` in `era_date`'s era."""
+ if table in ("TRADINGPRICE", "TRADINGINTERCONNECT"):
+ return 5 if era_date >= _TRADING_5MIN_FROM else 30
+ if table in ("TRADINGLOAD", "TRADINGREGIONSUM"):
+ return 30
+ if table == "ROOFTOP_PV_ACTUAL":
+ return 30
+ # All dispatch tables, BIDPEROFFER_D, MNSP_PEROFFER.
+ return 5
+
+
+# Tables this helper does NOT cover (boundary tests live in the per-table
+# files instead, or are skipped entirely):
+#
+# BIDDAYOFFER_D, MNSP_DAYOFFER — daily stride, market-day-keyed rows
+# that don't fit the per-interval shape
+# BIDPEROFFER_D — bids are partitioned by trading day
+# (04:05 → 04:00) not calendar day, so
+# a query in calendar 00:00–04:00 lands
+# in the previous trading-day archive
+# and the helper's row-count assumptions
+# don't hold
+# MNSP_PEROFFER — multi-dimensional rows (PERIODID etc.)
+# per (LINKID, SETTLEMENTDATE) plus the
+# schema-drift bug (issue #68) caps it
+# at low coverage anyway
+# DISPATCHCONSTRAINT — a constraint isn't binding every
+# interval, so per-interval row counts
+# are non-deterministic
+_HELPER_DOES_NOT_COVER = {
+ "BIDDAYOFFER_D",
+ "MNSP_DAYOFFER",
+ "BIDPEROFFER_D",
+ "MNSP_PEROFFER",
+ "DISPATCHCONSTRAINT",
+}
+
+
+def _time_points_for_stride(stride: int) -> list[time]:
+ """Three probe times per day. Midnight, market-day-end (04:00), and just
+ past it. The post-end probe is 04:05 for 5-min strides; for 30-min
+ tables we use 04:30 since :05 isn't a valid stride point there."""
+ if stride == 30:
+ return [time(0, 0), time(4, 0), time(4, 30)]
+ return [time(0, 0), time(4, 0), time(4, 5)]
+
+
+# ===========================================================================
+# Window construction — one explicit function per flavour
+# ===========================================================================
+
+def _at_window(era_date: date, T: time) -> tuple[datetime, datetime]:
+ """`at` flavour: forward 1h from T on day 1 of the era month."""
+ anchor = datetime.combine(era_date, T)
+ return anchor, anchor + timedelta(hours=1)
+
+
+def _before_window(era_date: date, T: time) -> tuple[datetime, datetime]:
+ """`before` flavour: backward 1h ending at T on day 1 of the era month."""
+ anchor = datetime.combine(era_date, T)
+ return anchor - timedelta(hours=1), anchor
+
+
+def _into_window(era_date: date, T: time) -> tuple[datetime, datetime]:
+ """`into` flavour: from last-day@23:00 to T on day 1 of the era month."""
+ last_day_23 = datetime.combine(era_date, time(0, 0)) - timedelta(hours=1)
+ anchor = datetime.combine(era_date, T)
+ return last_day_23, anchor
+
+
+_FLAVOUR_BUILDERS = {
+ "at": _at_window,
+ "before": _before_window,
+ "into": _into_window,
+}
+
+
+# ===========================================================================
+# Case generation
+# ===========================================================================
+
+@dataclass(frozen=True)
+class BoundaryCase:
+ """One concrete (era × flavour × T) test case for a given table.
+
+ Fields fall into three groups:
+ * inputs — which slot in the matrix this case represents
+ * query — the literal datetimes & stride passed to NEMOSIS
+ * expected — what the returned frame should look like *per entity*
+ (per region, per DUID, etc — whichever column the table
+ is filtered on)
+ """
+ # --- inputs ---
+ table: str
+ era_key: str
+ era_date: date
+ flavour: str # "at" | "before" | "into"
+ time_point: time
+
+ # --- query ---
+ start: datetime
+ end: datetime
+ stride_minutes: int
+
+ # --- expected per-entity frame ---
+ expected_count: int
+ expected_first: datetime
+ expected_last: datetime
+
+ @property
+ def id(self) -> str:
+ return f"{self.era_key}-T{self.time_point.strftime('%H%M')}-{self.flavour}"
+
+ @property
+ def start_str(self) -> str:
+ return self.start.strftime("%Y/%m/%d %H:%M:%S")
+
+ @property
+ def end_str(self) -> str:
+ return self.end.strftime("%Y/%m/%d %H:%M:%S")
+
+
+def boundary_cases(table: str) -> list[BoundaryCase]:
+ """Generate every (era × flavour × T) case for `table`, based on the
+ eras listed in `spec.DYNAMIC_TABLES[table]["eras"]`.
+
+ Skipped:
+ * the `recent` scrape era (handled by separate scrape-pattern tests)
+ * daily-stride bid tables (see `_DAILY_TABLES`)
+ * `flavour=into, T=00:00` cases (identical to `flavour=before, T=00:00`)
+ """
+ if table in _HELPER_DOES_NOT_COVER:
+ return []
+
+ eras = spec.DYNAMIC_TABLES[table]["eras"]
+ cases: list[BoundaryCase] = []
+
+ for era_key in eras:
+ if era_key == "recent":
+ continue
+ era_date = spec.ERAS[era_key]
+ stride = stride_for(table, era_date)
+
+ for T in _time_points_for_stride(stride):
+ for flavour, build_window in _FLAVOUR_BUILDERS.items():
+ if _is_duplicate_into_at_midnight(flavour, T):
+ continue
+ start, end = build_window(era_date, T)
+ first, last, n = _expected_period_endings(start, end, stride)
+ cases.append(BoundaryCase(
+ table=table,
+ era_key=era_key,
+ era_date=era_date,
+ flavour=flavour,
+ time_point=T,
+ start=start,
+ end=end,
+ stride_minutes=stride,
+ expected_count=n,
+ expected_first=first,
+ expected_last=last,
+ ))
+ return cases
+
+
+def _is_duplicate_into_at_midnight(flavour: str, T: time) -> bool:
+ """At T == 00:00, `into` and `before` produce the same window
+ ([last-day@23:00, day1@00:00]) — `into`'s start (last-day@23:00) IS
+ `before`'s start (anchor − 1h, where anchor = midnight). Skip the
+ duplicate so the matrix doesn't double-count it."""
+ return flavour == "into" and T == time(0, 0)
+
+
+def _expected_period_endings(
+ start: datetime, end: datetime, stride: int,
+) -> tuple[datetime, datetime, int]:
+ """Compute the period-ending timestamps NEMOSIS should return for a
+ query [start, end] on a `stride`-minute table.
+
+ AEMO settlement timestamps are period-ending: a row labelled 04:05
+ represents the interval 04:00–04:05. For a query starting at 04:00,
+ the first matching row is 04:05; for a query starting at 04:05, the
+ first matching row is 04:10 (because 04:05 is at-or-before the start,
+ not after it).
+
+ We model that with a pandas date_range over [start, end] at the table's
+ stride and discard any timestamp equal to `start`. Assumes `start` and
+ `end` are both stride-aligned, which holds for every case in our matrix
+ (T values are always 00:00, 04:00, 04:05, or 04:30).
+
+ Returns (first_timestamp, last_timestamp, count). When count == 0
+ (window narrower than one stride), first/last are returned as `start`
+ as a harmless placeholder — assertion code skips first/last checks
+ when count is zero.
+ """
+ all_points = pd.date_range(start=start, end=end, freq=f"{stride}min")
+ period_endings = all_points[all_points > pd.Timestamp(start)]
+ if len(period_endings) == 0:
+ return start, start, 0
+ return (
+ period_endings[0].to_pydatetime(),
+ period_endings[-1].to_pydatetime(),
+ len(period_endings),
+ )
+
+
+# ===========================================================================
+# Assertion — split into named sub-checks
+# ===========================================================================
+
+def assert_boundary_shape(
+ data: pd.DataFrame,
+ case: BoundaryCase,
+ *,
+ date_col: str,
+ entities_col: str,
+ expected_entities: set,
+) -> None:
+ """Assert `data` matches what `case` predicts. Reads top-down as a
+ checklist of properties the returned frame must have."""
+ _assert_entity_set_matches(data, case, entities_col, expected_entities)
+
+ for entity in sorted(expected_entities):
+ per_entity = data[data[entities_col] == entity].sort_values(date_col)
+ timestamps = per_entity[date_col].tolist()
+
+ _assert_row_count(timestamps, case, entity)
+ if case.expected_count == 0:
+ continue
+ _assert_first_and_last_timestamps(timestamps, case, entity)
+ _assert_no_duplicate_timestamps(timestamps, case, entity)
+ _assert_stride_is_regular(timestamps, case, entity)
+
+
+def _assert_entity_set_matches(data, case, entities_col, expected):
+ actual = set(data[entities_col])
+ assert actual == expected, (
+ f"{case.id}: entity set {actual!r} != expected {expected!r}"
+ )
+
+
+def _assert_row_count(timestamps, case, entity):
+ assert len(timestamps) == case.expected_count, (
+ f"{case.id} [{entity}]: got {len(timestamps)} rows, "
+ f"expected {case.expected_count}"
+ )
+
+
+def _assert_first_and_last_timestamps(timestamps, case, entity):
+ assert timestamps[0] == case.expected_first, (
+ f"{case.id} [{entity}]: first {timestamps[0]} != "
+ f"expected {case.expected_first}"
+ )
+ assert timestamps[-1] == case.expected_last, (
+ f"{case.id} [{entity}]: last {timestamps[-1]} != "
+ f"expected {case.expected_last}"
+ )
+
+
+def _assert_no_duplicate_timestamps(timestamps, case, entity):
+ assert len(set(timestamps)) == len(timestamps), (
+ f"{case.id} [{entity}]: duplicate timestamps in returned frame"
+ )
+
+
+def _assert_stride_is_regular(timestamps, case, entity):
+ """Every consecutive pair of timestamps should differ by exactly
+ `case.stride_minutes` — no gaps, no irregular spacing."""
+ consecutive_diffs = {b - a for a, b in zip(timestamps, timestamps[1:])}
+ if not consecutive_diffs:
+ return # only one row — nothing to compare
+ expected = timedelta(minutes=case.stride_minutes)
+ assert consecutive_diffs == {expected}, (
+ f"{case.id} [{entity}]: irregular stride {consecutive_diffs}, "
+ f"expected single {expected}"
+ )
diff --git a/tests/end_to_end_table_tests/test_bid_day_offer_d.py b/tests/end_to_end_table_tests/test_bid_day_offer_d.py
new file mode 100644
index 0000000..da55a76
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_bid_day_offer_d.py
@@ -0,0 +1,45 @@
+"""Tests for BIDDAYOFFER_D.
+
+Per-unit daily bid offers (price bands, daily energy constraint,
+rebid explanations). MMS monthly archive at every era — the archive
+just has a hole between March 2021 and July 2024 that we skip over.
+
+Fixtured eras: 2018-05, 2021-02, 2024-09; DUID ∈ {AGLHAL, HDWF2}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+
+EXPECTED_BIDTYPES = {
+ "ENERGY", "RAISEREG", "LOWERREG",
+ "RAISE5MIN", "RAISE60SEC", "LOWER5MIN", "LOWER60SEC",
+}
+
+
+@pytest.mark.parametrize("era_start,prev_day", [
+ ("2018/05/01 00:00:00", "2018-04-30"),
+ ("2021/02/01 00:00:00", "2021-01-31"),
+ ("2024/09/01 00:00:00", "2024-08-31"),
+])
+def test_day_offers_at_calendar_boundary(nemosis_fixture, era_start, prev_day):
+ """At calendar midnight of day 1, NEMOSIS returns the previous trading
+ day's bids (trading-day convention — day 1's bid doesn't start until
+ 04:05). AGLHAL is energy-only; HDWF2 bids every FCAS service as well."""
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="BIDDAYOFFER_D",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "BIDTYPE"],
+ )
+
+ assert len(data) == 8
+ assert set(data["DUID"]) == {"AGLHAL", "HDWF2"}
+ assert set(data["SETTLEMENTDATE"]) == {pd.Timestamp(prev_day)}
+ assert set(data[data["DUID"] == "AGLHAL"]["BIDTYPE"]) == {"ENERGY"}
+ assert set(data[data["DUID"] == "HDWF2"]["BIDTYPE"]) == EXPECTED_BIDTYPES
diff --git a/tests/end_to_end_table_tests/test_bid_per_offer_d.py b/tests/end_to_end_table_tests/test_bid_per_offer_d.py
new file mode 100644
index 0000000..d6ec302
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_bid_per_offer_d.py
@@ -0,0 +1,68 @@
+"""Tests for BIDPEROFFER_D.
+
+Per-unit per-five-minute bid offers (up to 10 price bands). The MMS
+monthly archive is the source at every era NEMOSIS supports — AEMO
+stopped publishing these files between March 2021 and July 2024, so
+coverage jumps over that gap.
+
+Boundary tests are NOT generated for this table — bids are partitioned
+by AEMO trading day (04:05 → 04:00 next day), not calendar day, so
+calendar-aligned queries between 00:00 and 04:00 fall in the previous
+trading-day archive and our boundary helper's row-count assertions
+break. See `_boundaries.py::_HELPER_DOES_NOT_COVER` for the full list
+of skipped tables.
+
+DUID ∈ {AGLHAL, HDWF2}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+FIXTURED_DUIDS = {"AGLHAL", "HDWF2"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/02/01 00:00:00",
+ "2024/09/01 00:00:00",
+])
+def test_trading_day_buffer_back_at_start_of_month(nemosis_fixture, era_start):
+ """A calendar-midnight query on day 1 must reach into the prev-month
+ archive for the prior trading day's 00:05 → 04:00 rows and stitch them
+ to day 1's 04:05 → onward rows. The [day1 00:00, day1 05:15] window is
+ the canonical shape that exercises this — the buffer-back only fires
+ when start_time is exactly first-of-month midnight (see issue #70)."""
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=5, minutes=15)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="BIDPEROFFER_D",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERVAL_DATETIME", "DUID", "BIDTYPE", "MAXAVAIL"],
+ filter_cols=["DUID", "BIDTYPE"],
+ filter_values=(["AGLHAL"], ["ENERGY"]),
+ )
+
+ timestamps = sorted(data["INTERVAL_DATETIME"].tolist())
+ assert len(timestamps) == 63 # 5h15min / 5min
+ assert timestamps[0] == start + pd.Timedelta(minutes=5) # 00:05
+ assert timestamps[-1] == start + pd.Timedelta(hours=5, minutes=15) # 05:15
+ diffs = {b - a for a, b in zip(timestamps, timestamps[1:])}
+ assert diffs == {pd.Timedelta(minutes=5)}
+
+
+def test_bids_returned_for_fixtured_duids(nemosis_fixture):
+ """Smoke: both DUIDs present in a simple 1h window."""
+ data = dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 01:00:00",
+ table_name="BIDPEROFFER_D",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERVAL_DATETIME", "DUID", "BIDTYPE", "MAXAVAIL"],
+ )
+
+ assert not data.empty
+ assert set(data["DUID"]) <= FIXTURED_DUIDS
diff --git a/tests/end_to_end_table_tests/test_cache_compiler.py b/tests/end_to_end_table_tests/test_cache_compiler.py
new file mode 100644
index 0000000..ee715d0
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_cache_compiler.py
@@ -0,0 +1,59 @@
+"""Tests for `cache_compiler` — the public sibling of `dynamic_data_compiler`
+that writes typed feather / parquet files to disk for downstream consumers.
+
+Verifies (a) the round-trip through cache → dynamic_data_compiler preserves
+typed columns and (b) `select_columns` narrows the cached file itself, not
+just the returned frame.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import cache_compiler, dynamic_data_compiler
+
+
+START = "2018/05/01 00:00:00"
+END = "2018/05/01 00:30:00"
+
+
+@pytest.mark.parametrize("fformat", ["feather", "parquet"])
+def test_round_trip_preserves_dtypes(nemosis_fixture, fformat):
+ cache_compiler(
+ start_time=START, end_time=END,
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ fformat=fformat,
+ )
+ data = dynamic_data_compiler(
+ start_time=START, end_time=END,
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ fformat=fformat,
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP", "INTERVENTION"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+ assert not data.empty
+ assert data["SETTLEMENTDATE"].dtype == "datetime64[ns]"
+ assert data["REGIONID"].dtype == "object"
+ # If all dtypes were object, typing failed somewhere in the pipeline.
+ assert not all(data.dtypes == "object")
+
+
+def test_select_columns_narrows_cached_file(nemosis_fixture):
+ """`cache_compiler` with `select_columns` should write only those
+ columns to the cached file, not just filter on read."""
+ cache_compiler(
+ start_time=START, end_time=END,
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ fformat="parquet",
+ select_columns=["SETTLEMENTDATE", "REGIONID"],
+ rebuild=True,
+ )
+ # Cache writes one parquet per fetched month — NEMOSIS's prev-month
+ # buffer means day-1 midnight queries always pull April too.
+ parquet_files = list(nemosis_fixture.glob("*.parquet"))
+ assert len(parquet_files) >= 1, parquet_files
+ for path in parquet_files:
+ on_disk = pd.read_parquet(path)
+ assert set(on_disk.columns) == {"SETTLEMENTDATE", "REGIONID"}, path
diff --git a/tests/end_to_end_table_tests/test_daily_region_summary.py b/tests/end_to_end_table_tests/test_daily_region_summary.py
new file mode 100644
index 0000000..d23fa54
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_daily_region_summary.py
@@ -0,0 +1,68 @@
+"""Tests for DAILY_REGION_SUMMARY.
+
+Scrape-pattern table: NEMOSIS fetches AEMO's rolling current-data index at
+/Reports/Current/Daily_Reports/, finds the filename matching a date stub,
+downloads the zip, and extracts the DAILY_REGION_SUMMARY section from the
+multi-table PUBLIC_DAILY file.
+
+The fixture freezes one recent day (see ERAS["recent"] in spec.py) plus
+the prior day (NEMOSIS's 1-day buffer). Unrelated sections of PUBLIC_DAILY
+have been stripped to keep the fixture small.
+"""
+from nemosis import dynamic_data_compiler
+
+
+import pandas as pd
+
+
+def test_recent_day_returns_rows_for_filtered_regions(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 00:00:00",
+ end_time="2026/03/15 01:00:00",
+ table_name="DAILY_REGION_SUMMARY",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "TOTALDEMAND"],
+ )
+
+ assert not data.empty
+ assert set(data["REGIONID"]) == {"SA1", "NSW1"}
+
+
+# ---------------------------------------------------------------------------
+# Market-day boundary at 04:00. Daily files cover `{date} 04:05 → {date+1}
+# 04:00` — so the 04:00 row lives in the *previous* daily file. NEMOSIS's
+# `current_gen` always subtracts 1 day (unconditional buffer-back), so a
+# query ending at 04:00 must still pull the prior day's file to retrieve
+# that final row.
+# ---------------------------------------------------------------------------
+
+def test_end_market_day_returns_0400_row_from_previous_file(nemosis_fixture):
+ """[03:55, 04:00]: filter is start-exclusive end-inclusive, so only
+ the 04:00 row qualifies — and it lives in the `20260314` daily file,
+ not `20260315`. Non-empty proves buffer-back fired correctly."""
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 03:55:00",
+ end_time="2026/03/15 04:00:00",
+ table_name="DAILY_REGION_SUMMARY",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "TOTALDEMAND"],
+ )
+ assert set(data["SETTLEMENTDATE"].unique()) == {pd.Timestamp("2026-03-15 04:00:00")}
+ assert set(data["REGIONID"]) == {"SA1", "NSW1"}
+ assert not data.duplicated(["SETTLEMENTDATE", "REGIONID"]).any()
+
+
+def test_start_market_day_returns_0405_row_from_current_file(nemosis_fixture):
+ """[04:00, 04:05]: start-exclusive means 04:00 is dropped; 04:05 is
+ the first row of the `20260315` daily file. Tests the stitch from
+ the consumer side — the current day's file must be fetched."""
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 04:00:00",
+ end_time="2026/03/15 04:05:00",
+ table_name="DAILY_REGION_SUMMARY",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "TOTALDEMAND"],
+ )
+ assert set(data["SETTLEMENTDATE"].unique()) == {pd.Timestamp("2026-03-15 04:05:00")}
+ assert set(data["REGIONID"]) == {"SA1", "NSW1"}
+ assert not data.duplicated(["SETTLEMENTDATE", "REGIONID"]).any()
diff --git a/tests/end_to_end_table_tests/test_dispatch_constraint.py b/tests/end_to_end_table_tests/test_dispatch_constraint.py
new file mode 100644
index 0000000..a7f66f1
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dispatch_constraint.py
@@ -0,0 +1,64 @@
+"""Tests for DISPATCHCONSTRAINT.
+
+Per-constraint five-minute state (RHS, marginal value, LHS terms). The
+pinned CONSTRAINTID `DATASNAP_DFS_Q_CLST` binds every 5-min interval on
+day 1 across every fixtured era (verified empirically 2018-05 → 2025-01),
+so the standard boundary matrix applies here.
+
+Fixtured eras: 2018-05, 2020-01, 2021-05, 2022-01, 2024-08, 2025-01;
+CONSTRAINTID ∈ {DATASNAP_DFS_Q_CLST}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_CONSTRAINTS = {"DATASNAP_DFS_Q_CLST"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_constraint_rows_are_within_requested_window(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=2)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="DISPATCHCONSTRAINT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "CONSTRAINTID", "INTERVENTION", "RHS"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert set(data["CONSTRAINTID"]) == FIXTURED_CONSTRAINTS
+ assert data["SETTLEMENTDATE"].min() >= start
+ assert data["SETTLEMENTDATE"].max() <= end
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("DISPATCHCONSTRAINT"), ids=lambda c: c.id
+)
+def test_dispatch_constraint_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="DISPATCHCONSTRAINT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "CONSTRAINTID", "INTERVENTION", "RHS"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="CONSTRAINTID",
+ expected_entities=FIXTURED_CONSTRAINTS,
+ )
diff --git a/tests/end_to_end_table_tests/test_dispatch_interconnectorres.py b/tests/end_to_end_table_tests/test_dispatch_interconnectorres.py
new file mode 100644
index 0000000..73c702b
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dispatch_interconnectorres.py
@@ -0,0 +1,66 @@
+"""Tests for DISPATCHINTERCONNECTORRES.
+
+Per-interconnector five-minute flow data (MW, losses, constraints).
+Has INTERVENTION column like other dispatch tables.
+
+Boundary matrix (`test_dispatch_interconnectorres_boundary`) is
+auto-generated from `spec.DYNAMIC_TABLES["DISPATCHINTERCONNECTORRES"]
+["eras"]` by `_boundaries.py`.
+
+INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_INTERCONNECTORS = {"VIC1-NSW1"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_one_hour_gives_twelve_intervals_per_interconnector(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+ expected = [start + pd.Timedelta(minutes=5 * i) for i in range(1, 13)]
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="DISPATCHINTERCONNECTORRES",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "INTERCONNECTORID", "INTERVENTION", "METEREDMWFLOW"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert set(data["INTERCONNECTORID"]) == FIXTURED_INTERCONNECTORS
+ timestamps = sorted(data["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("DISPATCHINTERCONNECTORRES"), ids=lambda c: c.id
+)
+def test_dispatch_interconnectorres_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="DISPATCHINTERCONNECTORRES",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "INTERCONNECTORID", "INTERVENTION", "METEREDMWFLOW"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="INTERCONNECTORID",
+ expected_entities=FIXTURED_INTERCONNECTORS,
+ )
diff --git a/tests/end_to_end_table_tests/test_dispatch_load.py b/tests/end_to_end_table_tests/test_dispatch_load.py
new file mode 100644
index 0000000..a920c48
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dispatch_load.py
@@ -0,0 +1,82 @@
+"""Tests for the DISPATCHLOAD dispatch table.
+
+DISPATCHLOAD publishes one row per dispatchable unit (DUID) per five-minute
+interval, giving per-unit dispatch targets, ramp rates, availability, etc.
+Like DISPATCHPRICE it has both a "normal" and an "intervention" run per
+interval; the tests filter INTERVENTION == 0 to pin the normal run.
+
+Boundary matrix (`test_dispatch_load_boundary`) is auto-generated from
+`spec.DYNAMIC_TABLES["DISPATCHLOAD"]["eras"]` by `_boundaries.py`.
+
+The fixture pre-filters rows to DUID ∈ {AGLHAL, HDWF2}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_DUIDS = {"AGLHAL", "HDWF2"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_one_hour_gives_twelve_intervals_per_duid(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+ expected = [start + pd.Timedelta(minutes=5 * i) for i in range(1, 13)]
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="DISPATCHLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INTERVENTION", "INITIALMW"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert set(data["DUID"]) == FIXTURED_DUIDS
+ for duid in FIXTURED_DUIDS:
+ timestamps = sorted(data[data["DUID"] == duid]["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+def test_duid_filter_narrows_to_one_unit(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 01:00:00",
+ table_name="DISPATCHLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INITIALMW"],
+ filter_cols=["DUID"],
+ filter_values=(["AGLHAL"],),
+ )
+
+ assert set(data["DUID"]) == {"AGLHAL"}
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("DISPATCHLOAD"), ids=lambda c: c.id
+)
+def test_dispatch_load_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="DISPATCHLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INTERVENTION", "INITIALMW"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="DUID",
+ expected_entities=FIXTURED_DUIDS,
+ )
diff --git a/tests/end_to_end_table_tests/test_dispatch_price.py b/tests/end_to_end_table_tests/test_dispatch_price.py
new file mode 100644
index 0000000..f9b8a0e
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dispatch_price.py
@@ -0,0 +1,138 @@
+"""Tests for the DISPATCHPRICE dispatch table.
+
+DISPATCHPRICE publishes one row per region per five-minute dispatch interval.
+Settlement timestamps are period-ending, so a request for 00:00–01:00 returns
+the twelve intervals labelled 00:05 through 01:00.
+
+The fixture pre-filters rows to REGIONID ∈ {SA1, NSW1} and covers six AEMO
+eras:
+ 2018-05 — pre-5-min-trading baseline
+ 2020-01 — year boundary, pre-5-min format
+ 2021-05 — first month of the daily BIDMOVE_COMPLETE bidding layout
+ 2022-01 — year boundary, 5-min dispatch
+ 2024-08 — PUBLIC_DVD → PUBLIC_ARCHIVE# filename cutover
+ 2025-01 — year boundary, PUBLIC_ARCHIVE# format
+
+The boundary matrix (`test_dispatch_price_boundary`) is auto-generated from
+`spec.DYNAMIC_TABLES["DISPATCHPRICE"]["eras"]` by
+`tests/end_to_end_table_tests/_boundaries.py` — see that module for flavour
+and time-of-day semantics.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_REGIONS = {"SA1", "NSW1"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_one_hour_gives_twelve_intervals_per_region(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+ expected = [start + pd.Timedelta(minutes=5 * i) for i in range(1, 13)]
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP", "INTERVENTION"],
+ filter_cols=["INTERVENTION"], # exclude intervention dispatch runs,
+ filter_values=([0],), # which happen sporadically
+ )
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ for region in FIXTURED_REGIONS:
+ timestamps = sorted(data[data["REGIONID"] == region]["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+def test_region_filter_narrows_to_one_region(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 01:00:00",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ filter_cols=["REGIONID"],
+ filter_values=(["SA1"],),
+ )
+
+ assert set(data["REGIONID"]) == {"SA1"}
+
+
+def test_select_columns_returns_only_those_columns(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 00:30:00",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "RRP"],
+ )
+
+ assert list(data.columns) == ["SETTLEMENTDATE", "RRP"]
+
+
+def test_multi_day_cross_month_query(nemosis_fixture):
+ """3-day window straddling Aug→Sep 2024 exercises `year_and_month_gen`'s
+ multi-month iteration (which the boundary matrix's <=5.5h windows
+ don't reach). 3 days × 288 intervals = 864 timestamps per region."""
+ data = dynamic_data_compiler(
+ start_time="2024/08/30 00:00:00",
+ end_time="2024/09/02 00:00:00",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP", "INTERVENTION"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ for region in FIXTURED_REGIONS:
+ timestamps = sorted(data[data["REGIONID"] == region]["SETTLEMENTDATE"].tolist())
+ assert len(timestamps) == 864
+ assert timestamps[0] == pd.Timestamp("2024-08-30 00:05:00")
+ assert timestamps[-1] == pd.Timestamp("2024-09-02 00:00:00")
+
+
+def test_sub_stride_window_returns_empty(nemosis_fixture):
+ """30-second window falls between 5-min stride points — should return
+ an empty frame (not error), confirming the filter layer handles
+ zero-row cases without surfacing an exception."""
+ data = dynamic_data_compiler(
+ start_time="2024/08/01 00:30:00",
+ end_time="2024/08/01 00:30:30",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ )
+ assert data.empty
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("DISPATCHPRICE"), ids=lambda c: c.id
+)
+def test_dispatch_price_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP", "INTERVENTION"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="REGIONID",
+ expected_entities=FIXTURED_REGIONS,
+ )
diff --git a/tests/end_to_end_table_tests/test_dispatch_region_sum.py b/tests/end_to_end_table_tests/test_dispatch_region_sum.py
new file mode 100644
index 0000000..86bfb46
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dispatch_region_sum.py
@@ -0,0 +1,68 @@
+"""Tests for DISPATCHREGIONSUM.
+
+Per-region five-minute summary: total demand, cleared generation, FCAS
+enablements, etc. Has INTERVENTION column like DISPATCHPRICE, so tests
+filter INTERVENTION == 0 to pin the normal-run timeline.
+
+Boundary matrix (`test_dispatch_region_sum_boundary`) is auto-generated
+from `spec.DYNAMIC_TABLES["DISPATCHREGIONSUM"]["eras"]` by
+`_boundaries.py`.
+
+REGIONID ∈ {SA1, NSW1}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_REGIONS = {"SA1", "NSW1"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_one_hour_gives_twelve_intervals_per_region(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+ expected = [start + pd.Timedelta(minutes=5 * i) for i in range(1, 13)]
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="DISPATCHREGIONSUM",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "INTERVENTION", "TOTALDEMAND"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ for region in FIXTURED_REGIONS:
+ timestamps = sorted(data[data["REGIONID"] == region]["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("DISPATCHREGIONSUM"), ids=lambda c: c.id
+)
+def test_dispatch_region_sum_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="DISPATCHREGIONSUM",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "INTERVENTION", "TOTALDEMAND"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="REGIONID",
+ expected_entities=FIXTURED_REGIONS,
+ )
diff --git a/tests/end_to_end_table_tests/test_dispatch_unit_scada.py b/tests/end_to_end_table_tests/test_dispatch_unit_scada.py
new file mode 100644
index 0000000..e1c7cc1
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dispatch_unit_scada.py
@@ -0,0 +1,64 @@
+"""Tests for DISPATCH_UNIT_SCADA.
+
+Per-unit measured output (SCADAVALUE) at five-minute resolution. Unlike
+DISPATCHLOAD this table has no INTERVENTION column, so every interval
+produces exactly one row per DUID.
+
+Boundary matrix (`test_dispatch_unit_scada_boundary`) is auto-generated
+from `spec.DYNAMIC_TABLES["DISPATCH_UNIT_SCADA"]["eras"]` by
+`_boundaries.py`.
+
+Filtered to DUID ∈ {AGLHAL, HDWF2}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_DUIDS = {"AGLHAL", "HDWF2"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_one_hour_gives_twelve_intervals_per_duid(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+ expected = [start + pd.Timedelta(minutes=5 * i) for i in range(1, 13)]
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="DISPATCH_UNIT_SCADA",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "SCADAVALUE"],
+ )
+
+ assert set(data["DUID"]) == FIXTURED_DUIDS
+ for duid in FIXTURED_DUIDS:
+ timestamps = sorted(data[data["DUID"] == duid]["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("DISPATCH_UNIT_SCADA"), ids=lambda c: c.id
+)
+def test_dispatch_unit_scada_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="DISPATCH_UNIT_SCADA",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "SCADAVALUE"],
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="DUID",
+ expected_entities=FIXTURED_DUIDS,
+ )
diff --git a/tests/end_to_end_table_tests/test_dudetail.py b/tests/end_to_end_table_tests/test_dudetail.py
new file mode 100644
index 0000000..f02af4b
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dudetail.py
@@ -0,0 +1,24 @@
+"""Tests for DUDETAIL.
+
+Effective-date config table: registration details per dispatchable unit
+(connection point, registered capacity, station ID, etc.).
+search_type="all".
+
+Fixtured eras: 2021-05; DUID ∈ {AGLHAL, HDWF2}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_dudetail_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="DUDETAIL",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["DUID", "EFFECTIVEDATE", "VERSIONNO", "CONNECTIONPOINTID", "REGISTEREDCAPACITY"],
+ )
+
+ assert not data.empty
+ assert set(data["DUID"]) <= {"AGLHAL", "HDWF2"}
diff --git a/tests/end_to_end_table_tests/test_dudetailsummary.py b/tests/end_to_end_table_tests/test_dudetailsummary.py
new file mode 100644
index 0000000..c3b5f40
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_dudetailsummary.py
@@ -0,0 +1,46 @@
+"""Tests for DUDETAILSUMMARY.
+
+Effective-date config table summarising DUDETAIL into start/end date
+ranges per DUID (more compact than DUDETAIL for participant lookups).
+search_type="end" — fetches archives only up to end_time.
+
+Fixtured eras: 2021-05; DUID ∈ {AGLHAL, HDWF2}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_dudetailsummary_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="DUDETAILSUMMARY",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["DUID", "START_DATE", "END_DATE", "DISPATCHTYPE", "REGIONID"],
+ )
+
+ assert not data.empty
+ assert set(data["DUID"]) <= {"AGLHAL", "HDWF2"}
+
+
+def test_narrow_window_one_row_per_duid(nemosis_fixture, monkeypatch):
+ """`filter_on_start_date` should collapse to one effective row per DUID
+ at any single instant in time. Grouping by PK minus START_DATE must
+ show no duplicates — otherwise two versions of the same DUID are
+ leaking through the effective-date filter."""
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ pk = defaults.table_primary_keys["DUDETAILSUMMARY"]
+ group_cols = [c for c in pk if c != "START_DATE"]
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/20 23:00:00",
+ end_time="2021/05/20 23:05:00",
+ table_name="DUDETAILSUMMARY",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=pk,
+ )
+
+ assert not data.empty
+ assert not data.duplicated(group_cols).any(), data[data.duplicated(group_cols, keep=False)]
diff --git a/tests/end_to_end_table_tests/test_fformat_csv.py b/tests/end_to_end_table_tests/test_fformat_csv.py
new file mode 100644
index 0000000..d99d21a
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_fformat_csv.py
@@ -0,0 +1,45 @@
+"""Coverage for `dynamic_data_compiler(fformat="csv")`.
+
+The CSV path is materially different from feather/parquet — it skips the
+cache-read/cache-write branch entirely and re-parses the raw AEMO CSV on
+every call. Feather/parquet coverage lives in `test_cache_compiler.py`;
+this file locks in that the CSV branch doesn't silently break.
+
+`cache_compiler` only accepts feather/parquet, so CSV is exclusively a
+`dynamic_data_compiler` concern.
+"""
+from nemosis import dynamic_data_compiler
+
+
+def test_csv_fformat_round_trip(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 00:30:00",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ fformat="csv",
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP", "INTERVENTION"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+ assert not data.empty
+ assert data["SETTLEMENTDATE"].dtype == "datetime64[ns]"
+ assert data["REGIONID"].dtype == "object"
+ assert not all(data.dtypes == "object")
+
+
+def test_keep_csv_false_leaves_cache_empty(nemosis_fixture):
+ """With `fformat="csv"` and `keep_csv=False`, the raw CSV is fetched,
+ used to produce the return frame, and then deleted — the cache dir
+ should be empty afterwards. This is the opt-out path for users who
+ don't want NEMOSIS accumulating files on disk."""
+ dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 00:30:00",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ fformat="csv",
+ keep_csv=False,
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ )
+ assert list(nemosis_fixture.iterdir()) == []
diff --git a/tests/end_to_end_table_tests/test_gencondata.py b/tests/end_to_end_table_tests/test_gencondata.py
new file mode 100644
index 0000000..6e064bb
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_gencondata.py
@@ -0,0 +1,23 @@
+"""Tests for GENCONDATA.
+
+Effective-date config table: master list of generic constraints
+(constraint type, RHS, weight, description). search_type="all".
+
+Fixtured eras: 2021-05; GENCONID ∈ {#NSW1-QLD1_RAMP_I_F}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_gencondata_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="GENCONDATA",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["GENCONID", "EFFECTIVEDATE", "VERSIONNO", "CONSTRAINTTYPE", "CONSTRAINTVALUE"],
+ )
+
+ assert not data.empty
+ assert set(data["GENCONID"]) == {"#NSW1-QLD1_RAMP_I_F"}
diff --git a/tests/end_to_end_table_tests/test_interconnector.py b/tests/end_to_end_table_tests/test_interconnector.py
new file mode 100644
index 0000000..0d687b1
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_interconnector.py
@@ -0,0 +1,28 @@
+"""Tests for INTERCONNECTOR.
+
+Effective-date config table: one row per interconnector, `LASTCHANGED`
+records the last config change. NEMOSIS classifies it as search_type
+"all" — by default it would iterate every month from July 2009 through
+end_time. The test narrows that window by monkeypatching
+`nem_data_model_start_time` so only a single fixture month is probed.
+
+Fixtured eras: 2021-05; INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+import pandas as pd
+
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_interconnector_returns_fixtured_row(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="INTERCONNECTOR",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERCONNECTORID", "REGIONFROM", "REGIONTO", "LASTCHANGED"],
+ )
+
+ assert not data.empty
+ assert set(data["INTERCONNECTORID"]) == {"VIC1-NSW1"}
diff --git a/tests/end_to_end_table_tests/test_interconnector_constraint.py b/tests/end_to_end_table_tests/test_interconnector_constraint.py
new file mode 100644
index 0000000..ee26fff
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_interconnector_constraint.py
@@ -0,0 +1,23 @@
+"""Tests for INTERCONNECTORCONSTRAINT.
+
+Effective-date config table: per-interconnector physical limits and
+loss parameters. search_type="all".
+
+Fixtured eras: 2021-05; INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_interconnector_constraint_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="INTERCONNECTORCONSTRAINT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERCONNECTORID", "EFFECTIVEDATE", "VERSIONNO", "IMPORTLIMIT", "EXPORTLIMIT"],
+ )
+
+ assert not data.empty
+ assert set(data["INTERCONNECTORID"]) == {"VIC1-NSW1"}
diff --git a/tests/end_to_end_table_tests/test_intermittent_gen_scada.py b/tests/end_to_end_table_tests/test_intermittent_gen_scada.py
new file mode 100644
index 0000000..91179ac
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_intermittent_gen_scada.py
@@ -0,0 +1,58 @@
+"""Tests for INTERMITTENT_GEN_SCADA.
+
+Scrape-pattern table publishing SCADA output for semi-scheduled
+generators in the next-day window. Keyed on RUN_DATETIME + DUID.
+
+Fixtured era: recent (scrape endpoint retention is short).
+"""
+from nemosis import dynamic_data_compiler
+
+
+import pandas as pd
+
+
+def test_recent_day_returns_rows_for_fixtured_duids(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 00:00:00",
+ end_time="2026/03/15 01:00:00",
+ table_name="INTERMITTENT_GEN_SCADA",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["RUN_DATETIME", "DUID", "SCADA_VALUE"],
+ )
+
+ assert not data.empty
+ assert set(data["DUID"]) <= {"AGLHAL", "HDWF2"}
+
+
+# Market-day stitch at 04:00 — same convention as the other scrape tables.
+# AGLHAL is a peaking plant (scheduled, not intermittent) so only HDWF2
+# appears in this table's fixture. SCADA_TYPE is part of the PK — each
+# (RUN_DATETIME, DUID) has two rows: ELAV (Element Availability) and
+# LOCL (Local SCADA value).
+
+def test_end_market_day_returns_0400_row_from_previous_file(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 03:55:00",
+ end_time="2026/03/15 04:00:00",
+ table_name="INTERMITTENT_GEN_SCADA",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["RUN_DATETIME", "DUID", "SCADA_TYPE", "SCADA_VALUE"],
+ )
+ assert set(data["RUN_DATETIME"].unique()) == {pd.Timestamp("2026-03-15 04:00:00")}
+ assert set(data["DUID"]) == {"HDWF2"}
+ assert set(data["SCADA_TYPE"]) == {"ELAV", "LOCL"}
+ assert not data.duplicated(["RUN_DATETIME", "DUID", "SCADA_TYPE"]).any()
+
+
+def test_start_market_day_returns_0405_row_from_current_file(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 04:00:00",
+ end_time="2026/03/15 04:05:00",
+ table_name="INTERMITTENT_GEN_SCADA",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["RUN_DATETIME", "DUID", "SCADA_TYPE", "SCADA_VALUE"],
+ )
+ assert set(data["RUN_DATETIME"].unique()) == {pd.Timestamp("2026-03-15 04:05:00")}
+ assert set(data["DUID"]) == {"HDWF2"}
+ assert set(data["SCADA_TYPE"]) == {"ELAV", "LOCL"}
+ assert not data.duplicated(["RUN_DATETIME", "DUID", "SCADA_TYPE"]).any()
diff --git a/tests/end_to_end_table_tests/test_loss_factor_model.py b/tests/end_to_end_table_tests/test_loss_factor_model.py
new file mode 100644
index 0000000..93bec0a
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_loss_factor_model.py
@@ -0,0 +1,23 @@
+"""Tests for LOSSFACTORMODEL.
+
+Effective-date config table: per-region loss factors for each
+interconnector. search_type="all".
+
+Fixtured eras: 2021-05; INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_loss_factor_model_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="LOSSFACTORMODEL",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERCONNECTORID", "REGIONID", "EFFECTIVEDATE", "VERSIONNO", "DEMANDCOEFFICIENT"],
+ )
+
+ assert not data.empty
+ assert set(data["INTERCONNECTORID"]) == {"VIC1-NSW1"}
diff --git a/tests/end_to_end_table_tests/test_loss_model.py b/tests/end_to_end_table_tests/test_loss_model.py
new file mode 100644
index 0000000..0bc1496
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_loss_model.py
@@ -0,0 +1,23 @@
+"""Tests for LOSSMODEL.
+
+Effective-date config table: piecewise loss model per interconnector,
+broken into segments. search_type="all".
+
+Fixtured eras: 2021-05; INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_loss_model_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="LOSSMODEL",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERCONNECTORID", "EFFECTIVEDATE", "VERSIONNO", "LOSSSEGMENT", "MWBREAKPOINT"],
+ )
+
+ assert not data.empty
+ assert set(data["INTERCONNECTORID"]) == {"VIC1-NSW1"}
diff --git a/tests/end_to_end_table_tests/test_market_price_thresholds.py b/tests/end_to_end_table_tests/test_market_price_thresholds.py
new file mode 100644
index 0000000..731555a
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_market_price_thresholds.py
@@ -0,0 +1,23 @@
+"""Tests for MARKET_PRICE_THRESHOLDS.
+
+Effective-date config table: NEM market price floor and cap, plus
+admin-price thresholds. Tiny — 12 rows total — so no row filter is
+applied at fixture-build time. search_type="all".
+
+Fixtured eras: 2021-05.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_market_price_thresholds_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="MARKET_PRICE_THRESHOLDS",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["EFFECTIVEDATE", "VERSIONNO", "VOLL", "MARKETPRICEFLOOR"],
+ )
+
+ assert not data.empty
diff --git a/tests/end_to_end_table_tests/test_mnsp_day_offer.py b/tests/end_to_end_table_tests/test_mnsp_day_offer.py
new file mode 100644
index 0000000..be404fb
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_mnsp_day_offer.py
@@ -0,0 +1,37 @@
+"""Tests for MNSP_DAYOFFER.
+
+Daily bid offers from MNSP interconnectors (price bands, daily totals;
+keyed on LINKID). MMS monthly archive at every era — the table kept
+its name across the Aug-2024 archive rename, unlike MNSP_PEROFFER, so
+coverage extends to 2024-09.
+
+Fixtured eras: 2018-05, 2021-05, 2024-09; LINKID ∈ {BLNKTAS, BLNKVIC}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+ "2024/09/01 00:00:00",
+])
+def test_day_offers_returned_for_fixtured_links(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="MNSP_DAYOFFER",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=[
+ "SETTLEMENTDATE", "OFFERDATE", "VERSIONNO",
+ "PARTICIPANTID", "LINKID", "PRICEBAND1",
+ ],
+ )
+
+ assert not data.empty
+ assert set(data["LINKID"]) <= {"BLNKTAS", "BLNKVIC"}
diff --git a/tests/end_to_end_table_tests/test_mnsp_interconnector.py b/tests/end_to_end_table_tests/test_mnsp_interconnector.py
new file mode 100644
index 0000000..2b635c5
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_mnsp_interconnector.py
@@ -0,0 +1,29 @@
+"""Tests for MNSP_INTERCONNECTOR.
+
+Effective-date config table: per-MNSP-link routing into a parent
+"interconnector" abstraction (e.g. BLNKTAS → T-V-MNSP1). search_type="all"
+— test narrows the scan window via monkeypatched `nem_data_model_start_time`.
+
+Filtered on LINKID rather than INTERCONNECTORID because the
+INTERCONNECTORIDs in this table are the MNSP-side abstractions
+(T-V-MNSP1, V-S-MNSP1, N-Q-MNSP1), not the regulated AC interconnectors
+in our INTERCONNECTORS constant.
+
+Fixtured eras: 2021-05; LINKID ∈ {BLNKTAS, BLNKVIC}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_mnsp_interconnector_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="MNSP_INTERCONNECTOR",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["LINKID", "INTERCONNECTORID", "EFFECTIVEDATE", "VERSIONNO"],
+ )
+
+ assert not data.empty
+ assert set(data["LINKID"]) <= {"BLNKTAS", "BLNKVIC"}
diff --git a/tests/end_to_end_table_tests/test_mnsp_per_offer.py b/tests/end_to_end_table_tests/test_mnsp_per_offer.py
new file mode 100644
index 0000000..3a4f842
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_mnsp_per_offer.py
@@ -0,0 +1,38 @@
+"""Tests for MNSP_PEROFFER.
+
+Per-period bid offers from MNSP interconnectors (keyed on LINKID
+rather than DUID). MMS monthly archive at every era NEMOSIS can
+currently read. After ~April 2021 AEMO began writing MNSP_BIDOFFERPERIOD
+data (different column names) into the PUBLIC_DVD_MNSP_PEROFFER archives
+and then at Aug-2024 renamed the archive stub outright — NEMOSIS doesn't
+handle either change, so coverage stops at 2021-02.
+
+Fixtured eras: 2018-05, 2021-02; LINKID ∈ {BLNKTAS, BLNKVIC}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/02/01 00:00:00",
+])
+def test_offers_returned_for_fixtured_links(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="MNSP_PEROFFER",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=[
+ "SETTLEMENTDATE", "OFFERDATE", "VERSIONNO",
+ "PARTICIPANTID", "LINKID", "PERIODID", "BANDAVAIL1",
+ ],
+ )
+
+ assert not data.empty
+ assert set(data["LINKID"]) <= {"BLNKTAS", "BLNKVIC"}
diff --git a/tests/end_to_end_table_tests/test_next_day_dispatch_load.py b/tests/end_to_end_table_tests/test_next_day_dispatch_load.py
new file mode 100644
index 0000000..a52b1e1
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_next_day_dispatch_load.py
@@ -0,0 +1,54 @@
+"""Tests for NEXT_DAY_DISPATCHLOAD.
+
+Scrape-pattern version of DISPATCHLOAD published as part of the
+next-day dispatch reports. Five-minute resolution, DUID-keyed.
+
+Fixtured era: recent (scrape endpoint keeps only a few months of data).
+"""
+from nemosis import dynamic_data_compiler
+
+
+import pandas as pd
+
+
+def test_recent_day_returns_rows_for_fixtured_duids(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 00:00:00",
+ end_time="2026/03/15 01:00:00",
+ table_name="NEXT_DAY_DISPATCHLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INITIALMW"],
+ )
+
+ assert not data.empty
+ assert set(data["DUID"]) <= {"AGLHAL", "HDWF2"}
+
+
+# Market-day stitch at 04:00. See test_daily_region_summary.py for the
+# full explanation — NEXT_DAY_DISPATCH files share the same convention:
+# `{date} 04:05 → {date+1} 04:00` per file, with 04:00 in the prior file.
+
+def test_end_market_day_returns_0400_row_from_previous_file(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 03:55:00",
+ end_time="2026/03/15 04:00:00",
+ table_name="NEXT_DAY_DISPATCHLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INITIALMW"],
+ )
+ assert set(data["SETTLEMENTDATE"].unique()) == {pd.Timestamp("2026-03-15 04:00:00")}
+ assert set(data["DUID"]) <= {"AGLHAL", "HDWF2"}
+ assert not data.duplicated(["SETTLEMENTDATE", "DUID"]).any()
+
+
+def test_start_market_day_returns_0405_row_from_current_file(nemosis_fixture):
+ data = dynamic_data_compiler(
+ start_time="2026/03/15 04:00:00",
+ end_time="2026/03/15 04:05:00",
+ table_name="NEXT_DAY_DISPATCHLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INITIALMW"],
+ )
+ assert set(data["SETTLEMENTDATE"].unique()) == {pd.Timestamp("2026-03-15 04:05:00")}
+ assert set(data["DUID"]) <= {"AGLHAL", "HDWF2"}
+ assert not data.duplicated(["SETTLEMENTDATE", "DUID"]).any()
diff --git a/tests/end_to_end_table_tests/test_participant.py b/tests/end_to_end_table_tests/test_participant.py
new file mode 100644
index 0000000..4c067c5
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_participant.py
@@ -0,0 +1,69 @@
+"""Tests for PARTICIPANT.
+
+Effective-date config table: registered NEM participants. search_type="all".
+
+Fixtured eras: 2018-05, 2021-05;
+PARTICIPANTID ∈ {AGLE, INFIGENH, ERMPOWER}.
+
+ERMPOWER is fixtured specifically to exercise `filter_on_last_changed`
+across a wide window — its LASTCHANGED moves between 2018 and 2021
+archives, whereas AGLE and INFIGENH sit on 2013 LASTCHANGED values in
+both archives and so each appear exactly once even in a multi-year query.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+FIXTURED_PIDS = {"AGLE", "INFIGENH", "ERMPOWER"}
+
+
+def test_participant_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="PARTICIPANT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["PARTICIPANTID", "PARTICIPANTCLASSID", "NAME", "LASTCHANGED"],
+ )
+
+ assert not data.empty
+ assert set(data["PARTICIPANTID"]) <= FIXTURED_PIDS
+
+
+def test_narrow_window_returns_one_row_per_participantid(nemosis_fixture, monkeypatch):
+ """5-minute window: `filter_on_last_changed` surfaces exactly the
+ currently-effective row for each participant — no history."""
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 00:05:00",
+ table_name="PARTICIPANT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["PARTICIPANTID", "NAME", "LASTCHANGED"],
+ )
+
+ counts = data.groupby("PARTICIPANTID").size()
+ assert (counts == 1).all(), counts.to_dict()
+
+
+def test_wide_window_surfaces_lastchanged_history(nemosis_fixture, monkeypatch):
+ """Multi-year window: ERMPOWER has distinct LASTCHANGED values in the
+ 2018-05 and 2021-05 archives, so both rows survive `filter_on_last_changed`.
+ AGLE/INFIGENH never changed — still 1 row each. The contrast confirms
+ the filter is surfacing history, not collapsing to most-recent-per-PID."""
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2018/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2018/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="PARTICIPANT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["PARTICIPANTID", "NAME", "LASTCHANGED"],
+ )
+
+ counts = data.groupby("PARTICIPANTID").size().to_dict()
+ assert counts.get("ERMPOWER", 0) > 1, counts
+ assert counts.get("AGLE") == 1, counts
+ assert counts.get("INFIGENH") == 1, counts
diff --git a/tests/end_to_end_table_tests/test_rooftop_pv_actual.py b/tests/end_to_end_table_tests/test_rooftop_pv_actual.py
new file mode 100644
index 0000000..acdcb7e
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_rooftop_pv_actual.py
@@ -0,0 +1,69 @@
+"""Tests for ROOFTOP_PV_ACTUAL.
+
+Per-region rooftop PV generation at 30-min resolution. Introduced
+around 2019. Has a TYPE column with both SATELLITE (regional estimate
+updated through the day) and DAILY (next-day reconciled) values —
+a given (REGIONID, INTERVAL_DATETIME) can have up to two rows.
+
+Boundary matrix (`test_rooftop_pv_actual_boundary`) filters TYPE=SATELLITE
+so the helper sees one row per (REGIONID, interval) and its row-count
+assertions hold. The matrix is auto-generated from
+`spec.DYNAMIC_TABLES["ROOFTOP_PV_ACTUAL"]["eras"]` by `_boundaries.py`
+— 30-min stride throughout.
+
+REGIONID ∈ {SA1, NSW1}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_REGIONS = {"SA1", "NSW1"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2021/05/01 00:00:00",
+ "2024/08/01 00:00:00",
+])
+def test_one_hour_returns_two_half_hourly_rows_per_region_per_type(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="ROOFTOP_PV_ACTUAL",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERVAL_DATETIME", "REGIONID", "TYPE", "POWER"],
+ )
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ # Two half-hour intervals in one hour. Each region may appear under
+ # either SATELLITE or DAILY type (or both).
+ for region in FIXTURED_REGIONS:
+ by_type = data[data["REGIONID"] == region].groupby("TYPE").size()
+ assert (by_type == 2).all(), by_type
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("ROOFTOP_PV_ACTUAL"), ids=lambda c: c.id
+)
+def test_rooftop_pv_actual_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="ROOFTOP_PV_ACTUAL",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERVAL_DATETIME", "REGIONID", "TYPE", "POWER"],
+ filter_cols=["TYPE"],
+ filter_values=(["SATELLITE"],),
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="INTERVAL_DATETIME",
+ entities_col="REGIONID",
+ expected_entities=FIXTURED_REGIONS,
+ )
diff --git a/tests/end_to_end_table_tests/test_spd_connection_point_constraint.py b/tests/end_to_end_table_tests/test_spd_connection_point_constraint.py
new file mode 100644
index 0000000..db99e52
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_spd_connection_point_constraint.py
@@ -0,0 +1,28 @@
+"""Tests for SPDCONNECTIONPOINTCONSTRAINT.
+
+Effective-date config table: tells SPD which connection points feed
+into which generic constraints, and with what factor. search_type="all".
+
+Filtered by GENCONID rather than CONNECTIONPOINTID since we don't have a
+known connection-point shortlist; picked a low-volume constraint
+("S>>MKRB_NIL_WEMWP4", ~20 rows in 2021-05) with an EFFECTIVEDATE before
+the test window so NEMOSIS's `filter_on_effective_date` keeps it in.
+
+Fixtured eras: 2021-05; GENCONID ∈ {S>>MKRB_NIL_WEMWP4}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_spd_connection_point_constraint_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="SPDCONNECTIONPOINTCONSTRAINT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["CONNECTIONPOINTID", "GENCONID", "EFFECTIVEDATE", "VERSIONNO", "BIDTYPE", "FACTOR"],
+ )
+
+ assert not data.empty
+ assert set(data["GENCONID"]) == {"S>>MKRB_NIL_WEMWP4"}
diff --git a/tests/end_to_end_table_tests/test_spd_interconnector_constraint.py b/tests/end_to_end_table_tests/test_spd_interconnector_constraint.py
new file mode 100644
index 0000000..a19590d
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_spd_interconnector_constraint.py
@@ -0,0 +1,23 @@
+"""Tests for SPDINTERCONNECTORCONSTRAINT.
+
+Effective-date config table: tells SPD which interconnectors feed into
+which generic constraints, and with what factor. search_type="all".
+
+Fixtured eras: 2021-05; INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_spd_interconnector_constraint_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="SPDINTERCONNECTORCONSTRAINT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["INTERCONNECTORID", "GENCONID", "EFFECTIVEDATE", "VERSIONNO", "FACTOR"],
+ )
+
+ assert not data.empty
+ assert set(data["INTERCONNECTORID"]) == {"VIC1-NSW1"}
diff --git a/tests/end_to_end_table_tests/test_spd_region_constraint.py b/tests/end_to_end_table_tests/test_spd_region_constraint.py
new file mode 100644
index 0000000..b602d01
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_spd_region_constraint.py
@@ -0,0 +1,23 @@
+"""Tests for SPDREGIONCONSTRAINT.
+
+Effective-date config table: tells SPD which regions feed into which
+generic constraints, and with what factor. search_type="all".
+
+Fixtured eras: 2021-05; REGIONID ∈ {SA1, NSW1}.
+"""
+from nemosis import defaults, dynamic_data_compiler
+
+
+def test_spd_region_constraint_returns_fixtured_rows(nemosis_fixture, monkeypatch):
+ monkeypatch.setattr(defaults, "nem_data_model_start_time", "2021/05/01 00:00:00")
+
+ data = dynamic_data_compiler(
+ start_time="2021/05/01 00:00:00",
+ end_time="2021/05/01 01:00:00",
+ table_name="SPDREGIONCONSTRAINT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["REGIONID", "GENCONID", "EFFECTIVEDATE", "VERSIONNO", "FACTOR"],
+ )
+
+ assert not data.empty
+ assert set(data["REGIONID"]) <= {"SA1", "NSW1"}
diff --git a/tests/end_to_end_table_tests/test_static_tables.py b/tests/end_to_end_table_tests/test_static_tables.py
new file mode 100644
index 0000000..64dcb18
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_static_tables.py
@@ -0,0 +1,88 @@
+"""Tests for AEMO's static (time-independent) tables.
+
+These four tables share one test file because they have no date dimension —
+each is downloaded once as a snapshot and cached verbatim. The fixtures are
+frozen copies of what AEMO was serving when build.py last ran.
+
+ - ELEMENTS_FCAS_4_SECOND — CSV, scraped from a directory index
+ - VARIABLES_FCAS_4_SECOND — CSV, direct download
+ - Generators and Scheduled Loads — XLS workbook, direct download
+ - FCAS Providers — different tab of the same XLS workbook
+"""
+from nemosis import static_table
+
+
+def test_elements_fcas_returns_non_empty_frame(nemosis_fixture):
+ data = static_table("ELEMENTS_FCAS_4_SECOND", str(nemosis_fixture))
+ assert not data.empty
+
+
+def test_variables_fcas_returns_non_empty_frame(nemosis_fixture):
+ data = static_table("VARIABLES_FCAS_4_SECOND", str(nemosis_fixture))
+ assert not data.empty
+
+
+def test_generators_and_scheduled_loads_returns_non_empty_frame(nemosis_fixture):
+ data = static_table("Generators and Scheduled Loads", str(nemosis_fixture))
+ assert not data.empty
+
+
+def test_fcas_providers_returns_non_empty_frame(nemosis_fixture):
+ # "FCAS Providers" reads a different sheet of the same XLS workbook as
+ # "Generators and Scheduled Loads". NEMOSIS's static_downloader_map has
+ # no entry for "FCAS Providers", so the first fetch errors on an empty
+ # cache — prime it by fetching the sibling table.
+ static_table("Generators and Scheduled Loads", str(nemosis_fixture))
+ data = static_table("FCAS Providers", str(nemosis_fixture))
+ assert not data.empty
+
+
+# ---------------------------------------------------------------------------
+# Filter exercises — confirm `filter_cols`/`filter_values` actually narrow
+# the result. `static_table` requires `select_columns` whenever `filter_cols`
+# is passed (the filter cols must be a subset of the selected cols).
+# ---------------------------------------------------------------------------
+
+def test_elements_fcas_filter_narrows(nemosis_fixture):
+ cols = ["ELEMENTNUMBER", "EMSNAME", "ELEMENTTYPE"]
+ full = static_table("ELEMENTS_FCAS_4_SECOND", str(nemosis_fixture), select_columns=cols)
+ filtered = static_table(
+ "ELEMENTS_FCAS_4_SECOND", str(nemosis_fixture),
+ select_columns=cols, filter_cols=["ELEMENTTYPE"], filter_values=[["GEN"]],
+ )
+ assert 0 < len(filtered) < len(full)
+ assert set(filtered["ELEMENTTYPE"]) == {"GEN"}
+
+
+def test_variables_fcas_filter_narrows(nemosis_fixture):
+ cols = ["VARIABLENUMBER", "VARIABLETYPE"]
+ full = static_table("VARIABLES_FCAS_4_SECOND", str(nemosis_fixture), select_columns=cols)
+ filtered = static_table(
+ "VARIABLES_FCAS_4_SECOND", str(nemosis_fixture),
+ select_columns=cols, filter_cols=["VARIABLETYPE"], filter_values=[["MW"]],
+ )
+ assert 0 < len(filtered) < len(full)
+ assert set(filtered["VARIABLETYPE"]) == {"MW"}
+
+
+def test_generators_filter_narrows(nemosis_fixture):
+ cols = ["Participant", "Region", "DUID"]
+ full = static_table("Generators and Scheduled Loads", str(nemosis_fixture), select_columns=cols)
+ filtered = static_table(
+ "Generators and Scheduled Loads", str(nemosis_fixture),
+ select_columns=cols, filter_cols=["Region"], filter_values=[["SA1"]],
+ )
+ assert 0 < len(filtered) < len(full)
+ assert set(filtered["Region"]) == {"SA1"}
+
+
+def test_fcas_providers_filter_narrows(nemosis_fixture):
+ static_table("Generators and Scheduled Loads", str(nemosis_fixture))
+ cols = ["Participant", "Region", "DUID", "Bid Type"]
+ full = static_table("FCAS Providers", str(nemosis_fixture), select_columns=cols)
+ filtered = static_table(
+ "FCAS Providers", str(nemosis_fixture),
+ select_columns=cols, filter_cols=["Region"], filter_values=[["SA1"]],
+ )
+ assert 0 < len(filtered) < len(full)
+ assert set(filtered["Region"]) == {"SA1"}
diff --git a/tests/end_to_end_table_tests/test_trading_interconnect.py b/tests/end_to_end_table_tests/test_trading_interconnect.py
new file mode 100644
index 0000000..eb13923
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_trading_interconnect.py
@@ -0,0 +1,85 @@
+"""Tests for TRADINGINTERCONNECT.
+
+Per-interconnector trading summary. Same 30-min → 5-min transition
+at 2021-10-01 (5MS reform) as TRADINGPRICE.
+
+Boundary matrix (`test_trading_interconnect_boundary`) is auto-generated
+from `spec.DYNAMIC_TABLES["TRADINGINTERCONNECT"]["eras"]` by
+`_boundaries.py`. The helper picks the right stride per era automatically.
+
+INTERCONNECTORID ∈ {VIC1-NSW1}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_INTERCONNECTORS = {"VIC1-NSW1"}
+
+
+@pytest.mark.parametrize("era_start,expected_per_hour", [
+ ("2018/05/01 00:00:00", 2), # 30-min era
+ ("2021/05/01 00:00:00", 2),
+ ("2022/06/01 00:00:00", 12), # post-5MS (cutover was 2021-10-01)
+ ("2024/08/01 00:00:00", 12),
+])
+def test_interval_granularity_matches_era(nemosis_fixture, era_start, expected_per_hour):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="TRADINGINTERCONNECT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "INTERCONNECTORID", "METEREDMWFLOW"],
+ )
+
+ assert set(data["INTERCONNECTORID"]) == FIXTURED_INTERCONNECTORS
+ assert len(data) == expected_per_hour
+
+
+def test_5ms_cutover_stride_transition(nemosis_fixture):
+ """Query a window straddling the 2021-10-01 5MS cutover and assert the
+ stride transition: two 30-min rows from the Sept file, then twelve 5-min
+ rows from the Oct file."""
+ data = dynamic_data_compiler(
+ start_time="2021/09/30 23:00:00",
+ end_time="2021/10/01 01:00:00",
+ table_name="TRADINGINTERCONNECT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "INTERCONNECTORID", "METEREDMWFLOW"],
+ )
+
+ expected = [
+ pd.Timestamp("2021-09-30 23:30:00"),
+ pd.Timestamp("2021-10-01 00:00:00"),
+ ] + [pd.Timestamp("2021-10-01 00:00:00") + pd.Timedelta(minutes=5 * i)
+ for i in range(1, 13)]
+
+ assert set(data["INTERCONNECTORID"]) == FIXTURED_INTERCONNECTORS
+ for link in FIXTURED_INTERCONNECTORS:
+ timestamps = sorted(data[data["INTERCONNECTORID"] == link]["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("TRADINGINTERCONNECT"), ids=lambda c: c.id
+)
+def test_trading_interconnect_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="TRADINGINTERCONNECT",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "INTERCONNECTORID", "METEREDMWFLOW"],
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="INTERCONNECTORID",
+ expected_entities=FIXTURED_INTERCONNECTORS,
+ )
diff --git a/tests/end_to_end_table_tests/test_trading_load.py b/tests/end_to_end_table_tests/test_trading_load.py
new file mode 100644
index 0000000..cc5c870
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_trading_load.py
@@ -0,0 +1,62 @@
+"""Tests for TRADINGLOAD.
+
+Per-unit 30-minute trading summary (INITIALMW, TOTALCLEARED, etc.).
+**Discontinued before 2022-01** — AEMO stopped publishing the monthly
+archive for this table when trading merged with dispatch. Only
+pre-2022 eras are fixtured.
+
+Boundary matrix (`test_trading_load_boundary`) is auto-generated from
+`spec.DYNAMIC_TABLES["TRADINGLOAD"]["eras"]` by `_boundaries.py` —
+30-min stride throughout.
+
+DUID ∈ {AGLHAL, HDWF2}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_DUIDS = {"AGLHAL", "HDWF2"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+])
+def test_one_hour_gives_two_half_hourly_intervals_per_duid(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="TRADINGLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INITIALMW"],
+ )
+
+ assert set(data["DUID"]) == FIXTURED_DUIDS
+ for duid in FIXTURED_DUIDS:
+ assert len(data[data["DUID"] == duid]) == 2
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("TRADINGLOAD"), ids=lambda c: c.id
+)
+def test_trading_load_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="TRADINGLOAD",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "DUID", "INITIALMW"],
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="DUID",
+ expected_entities=FIXTURED_DUIDS,
+ )
diff --git a/tests/end_to_end_table_tests/test_trading_price.py b/tests/end_to_end_table_tests/test_trading_price.py
new file mode 100644
index 0000000..7da6fcf
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_trading_price.py
@@ -0,0 +1,91 @@
+"""Tests for TRADINGPRICE.
+
+Per-region trading price summary. Interval granularity changed on 2021-10-01
+(the 5MS reform cutover): before that date trading was 30-minute, after it
+matches dispatch at 5-min. The tests make that transition explicit via era
+parametrisation and a dedicated cutover test.
+
+Boundary matrix (`test_trading_price_boundary`) is auto-generated from
+`spec.DYNAMIC_TABLES["TRADINGPRICE"]["eras"]` by `_boundaries.py`. The
+helper picks the right stride per era automatically (30 → 5 at 2021-10).
+
+REGIONID ∈ {SA1, NSW1}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_REGIONS = {"SA1", "NSW1"}
+
+
+@pytest.mark.parametrize("era_start,interval_minutes,expected_per_hour", [
+ ("2018/05/01 00:00:00", 30, 2), # pre-5-min era — 2 half-hour intervals/hour
+ ("2021/05/01 00:00:00", 30, 2),
+ ("2022/06/01 00:00:00", 5, 12), # post-5MS (cutover was 2021-10-01)
+ ("2024/08/01 00:00:00", 5, 12),
+])
+def test_interval_granularity_matches_era(
+ nemosis_fixture, era_start, interval_minutes, expected_per_hour,
+):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="TRADINGPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ )
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ for region in FIXTURED_REGIONS:
+ region_data = data[data["REGIONID"] == region].sort_values("SETTLEMENTDATE")
+ assert len(region_data) == expected_per_hour
+
+
+def test_5ms_cutover_stride_transition(nemosis_fixture):
+ """Query a window straddling the 2021-10-01 5MS cutover and assert the
+ stride transition is reproduced exactly: two 30-min rows from the Sept
+ file, then twelve 5-min rows from the Oct file."""
+ data = dynamic_data_compiler(
+ start_time="2021/09/30 23:00:00",
+ end_time="2021/10/01 01:00:00",
+ table_name="TRADINGPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ )
+
+ expected = [
+ pd.Timestamp("2021-09-30 23:30:00"),
+ pd.Timestamp("2021-10-01 00:00:00"),
+ ] + [pd.Timestamp("2021-10-01 00:00:00") + pd.Timedelta(minutes=5 * i)
+ for i in range(1, 13)]
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ for region in FIXTURED_REGIONS:
+ timestamps = sorted(data[data["REGIONID"] == region]["SETTLEMENTDATE"].tolist())
+ assert timestamps == expected
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("TRADINGPRICE"), ids=lambda c: c.id
+)
+def test_trading_price_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="TRADINGPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="REGIONID",
+ expected_entities=FIXTURED_REGIONS,
+ )
diff --git a/tests/end_to_end_table_tests/test_trading_region_sum.py b/tests/end_to_end_table_tests/test_trading_region_sum.py
new file mode 100644
index 0000000..637f48e
--- /dev/null
+++ b/tests/end_to_end_table_tests/test_trading_region_sum.py
@@ -0,0 +1,60 @@
+"""Tests for TRADINGREGIONSUM.
+
+Per-region 30-minute trading summary. Discontinued in 2022 (same
+reason as TRADINGLOAD), so only pre-2022 eras are fixtured.
+
+Boundary matrix (`test_trading_region_sum_boundary`) is auto-generated
+from `spec.DYNAMIC_TABLES["TRADINGREGIONSUM"]["eras"]` by `_boundaries.py`
+— 30-min stride throughout.
+
+REGIONID ∈ {SA1, NSW1}.
+"""
+import pandas as pd
+import pytest
+
+from nemosis import dynamic_data_compiler
+
+from _boundaries import assert_boundary_shape, boundary_cases
+
+FIXTURED_REGIONS = {"SA1", "NSW1"}
+
+
+@pytest.mark.parametrize("era_start", [
+ "2018/05/01 00:00:00",
+ "2021/05/01 00:00:00",
+])
+def test_one_hour_gives_two_half_hourly_intervals_per_region(nemosis_fixture, era_start):
+ start = pd.to_datetime(era_start, format="%Y/%m/%d %H:%M:%S")
+ end = start + pd.Timedelta(hours=1)
+
+ data = dynamic_data_compiler(
+ start_time=start.strftime("%Y/%m/%d %H:%M:%S"),
+ end_time=end.strftime("%Y/%m/%d %H:%M:%S"),
+ table_name="TRADINGREGIONSUM",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "TOTALDEMAND"],
+ )
+
+ assert set(data["REGIONID"]) == FIXTURED_REGIONS
+ for region in FIXTURED_REGIONS:
+ assert len(data[data["REGIONID"] == region]) == 2
+
+
+@pytest.mark.parametrize(
+ "case", boundary_cases("TRADINGREGIONSUM"), ids=lambda c: c.id
+)
+def test_trading_region_sum_boundary(nemosis_fixture, case):
+ data = dynamic_data_compiler(
+ start_time=case.start_str,
+ end_time=case.end_str,
+ table_name="TRADINGREGIONSUM",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "TOTALDEMAND"],
+ )
+
+ assert_boundary_shape(
+ data, case,
+ date_col="SETTLEMENTDATE",
+ entities_col="REGIONID",
+ expected_entities=FIXTURED_REGIONS,
+ )
diff --git a/tests/fixtures/build.py b/tests/fixtures/build.py
new file mode 100644
index 0000000..16a916b
--- /dev/null
+++ b/tests/fixtures/build.py
@@ -0,0 +1,499 @@
+"""Download and filter real AEMO data into compact local test fixtures.
+
+Reads the spec in tests/fixtures/spec.py, downloads every (table, era) pair
+from AEMO, filters rows down to the entities in the spec, and writes the
+result under tests/fixtures/data/ in a directory tree that mirrors the URL
+paths on nemweb.com.au and aemo.com.au. The dummy HTTP server serves that
+tree verbatim, so every URL AEMO exposes has a 1:1 local counterpart.
+
+Usage:
+ python tests/fixtures/build.py # build missing fixtures only
+ python tests/fixtures/build.py --rebuild # re-download and overwrite all
+
+This is intended to be run rarely — whenever an era or table is added to the
+spec — and the resulting files committed to the repo.
+"""
+from __future__ import annotations
+
+import argparse
+import io
+import logging
+import sys
+import zipfile
+from datetime import date
+from pathlib import Path
+
+import pandas as pd
+import requests
+from bs4 import BeautifulSoup
+
+REPO_ROOT = Path(__file__).resolve().parents[2]
+FIXTURE_ROOT = Path(__file__).parent / "data"
+sys.path.insert(0, str(REPO_ROOT / "src"))
+sys.path.insert(0, str(Path(__file__).parent))
+
+from nemosis import defaults # noqa: E402
+import spec # noqa: E402
+
+USR_AGENT = {"User-Agent": "NEMOSIS-fixture-builder"}
+MMS_ARCHIVE_CUTOVER = date(2024, 8, 1) # PUBLIC_DVD_ → PUBLIC_ARCHIVE# on this month
+
+# Temporal filter applied to every row whose table has a primary date column.
+# Keeping only the first and last few days of the month shrinks fixtures ~6x
+# while preserving the start-of-month / end-of-month / month-straddle boundaries
+# the tests care about.
+KEEP_FIRST_DAYS = 3
+KEEP_LAST_DAYS = 2
+
+log = logging.getLogger("fixtures")
+
+
+# ---------------------------------------------------------------------------
+# Network / IO helpers
+# ---------------------------------------------------------------------------
+
+def http_get(url: str) -> requests.Response:
+ log.info("GET %s", url)
+ r = requests.get(url.replace("#", "%23"), headers=USR_AGENT, timeout=180)
+ r.raise_for_status()
+ return r
+
+
+def write_file(target: Path, contents: bytes) -> None:
+ target.parent.mkdir(parents=True, exist_ok=True)
+ target.write_bytes(contents)
+ log.info("wrote %s (%s bytes)", target.relative_to(FIXTURE_ROOT), len(contents))
+
+
+def read_single_csv_from_zip(zip_bytes: bytes) -> tuple[str, bytes]:
+ with zipfile.ZipFile(io.BytesIO(zip_bytes)) as z:
+ name = z.namelist()[0]
+ return name, z.read(name)
+
+
+def pack_csv_as_zip(csv_name: str, csv_bytes: bytes) -> bytes:
+ buf = io.BytesIO()
+ with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z:
+ z.writestr(csv_name, csv_bytes)
+ return buf.getvalue()
+
+
+# ---------------------------------------------------------------------------
+# MMS CSV filtering
+# ---------------------------------------------------------------------------
+# AEMO MMS CSVs have the shape:
+# row 0: C,NEMP.WORLD,... (summary — preserved as-is)
+# row 1: I,TABLE,SUBCAT,VERSION,col,col (column header — preserved as-is)
+# rows 2-N: D,TABLE,SUBCAT,VERSION,val,val (data rows — filtered)
+# row N+1: C,"END OF REPORT",n (footer — preserved as-is)
+
+def filter_mms_csv(
+ csv_bytes: bytes,
+ filters: dict[str, list],
+ date_column: str | None,
+ era_date: date,
+) -> bytes:
+ lines = csv_bytes.decode("utf-8").splitlines(keepends=True)
+ summary, footer = lines[0], lines[-1]
+ body = "".join(lines[1:-1])
+
+ df = pd.read_csv(io.StringIO(body), dtype=str, keep_default_na=False)
+ for col, allowed in filters.items():
+ if col not in df.columns:
+ raise KeyError(f"filter column {col!r} not found in {list(df.columns)}")
+ df = df[df[col].isin([str(v) for v in allowed])]
+
+ if date_column and date_column in df.columns:
+ df = _keep_first_and_last_days(df, date_column, era_date)
+
+ out = io.StringIO()
+ out.write(summary)
+ df.to_csv(out, index=False, lineterminator="\n")
+ out.write(footer)
+ return out.getvalue().encode("utf-8")
+
+
+def _keep_first_and_last_days(df: pd.DataFrame, col: str, era_date: date) -> pd.DataFrame:
+ """Keep rows from the first KEEP_FIRST_DAYS and last KEEP_LAST_DAYS of the
+ era's month. Also keep rows from adjacent months if they happen to be
+ present — MMS archives sometimes contain records from the trailing edge of
+ the neighbouring month, and tests exercising that behaviour want them."""
+ parsed = pd.to_datetime(df[col], errors="coerce")
+ same_month = (parsed.dt.year == era_date.year) & (parsed.dt.month == era_date.month)
+ days_in_month = (parsed + pd.offsets.MonthEnd(0)).dt.day
+ early = same_month & (parsed.dt.day <= KEEP_FIRST_DAYS)
+ late = same_month & (parsed.dt.day > days_in_month - KEEP_LAST_DAYS)
+ other_month = ~same_month # preserve any cross-month rows verbatim
+ return df[early | late | other_month]
+
+
+# ---------------------------------------------------------------------------
+# URL & path construction (MMS dynamic tables)
+# ---------------------------------------------------------------------------
+
+def mms_filename(table: str, era_date: date, chunk: int) -> str:
+ year, month = era_date.year, f"{era_date.month:02d}"
+ if era_date >= MMS_ARCHIVE_CUTOVER:
+ return f"PUBLIC_ARCHIVE#{table}#FILE{chunk:02d}#{year}{month}010000.zip"
+ return f"{defaults.names[table]}_{year}{month}010000.zip"
+
+
+def mms_fixture_path(table: str, era_date: date, chunk: int) -> Path:
+ year, month = era_date.year, f"{era_date.month:02d}"
+ return (
+ FIXTURE_ROOT
+ / "Data_Archive/Wholesale_Electricity/MMSDM"
+ / str(year)
+ / f"MMSDM_{year}_{month}"
+ / "MMSDM_Historical_Data_SQLLoader/DATA"
+ / mms_filename(table, era_date, chunk)
+ )
+
+
+def mms_source_url(table: str, era_date: date, chunk: int) -> str:
+ return defaults.aemo_mms_url.format(
+ era_date.year,
+ era_date.year,
+ f"{era_date.month:02d}",
+ mms_filename(table, era_date, chunk).removesuffix(".zip"),
+ )
+
+
+# ---------------------------------------------------------------------------
+# Builders
+# ---------------------------------------------------------------------------
+
+def build_mms_month(table: str, month: date, *, rebuild: bool) -> None:
+ """Download, filter, and write every chunk of one (table, month) MMS archive."""
+ cfg = spec.DYNAMIC_TABLES[table]
+ filters = cfg["filter"]
+ # Effective-date config tables publish sparsely — the default first-3 /
+ # last-2 days trim would often leave the fixture empty. They opt out with
+ # `keep_full_month: True`, meaning we only apply the row (column-value)
+ # filter, never the time filter.
+ date_column = None if cfg.get("keep_full_month") else defaults.primary_date_columns.get(table)
+ chunked = month >= MMS_ARCHIVE_CUTOVER
+
+ chunk = 1
+ while True:
+ target = mms_fixture_path(table, month, chunk)
+ if target.exists() and not rebuild:
+ log.info("skip (cached) %s", target.relative_to(FIXTURE_ROOT))
+ else:
+ url = mms_source_url(table, month, chunk)
+ try:
+ zip_bytes = http_get(url).content
+ except requests.HTTPError:
+ if chunk == 1:
+ raise
+ log.info("no chunk %d for %s %s — stopping", chunk, table, month)
+ return
+ csv_name, csv_bytes = read_single_csv_from_zip(zip_bytes)
+ filtered = filter_mms_csv(csv_bytes, filters, date_column, month)
+ write_file(target, pack_csv_as_zip(csv_name, filtered))
+
+ if not chunked:
+ return
+ chunk += 1
+
+
+def previous_month(month: date) -> date:
+ if month.month == 1:
+ return date(month.year - 1, 12, 1)
+ return date(month.year, month.month - 1, 1)
+
+
+def build_static_variables_fcas(*, rebuild: bool) -> None:
+ """Direct CSV download from aemo.com.au — no scraping, no filtering."""
+ url = defaults.static_table_url["VARIABLES_FCAS_4_SECOND"]
+ target = FIXTURE_ROOT / url.split("://", 1)[1].split("/", 1)[1]
+ if target.exists() and not rebuild:
+ log.info("skip (cached) %s", target.relative_to(FIXTURE_ROOT))
+ return
+ write_file(target, http_get(url).content)
+
+
+def build_static_generators_xls(*, rebuild: bool) -> None:
+ """Direct XLS download from aemo.com.au — covers both 'Generators and
+ Scheduled Loads' and 'FCAS Providers', which share the same workbook."""
+ url = defaults.static_table_url["Generators and Scheduled Loads"]
+ target = FIXTURE_ROOT / url.split("://", 1)[1].split("/", 1)[1]
+ if target.exists() and not rebuild:
+ log.info("skip (cached) %s", target.relative_to(FIXTURE_ROOT))
+ return
+ write_file(target, http_get(url).content)
+
+
+def build_static_elements_fcas(*, rebuild: bool) -> None:
+ """Scrape-pattern: download the newest Elements CSV and write a fake
+ directory index alongside it so the live scraper logic still works."""
+ index_url = defaults.static_table_url["ELEMENTS_FCAS_4_SECOND"]
+ fixture_dir = FIXTURE_ROOT / index_url.split("://", 1)[1].split("/", 1)[1]
+
+ soup = BeautifulSoup(http_get(index_url).text, "html.parser")
+ links = [a.text for a in soup.find_all("a") if a.text.endswith(".csv")]
+ if not links:
+ raise RuntimeError("no Elements_FCAS CSV links found on AEMO index page")
+ filename = links[-1] # mirror downloader.download_elements_file's choice
+
+ target = fixture_dir / filename
+ if target.exists() and not rebuild:
+ log.info("skip (cached) %s", target.relative_to(FIXTURE_ROOT))
+ else:
+ write_file(target, http_get(index_url + filename).content)
+
+ elements_path = index_url.split("://", 1)[1].split("/", 1)[1]
+ write_file(fixture_dir / "index.html",
+ make_index_html([filename], elements_path).encode("utf-8"))
+
+
+def make_index_html(filenames: list[str], url_prefix: str) -> str:
+ """Minimal HTML directory listing. Real AEMO pages are messy tables of
+ server-generated links with absolute-path hrefs like /Reports/Current/X/Y.zip;
+ NEMOSIS concatenates those onto the domain URL, so our fake index must use
+ the same absolute-path form (not ./relative) for the resulting URL to point
+ at the right file on our server."""
+ anchors = "\n".join(f'{n}' for n in filenames)
+ return f"\n{anchors}\n"
+
+
+# ---------------------------------------------------------------------------
+# Scrape-pattern (BIDMOVE_COMPLETE, PUBLIC_DAILY, PUBLIC_NEXT_DAY_*)
+# ---------------------------------------------------------------------------
+# These tables live on AEMO's rolling "current data" index pages — each page
+# lists daily zips for the last few months. NEMOSIS finds them by scraping
+# the page for a link whose href contains a date-stamped filename stub.
+# Build-time complication: dates that age out of the window stop being served,
+# so we pick one date that's currently on the page when build runs (ERAS.recent)
+# and also fetch the day before, which NEMOSIS's date generator buffers for.
+
+# Each AEMO scrape file may contain several logical tables in one CSV,
+# delimited by 'I' header rows. NEMOSIS only reads specific sections — we
+# filter those by column-value and strip the D-rows of everything else,
+# which preserves the I-row structure NEMOSIS's section-counter relies on.
+# sections_to_keep: section-index → filter dict
+SCRAPE_FILES = {
+ "Reports/Current/Daily_Reports/": {
+ "stub_template": "PUBLIC_DAILY_{ymd}",
+ "sections_to_keep": {1: {"REGIONID": spec.REGIONS}},
+ },
+ "Reports/Current/NEXT_DAY_DISPATCH/": {
+ "stub_template": "PUBLIC_NEXT_DAY_DISPATCH_{ymd}",
+ "sections_to_keep": {0: {"DUID": spec.DUIDS}},
+ },
+ "Reports/Current/Next_Day_Intermittent_Gen_Scada/": {
+ "stub_template": "PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_{ymd}",
+ "sections_to_keep": {0: {"DUID": spec.DUIDS}},
+ },
+}
+
+# Which NEMOSIS table maps to which scrape file.
+TABLE_SCRAPE_FILE = {
+ "DAILY_REGION_SUMMARY": "Reports/Current/Daily_Reports/",
+ "NEXT_DAY_DISPATCHLOAD": "Reports/Current/NEXT_DAY_DISPATCH/",
+ "INTERMITTENT_GEN_SCADA": "Reports/Current/Next_Day_Intermittent_Gen_Scada/",
+}
+
+
+def build_scrape_day(table: str, day: date, *, rebuild: bool) -> None:
+ """Download, filter, and write one day's scrape-pattern fixture.
+
+ Also rewrites the directory's fake index.html to list every fixture
+ file currently present (so a later build for a different day adds a
+ link rather than replacing the page).
+ """
+ path = TABLE_SCRAPE_FILE[table]
+ cfg = SCRAPE_FILES[path]
+ ymd = f"{day.year}{day.month:02d}{day.day:02d}"
+ stub = cfg["stub_template"].format(ymd=ymd)
+ fixture_dir = FIXTURE_ROOT / path
+
+ existing_match = next(
+ (p for p in fixture_dir.glob("*") if stub in p.name and p.suffix != ".html"),
+ None,
+ )
+ if existing_match and not rebuild:
+ log.info("skip (cached) %s", existing_match.relative_to(FIXTURE_ROOT))
+ else:
+ href, zip_bytes = _fetch_matching_link(path, stub)
+ filename = href.split("/")[-1]
+ filtered_zip = _filter_multisection_zip(zip_bytes, cfg["sections_to_keep"])
+ write_file(fixture_dir / filename, filtered_zip)
+
+ _rewrite_index(fixture_dir)
+
+
+def _fetch_matching_link(path: str, stub: str) -> tuple[str, bytes]:
+ """Mirror NEMOSIS's scraper: hit the index, find an anchor whose href
+ contains the stub, download it. Returns (href, zip-bytes)."""
+ index_url = defaults.nem_web_domain_url.rstrip("/") + "/" + path
+ soup = BeautifulSoup(http_get(index_url).text, "html.parser")
+ domain = defaults.nem_web_domain_url.rstrip("/")
+ for link in soup.find_all("a"):
+ href = link.get("href") or ""
+ if stub not in href:
+ continue
+ if href.startswith("http"):
+ zip_url = href
+ elif href.startswith("/"):
+ zip_url = domain + href # absolute path on the domain
+ else:
+ zip_url = index_url.rstrip("/") + "/" + href.lstrip("./")
+ return href, http_get(zip_url).content
+ raise RuntimeError(f"no link matching {stub!r} on {index_url}")
+
+
+def _rewrite_index(directory: Path) -> None:
+ directory.mkdir(parents=True, exist_ok=True)
+ filenames = sorted(
+ p.name for p in directory.iterdir() if p.is_file() and p.suffix != ".html"
+ )
+ url_prefix = directory.relative_to(FIXTURE_ROOT).as_posix()
+ write_file(directory / "index.html",
+ make_index_html(filenames, url_prefix).encode("utf-8"))
+
+
+# ---------------------------------------------------------------------------
+# Multi-section AEMO CSV parsing (for BIDMOVE_COMPLETE, PUBLIC_DAILY, etc.)
+# ---------------------------------------------------------------------------
+# Several AEMO zip archives contain a single CSV holding multiple logical
+# tables — each delimited by an 'I' header row, ending with a 'C' footer.
+# We split at I-rows, filter each section independently (so BIDDAYOFFER_D
+# gets filtered by DUID, and so does BIDPEROFFER_D alongside it).
+
+def _filter_multisection_zip(zip_bytes: bytes, sections_to_keep: dict[int, dict]) -> bytes:
+ """Filter sections listed in `sections_to_keep` by their column-values,
+ and drop all D rows from every other section. I rows stay untouched so
+ NEMOSIS's section-counting (_find_start_row_nth_table) still matches up.
+ """
+ csv_name, csv_bytes = read_single_csv_from_zip(zip_bytes)
+ text = csv_bytes.decode("utf-8")
+ lines = text.splitlines(keepends=True)
+ summary = lines[0]
+ footer = lines[-1] if lines[-1].startswith("C,") else ""
+ body = lines[1:-1] if footer else lines[1:]
+
+ sections = _split_mms_sections(body)
+ for idx, section in enumerate(sections):
+ if idx in sections_to_keep:
+ section.update(_filter_one_section(section, sections_to_keep[idx]))
+ else:
+ section["data"] = [] # strip — NEMOSIS never reads this section
+
+ rebuilt = summary
+ for section in sections:
+ rebuilt += section["header"]
+ rebuilt += "".join(section["data"])
+ rebuilt += footer
+ return pack_csv_as_zip(csv_name, rebuilt.encode("utf-8"))
+
+
+def _split_mms_sections(body_lines: list[str]) -> list[dict]:
+ sections: list[dict] = []
+ current: dict | None = None
+ for line in body_lines:
+ if line.startswith("I,"):
+ if current is not None:
+ sections.append(current)
+ current = {"header": line, "data": []}
+ elif line.startswith("D,") and current is not None:
+ current["data"].append(line)
+ if current is not None:
+ sections.append(current)
+ return sections
+
+
+def _filter_one_section(section: dict, filters: dict[str, list]) -> dict:
+ if not section["data"]:
+ return section
+ df = pd.read_csv(
+ io.StringIO(section["header"] + "".join(section["data"])),
+ dtype=str, keep_default_na=False,
+ )
+ for col, allowed in filters.items():
+ if col in df.columns:
+ df = df[df[col].isin([str(v) for v in allowed])]
+ out = io.StringIO()
+ df.to_csv(out, index=False, lineterminator="\n")
+ lines = out.getvalue().splitlines(keepends=True)
+ return {"header": lines[0], "data": lines[1:]}
+
+
+def previous_day(day: date) -> date:
+ from datetime import timedelta
+ return day - timedelta(days=1)
+
+
+# ---------------------------------------------------------------------------
+# Entry point
+# ---------------------------------------------------------------------------
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--rebuild", action="store_true",
+ help="re-download and overwrite existing fixtures")
+ parser.add_argument("-v", "--verbose", action="store_true")
+ args = parser.parse_args()
+
+ logging.basicConfig(
+ level=logging.DEBUG if args.verbose else logging.INFO,
+ format="%(levelname)-5s %(message)s",
+ )
+
+ failed: list[tuple[str, str, str]] = []
+
+ for table, cfg in spec.DYNAMIC_TABLES.items():
+ # `extra_eras` are built but not picked up by `boundary_cases` — used
+ # for standalone tests whose windows don't fit the matrix's shape
+ # (e.g. straddling a stride-transition month like 2021-10).
+ for era_key in cfg["eras"] + cfg.get("extra_eras", []):
+ era_date = spec.ERAS[era_key]
+ try:
+ if _is_scrape_only(table, era_date):
+ build_scrape_day(table, era_date, rebuild=args.rebuild)
+ # NEMOSIS buffers one day back for scrape tables.
+ try:
+ build_scrape_day(table, previous_day(era_date), rebuild=args.rebuild)
+ except (requests.HTTPError, RuntimeError) as e:
+ log.info("prev-day buffer unavailable for %s @ %s (%s)",
+ table, era_key, e)
+ else:
+ build_mms_month(table, era_date, rebuild=args.rebuild)
+ try:
+ build_mms_month(table, previous_month(era_date), rebuild=args.rebuild)
+ except requests.HTTPError as e:
+ log.info("prev-month buffer unavailable for %s @ %s (%s)",
+ table, era_key, e)
+ except (requests.HTTPError, RuntimeError) as e:
+ failed.append((table, era_key, str(e)))
+ log.error("FAILED %s @ %s — %s", table, era_key, e)
+
+ for name in spec.STATIC_TABLES:
+ builder = {
+ "ELEMENTS_FCAS_4_SECOND": build_static_elements_fcas,
+ "VARIABLES_FCAS_4_SECOND": build_static_variables_fcas,
+ "Generators and Scheduled Loads": build_static_generators_xls,
+ }[name]
+ try:
+ builder(rebuild=args.rebuild)
+ except requests.HTTPError as e:
+ failed.append((name, "static", str(e)))
+ log.error("FAILED %s — %s", name, e)
+
+ if failed:
+ log.error("%d fixture(s) failed — fix the spec or retry:", len(failed))
+ for table, era, err in failed:
+ log.error(" %s @ %s: %s", table, era, err)
+ sys.exit(1)
+
+
+def _is_scrape_only(table: str, era_date: date) -> bool:
+ """Some AEMO tables are only published on rolling current-data pages, not
+ in the MMS monthly archive. (BIDPEROFFER_D and BIDDAYOFFER_D use MMS —
+ NEMOSIS's dead `BIDDING` code path would go through the Bidmove_Complete
+ scrape, but no table is actually wired to it.)"""
+ return table in ("DAILY_REGION_SUMMARY", "NEXT_DAY_DISPATCHLOAD", "INTERMITTENT_GEN_SCADA")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/fixtures/data/-/media/files/electricity/nem/Participant_Information/NEM-Registration-and-Exemption-List.xlsx b/tests/fixtures/data/-/media/files/electricity/nem/Participant_Information/NEM-Registration-and-Exemption-List.xlsx
new file mode 100644
index 0000000..bc14646
Binary files /dev/null and b/tests/fixtures/data/-/media/files/electricity/nem/Participant_Information/NEM-Registration-and-Exemption-List.xlsx differ
diff --git a/tests/fixtures/data/-/media/files/electricity/nem/settlements_and_payments/settlements/auction-reports/archive/ancillary-services-market-causer-pays-variables-file.csv b/tests/fixtures/data/-/media/files/electricity/nem/settlements_and_payments/settlements/auction-reports/archive/ancillary-services-market-causer-pays-variables-file.csv
new file mode 100644
index 0000000..1f86e73
--- /dev/null
+++ b/tests/fixtures/data/-/media/files/electricity/nem/settlements_and_payments/settlements/auction-reports/archive/ancillary-services-market-causer-pays-variables-file.csv
@@ -0,0 +1,19 @@
+1,"MW"
+2,"Gen_MW"
+3,"GenSPD_MW"
+4,"GenRPF_%"
+5,"GenRegComp_MW"
+6,"Spare06"
+7,"Spare07"
+8,"Spare08"
+9,"ON"
+10,"OFF"
+11,"ACE"
+12,"ACEFIL"
+13,"HZ"
+14,"HZNOM"
+15,"SEC"
+16,"ACEINT"
+17,"HZOFFSET"
+18,"HZDEV"
+19,"SPD_MWB"
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201804010000.zip
new file mode 100644
index 0000000..c66488a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201804010000.zip
new file mode 100644
index 0000000..6c8b1c1
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201804010000.zip
new file mode 100644
index 0000000..c17809c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201804010000.zip
new file mode 100644
index 0000000..be9c5bf
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201804010000.zip
new file mode 100644
index 0000000..48d4f6b
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201804010000.zip
new file mode 100644
index 0000000..c9a0a2a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201804010000.zip
new file mode 100644
index 0000000..acf2b18
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201804010000.zip
new file mode 100644
index 0000000..7220e2c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201804010000.zip
new file mode 100644
index 0000000..9a3425c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201804010000.zip
new file mode 100644
index 0000000..dbffdc6
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_201804010000.zip
new file mode 100644
index 0000000..2fdc63a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201804010000.zip
new file mode 100644
index 0000000..a23dbcc
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201804010000.zip
new file mode 100644
index 0000000..e8a270f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201804010000.zip
new file mode 100644
index 0000000..55d5987
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201804010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201804010000.zip
new file mode 100644
index 0000000..ef6396f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201804010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201805010000.zip
new file mode 100644
index 0000000..8225284
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201805010000.zip
new file mode 100644
index 0000000..1cf80ec
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201805010000.zip
new file mode 100644
index 0000000..caec0e9
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201805010000.zip
new file mode 100644
index 0000000..fb5e45b
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201805010000.zip
new file mode 100644
index 0000000..f577f3c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201805010000.zip
new file mode 100644
index 0000000..7868422
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201805010000.zip
new file mode 100644
index 0000000..9abe38a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201805010000.zip
new file mode 100644
index 0000000..b8eee6d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201805010000.zip
new file mode 100644
index 0000000..5766bfb
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201805010000.zip
new file mode 100644
index 0000000..739f409
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_201805010000.zip
new file mode 100644
index 0000000..1c222fe
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201805010000.zip
new file mode 100644
index 0000000..db6aefe
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201805010000.zip
new file mode 100644
index 0000000..a000235
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201805010000.zip
new file mode 100644
index 0000000..8746ffb
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201805010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201805010000.zip
new file mode 100644
index 0000000..10f863c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2018/MMSDM_2018_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201805010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201912010000.zip
new file mode 100644
index 0000000..fdce5b3
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201912010000.zip
new file mode 100644
index 0000000..97bb46a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201912010000.zip
new file mode 100644
index 0000000..1c8ae80
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201912010000.zip
new file mode 100644
index 0000000..e3f1bc6
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201912010000.zip
new file mode 100644
index 0000000..8cc508d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201912010000.zip
new file mode 100644
index 0000000..e9609f5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201912010000.zip
new file mode 100644
index 0000000..fe0895a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201912010000.zip
new file mode 100644
index 0000000..c4b6730
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201912010000.zip
new file mode 100644
index 0000000..67d7341
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201912010000.zip
new file mode 100644
index 0000000..b022e45
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_201912010000.zip
new file mode 100644
index 0000000..1ab82c3
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201912010000.zip
new file mode 100644
index 0000000..44aff28
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201912010000.zip
new file mode 100644
index 0000000..4e71715
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201912010000.zip
new file mode 100644
index 0000000..4f87743
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201912010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201912010000.zip
new file mode 100644
index 0000000..96b8bcb
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2019/MMSDM_2019_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_201912010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202001010000.zip
new file mode 100644
index 0000000..09ab124
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202001010000.zip
new file mode 100644
index 0000000..dbe2a44
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202001010000.zip
new file mode 100644
index 0000000..c03fc8f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202001010000.zip
new file mode 100644
index 0000000..a90b6e0
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202001010000.zip
new file mode 100644
index 0000000..cb9e35d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202001010000.zip
new file mode 100644
index 0000000..3980962
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202001010000.zip
new file mode 100644
index 0000000..9f8e3d5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202001010000.zip
new file mode 100644
index 0000000..7a8d527
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202001010000.zip
new file mode 100644
index 0000000..fd95118
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202001010000.zip
new file mode 100644
index 0000000..fdf923a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202001010000.zip
new file mode 100644
index 0000000..28fefef
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202001010000.zip
new file mode 100644
index 0000000..cc3aaea
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202001010000.zip
new file mode 100644
index 0000000..cde5200
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202001010000.zip
new file mode 100644
index 0000000..5ecaa97
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202001010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202001010000.zip
new file mode 100644
index 0000000..277b54f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2020/MMSDM_2020_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202001010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202101010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202101010000.zip
new file mode 100644
index 0000000..f2a3538
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202101010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202101010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202101010000.zip
new file mode 100644
index 0000000..6860154
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202101010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202101010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202101010000.zip
new file mode 100644
index 0000000..bd4b64f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202101010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202102010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202102010000.zip
new file mode 100644
index 0000000..e7ee2a5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDDAYOFFER_D_202102010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202102010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202102010000.zip
new file mode 100644
index 0000000..adfeb75
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_BIDPEROFFER_D_202102010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202102010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202102010000.zip
new file mode 100644
index 0000000..5aa11aa
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_02/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_PEROFFER_202102010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202104010000.zip
new file mode 100644
index 0000000..2ef134e
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202104010000.zip
new file mode 100644
index 0000000..3dacc86
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202104010000.zip
new file mode 100644
index 0000000..da1f970
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202104010000.zip
new file mode 100644
index 0000000..b698be7
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202104010000.zip
new file mode 100644
index 0000000..48776f1
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202104010000.zip
new file mode 100644
index 0000000..61f48bc
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAILSUMMARY_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAILSUMMARY_202104010000.zip
new file mode 100644
index 0000000..0968969
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAILSUMMARY_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAIL_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAIL_202104010000.zip
new file mode 100644
index 0000000..3e8845a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAIL_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_GENCONDATA_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_GENCONDATA_202104010000.zip
new file mode 100644
index 0000000..eb846b8
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_GENCONDATA_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTORCONSTRAINT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTORCONSTRAINT_202104010000.zip
new file mode 100644
index 0000000..0f48460
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTORCONSTRAINT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTOR_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTOR_202104010000.zip
new file mode 100644
index 0000000..e058e49
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTOR_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSFACTORMODEL_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSFACTORMODEL_202104010000.zip
new file mode 100644
index 0000000..b337872
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSFACTORMODEL_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSMODEL_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSMODEL_202104010000.zip
new file mode 100644
index 0000000..5631880
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSMODEL_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MARKET_PRICE_THRESHOLDS_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MARKET_PRICE_THRESHOLDS_202104010000.zip
new file mode 100644
index 0000000..1625ee1
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MARKET_PRICE_THRESHOLDS_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202104010000.zip
new file mode 100644
index 0000000..c4fee46
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_INTERCONNECTOR_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_INTERCONNECTOR_202104010000.zip
new file mode 100644
index 0000000..1d981b5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_INTERCONNECTOR_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_202104010000.zip
new file mode 100644
index 0000000..67c9a15
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202104010000.zip
new file mode 100644
index 0000000..aaea616
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDCONNECTIONPOINTCONSTRAINT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDCONNECTIONPOINTCONSTRAINT_202104010000.zip
new file mode 100644
index 0000000..862c560
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDCONNECTIONPOINTCONSTRAINT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDINTERCONNECTORCONSTRAINT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDINTERCONNECTORCONSTRAINT_202104010000.zip
new file mode 100644
index 0000000..afdbfab
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDINTERCONNECTORCONSTRAINT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDREGIONCONSTRAINT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDREGIONCONSTRAINT_202104010000.zip
new file mode 100644
index 0000000..ba41d9d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDREGIONCONSTRAINT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202104010000.zip
new file mode 100644
index 0000000..da7a97f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202104010000.zip
new file mode 100644
index 0000000..dd70a0c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202104010000.zip
new file mode 100644
index 0000000..a8c98a7
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202104010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202104010000.zip
new file mode 100644
index 0000000..c1b16e4
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_04/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202104010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202105010000.zip
new file mode 100644
index 0000000..1c08ed9
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202105010000.zip
new file mode 100644
index 0000000..b6518ed
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202105010000.zip
new file mode 100644
index 0000000..ddddc85
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202105010000.zip
new file mode 100644
index 0000000..dad377f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202105010000.zip
new file mode 100644
index 0000000..fdc5031
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202105010000.zip
new file mode 100644
index 0000000..640df17
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAILSUMMARY_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAILSUMMARY_202105010000.zip
new file mode 100644
index 0000000..23d77d9
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAILSUMMARY_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAIL_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAIL_202105010000.zip
new file mode 100644
index 0000000..efe20ef
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DUDETAIL_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_GENCONDATA_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_GENCONDATA_202105010000.zip
new file mode 100644
index 0000000..a31a8d8
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_GENCONDATA_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTORCONSTRAINT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTORCONSTRAINT_202105010000.zip
new file mode 100644
index 0000000..4a656c4
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTORCONSTRAINT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTOR_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTOR_202105010000.zip
new file mode 100644
index 0000000..6795083
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_INTERCONNECTOR_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSFACTORMODEL_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSFACTORMODEL_202105010000.zip
new file mode 100644
index 0000000..bb31d31
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSFACTORMODEL_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSMODEL_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSMODEL_202105010000.zip
new file mode 100644
index 0000000..79fdd80
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_LOSSMODEL_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MARKET_PRICE_THRESHOLDS_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MARKET_PRICE_THRESHOLDS_202105010000.zip
new file mode 100644
index 0000000..46ee23e
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MARKET_PRICE_THRESHOLDS_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202105010000.zip
new file mode 100644
index 0000000..e5ce5d7
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_DAYOFFER_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_INTERCONNECTOR_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_INTERCONNECTOR_202105010000.zip
new file mode 100644
index 0000000..fd68872
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_MNSP_INTERCONNECTOR_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_202105010000.zip
new file mode 100644
index 0000000..f25a855
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_PARTICIPANT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202105010000.zip
new file mode 100644
index 0000000..abc52d3
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDCONNECTIONPOINTCONSTRAINT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDCONNECTIONPOINTCONSTRAINT_202105010000.zip
new file mode 100644
index 0000000..1c0bde5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDCONNECTIONPOINTCONSTRAINT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDINTERCONNECTORCONSTRAINT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDINTERCONNECTORCONSTRAINT_202105010000.zip
new file mode 100644
index 0000000..4bf1f6a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDINTERCONNECTORCONSTRAINT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDREGIONCONSTRAINT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDREGIONCONSTRAINT_202105010000.zip
new file mode 100644
index 0000000..e1cd6e9
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_SPDREGIONCONSTRAINT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202105010000.zip
new file mode 100644
index 0000000..302fc8c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202105010000.zip
new file mode 100644
index 0000000..6c36008
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGLOAD_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202105010000.zip
new file mode 100644
index 0000000..eb183e3
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202105010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202105010000.zip
new file mode 100644
index 0000000..b14d7cc
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGREGIONSUM_202105010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202109010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202109010000.zip
new file mode 100644
index 0000000..4eed940
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202109010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202109010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202109010000.zip
new file mode 100644
index 0000000..6ed2124
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202109010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_10/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202110010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_10/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202110010000.zip
new file mode 100644
index 0000000..e08b6a4
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_10/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202110010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_10/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202110010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_10/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202110010000.zip
new file mode 100644
index 0000000..999af24
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_10/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202110010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202112010000.zip
new file mode 100644
index 0000000..ea1efa4
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202112010000.zip
new file mode 100644
index 0000000..914e028
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202112010000.zip
new file mode 100644
index 0000000..3752bc6
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202112010000.zip
new file mode 100644
index 0000000..f3f30b3
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202112010000.zip
new file mode 100644
index 0000000..b3dae2d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202112010000.zip
new file mode 100644
index 0000000..c64af77
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202112010000.zip
new file mode 100644
index 0000000..e3945c8
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202112010000.zip
new file mode 100644
index 0000000..9053f02
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202112010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202112010000.zip
new file mode 100644
index 0000000..f258f6d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2021/MMSDM_2021_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202112010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202201010000.zip
new file mode 100644
index 0000000..69da75b
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202201010000.zip
new file mode 100644
index 0000000..36ebd21
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202201010000.zip
new file mode 100644
index 0000000..28c88f8
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202201010000.zip
new file mode 100644
index 0000000..f789453
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202201010000.zip
new file mode 100644
index 0000000..104b13e
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202201010000.zip
new file mode 100644
index 0000000..4c9263f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202201010000.zip
new file mode 100644
index 0000000..93d76de
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202201010000.zip
new file mode 100644
index 0000000..be1c772
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202201010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202201010000.zip
new file mode 100644
index 0000000..331d868
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202201010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202205010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202205010000.zip
new file mode 100644
index 0000000..9dce3b5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202205010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202205010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202205010000.zip
new file mode 100644
index 0000000..32f0408
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_05/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202205010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_06/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202206010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_06/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202206010000.zip
new file mode 100644
index 0000000..abd0df4
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_06/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202206010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_06/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202206010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_06/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202206010000.zip
new file mode 100644
index 0000000..8ddeda2
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2022/MMSDM_2022_06/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202206010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202407010000.zip
new file mode 100644
index 0000000..eab9a6d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHCONSTRAINT_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202407010000.zip
new file mode 100644
index 0000000..bb27691
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHINTERCONNECTORRES_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202407010000.zip
new file mode 100644
index 0000000..1f648dc
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHLOAD_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202407010000.zip
new file mode 100644
index 0000000..8e13100
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHPRICE_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202407010000.zip
new file mode 100644
index 0000000..0249acd
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCHREGIONSUM_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202407010000.zip
new file mode 100644
index 0000000..8f8b26f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_DISPATCH_UNIT_SCADA_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202407010000.zip
new file mode 100644
index 0000000..7385667
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_ROOFTOP_PV_ACTUAL_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202407010000.zip
new file mode 100644
index 0000000..95dda01
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGINTERCONNECT_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202407010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202407010000.zip
new file mode 100644
index 0000000..8bf809c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_07/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_DVD_TRADINGPRICE_202407010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202408010000.zip
new file mode 100644
index 0000000..08fbf6c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202408010000.zip
new file mode 100644
index 0000000..81d35c2
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202408010000.zip
new file mode 100644
index 0000000..75f4e00
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202408010000.zip
new file mode 100644
index 0000000..b6b7cbb
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202408010000.zip
new file mode 100644
index 0000000..84bd753
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202408010000.zip
new file mode 100644
index 0000000..0f8b5df
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202408010000.zip
new file mode 100644
index 0000000..b06677b
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202408010000.zip
new file mode 100644
index 0000000..388059c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202408010000.zip
new file mode 100644
index 0000000..da01595
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202408010000.zip
new file mode 100644
index 0000000..95ce871
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202408010000.zip
new file mode 100644
index 0000000..811b166
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202408010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202408010000.zip
new file mode 100644
index 0000000..01cfda6
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_08/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202408010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202409010000.zip
new file mode 100644
index 0000000..47ccba8
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202409010000.zip
new file mode 100644
index 0000000..4f98c6d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202409010000.zip
new file mode 100644
index 0000000..c1c746d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202409010000.zip
new file mode 100644
index 0000000..9aa680f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202409010000.zip
new file mode 100644
index 0000000..c8aad8a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202409010000.zip
new file mode 100644
index 0000000..23790f2
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202409010000.zip
new file mode 100644
index 0000000..7a7cac4
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202409010000.zip
new file mode 100644
index 0000000..272d5b5
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202409010000.zip
new file mode 100644
index 0000000..3083e13
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202409010000.zip
new file mode 100644
index 0000000..13dd2fb
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202409010000.zip
new file mode 100644
index 0000000..1d20691
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202409010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202409010000.zip
new file mode 100644
index 0000000..216c055
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_09/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202409010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202412010000.zip
new file mode 100644
index 0000000..4313e2a
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202412010000.zip
new file mode 100644
index 0000000..88da9fd
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202412010000.zip
new file mode 100644
index 0000000..5dade2d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202412010000.zip
new file mode 100644
index 0000000..7e46ae2
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202412010000.zip
new file mode 100644
index 0000000..5d8795c
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202412010000.zip
new file mode 100644
index 0000000..597c6fc
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202412010000.zip
new file mode 100644
index 0000000..3db3c37
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202412010000.zip
new file mode 100644
index 0000000..b324440
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202412010000.zip
new file mode 100644
index 0000000..f927bd6
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202412010000.zip
new file mode 100644
index 0000000..1e8d354
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202412010000.zip
new file mode 100644
index 0000000..222a5bb
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202412010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202412010000.zip
new file mode 100644
index 0000000..57a5e7d
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2024/MMSDM_2024_12/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202412010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202501010000.zip
new file mode 100644
index 0000000..6e2a0a2
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDDAYOFFER_D#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202501010000.zip
new file mode 100644
index 0000000..a9dad68
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#BIDPEROFFER_D#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202501010000.zip
new file mode 100644
index 0000000..5aed03e
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHCONSTRAINT#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202501010000.zip
new file mode 100644
index 0000000..3b8c17e
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHINTERCONNECTORRES#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202501010000.zip
new file mode 100644
index 0000000..f886ca0
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHLOAD#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202501010000.zip
new file mode 100644
index 0000000..ffd5979
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHPRICE#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202501010000.zip
new file mode 100644
index 0000000..d7cdd61
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCHREGIONSUM#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202501010000.zip
new file mode 100644
index 0000000..e2d5415
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#DISPATCH_UNIT_SCADA#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202501010000.zip
new file mode 100644
index 0000000..b35d863
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#MNSP_DAYOFFER#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202501010000.zip
new file mode 100644
index 0000000..a0d14c7
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#ROOFTOP_PV_ACTUAL#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202501010000.zip
new file mode 100644
index 0000000..c650031
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGINTERCONNECT#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202501010000.zip b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202501010000.zip
new file mode 100644
index 0000000..180e39f
Binary files /dev/null and b/tests/fixtures/data/Data_Archive/Wholesale_Electricity/MMSDM/2025/MMSDM_2025_01/MMSDM_Historical_Data_SQLLoader/DATA/PUBLIC_ARCHIVE#TRADINGPRICE#FILE01#202501010000.zip differ
diff --git a/tests/fixtures/data/Reports/Current/Causer_Pays_Elements/Elements_FCAS_202504151310.csv b/tests/fixtures/data/Reports/Current/Causer_Pays_Elements/Elements_FCAS_202504151310.csv
new file mode 100644
index 0000000..74e542b
--- /dev/null
+++ b/tests/fixtures/data/Reports/Current/Causer_Pays_Elements/Elements_FCAS_202504151310.csv
@@ -0,0 +1,517 @@
+1,"SUBSTN.LYPA.GEN.A1GEN ","GEN","*MMS MarketName*"
+2,"SUBSTN.LYPA.GEN.A2GEN ","GEN","*MMS MarketName*"
+3,"SUBSTN.LYPA.GEN.A3GEN ","GEN","*MMS MarketName*"
+4,"SUBSTN.LYPA.GEN.A4GEN ","GEN","*MMS MarketName*"
+5,"SUBSTN.LYPB.GEN.B1GEN ","GEN","*MMS MarketName*"
+6,"SUBSTN.LYPB.GEN.B2GEN ","GEN","*MMS MarketName*"
+7,"SUBSTN.YPS.GEN.W1GEN ","GEN","*MMS MarketName*"
+8,"SUBSTN.YPS.GEN.W2GEN ","GEN","*MMS MarketName*"
+9,"SUBSTN.YPS.GEN.W3GEN ","GEN","*MMS MarketName*"
+10,"SUBSTN.YPS.GEN.W4GEN ","GEN","*MMS MarketName*"
+11,"SUBSTN.NPSD.GEN.GEN ","GEN","*MMS MarketName*"
+24,"SUBSTN.JLGSA.GEN.A1GEN ","GEN","*MMS MarketName*"
+25,"SUBSTN.JLGSA.GEN.A2GEN ","GEN","*MMS MarketName*"
+26,"SUBSTN.JLGSA.GEN.A3GEN ","GEN","*MMS MarketName*"
+27,"SUBSTN.JLGSA.GEN.A4GEN ","GEN","*MMS MarketName*"
+28,"SUBSTN.JLGSB.GEN.B1GEN ","GEN","*MMS MarketName*"
+29,"SUBSTN.JLGSB.GEN.B2GEN ","GEN","*MMS MarketName*"
+30,"SUBSTN.JLGSB.GEN.B3GEN ","GEN","*MMS MarketName*"
+31,"SUBSTN.DPS.GEN.GEN ","GEN","*MMS MarketName*"
+32,"SUBSTN.EPS.GEN.1GEN ","GEN","*MMS MarketName*"
+33,"SUBSTN.EPS.GEN.2GEN ","GEN","*MMS MarketName*"
+36,"SUBSTN.WKPS.GEN.GEN_1&2 ","GEN","*MMS MarketName*"
+37,"SUBSTN.WKPS.GEN.GEN_3&4 ","GEN","*MMS MarketName*"
+41,"SUBSTN.TUMUT1_2.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+42,"SUBSTN.TUMUT3.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+43,"SUBSTN.MURRAY.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+44,"SUBSTN.GUTHEGA.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+45,"SUBSTN.JINDABYN.UNIT.1 ","GEN","*MMS MarketName*"
+46,"SUBSTN.JINDABYN.UNIT.2 ","GEN","*MMS MarketName*"
+47,"SUBSTN.BAYSWATR.UNIT.1 ","GEN","*MMS MarketName*"
+48,"SUBSTN.BAYSWATR.UNIT.2 ","GEN","*MMS MarketName*"
+49,"SUBSTN.BAYSWATR.UNIT.3 ","GEN","*MMS MarketName*"
+50,"SUBSTN.BAYSWATR.UNIT.4 ","GEN","*MMS MarketName*"
+51,"SUBSTN.ERARING.UNIT.1 ","GEN","*MMS MarketName*"
+52,"SUBSTN.ERARING.UNIT.2 ","GEN","*MMS MarketName*"
+53,"SUBSTN.ERARING.UNIT.3 ","GEN","*MMS MarketName*"
+54,"SUBSTN.ERARING.UNIT.4 ","GEN","*MMS MarketName*"
+55,"SUBSTN.MT_PIPER.UNIT.1 ","GEN","*MMS MarketName*"
+56,"SUBSTN.MT_PIPER.UNIT.2 ","GEN","*MMS MarketName*"
+57,"SUBSTN.VALES_PT.UNIT.5 ","GEN","*MMS MarketName*"
+58,"SUBSTN.VALES_PT.UNIT.6 ","GEN","*MMS MarketName*"
+59,"SUBSTN.LIDDELL.UNIT.1 ","GEN","*MMS MarketName*"
+60,"SUBSTN.LIDDELL.UNIT.2 ","GEN","*MMS MarketName*"
+61,"SUBSTN.LIDDELL.UNIT.3 ","GEN","*MMS MarketName*"
+62,"SUBSTN.LIDDELL.UNIT.4 ","GEN","*MMS MarketName*"
+69,"SUBSTN.BRKNHILL.UNIT.GT1 ","GEN","*MMS MarketName*"
+70,"SUBSTN.BRKNHILL.UNIT.GT2 ","GEN","*MMS MarketName*"
+76,"SUBSTN.HUME.UNIT.HUMENSW ","GEN","*MMS MarketName*"
+77,"SUBSTN.HUME.UNIT.HUMEVIC ","GEN","*MMS MarketName*"
+79,"SUBSTN.BLOWERG.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+82,"SUBSTN.TIPS_A.GEN.TA_1 ","GEN","*MMS MarketName*"
+84,"SUBSTN.TIPS_A.GEN.TA_3 ","GEN","*MMS MarketName*"
+86,"SUBSTN.TIPS_B.GEN.TB_1 ","GEN","*MMS MarketName*"
+87,"SUBSTN.TIPS_B.GEN.TB_2 ","GEN","*MMS MarketName*"
+88,"SUBSTN.TIPS_B.GEN.TB_3 ","GEN","*MMS MarketName*"
+89,"SUBSTN.TIPS_B.GEN.TB_4 ","GEN","*MMS MarketName*"
+90,"SUBSTN.PEL_PS.GEN.PP_1 ","GEN","*MMS MarketName*"
+92,"SUBSTN.DRY_CK.GEN.DC_1 ","GEN","*MMS MarketName*"
+93,"SUBSTN.DRY_CK.GEN.DC_2 ","GEN","*MMS MarketName*"
+94,"SUBSTN.DRY_CK.GEN.DC_3 ","GEN","*MMS MarketName*"
+95,"SUBSTN.MINTAR.GEN.MN_1 ","GEN","*MMS MarketName*"
+96,"SUBSTN.SNUGGY.GEN.SN_1_2_3 ","GEN","*MMS MarketName*"
+97,"SUBSTN.OCPL.GEN.OC_1_2 ","GEN","*MMS MarketName*"
+98,"SUBSTN.LADB_G.GEN.LG_1 ","GEN","*MMS MarketName*"
+99,"SUBSTN.LADB_G.GEN.LG_2 ","GEN","*MMS MarketName*"
+100,"SUBSTN.PT_L_T.GEN.PL_1_2 ","GEN","*MMS MarketName*"
+101,"SUBSTN.TRNG_PS.GEN.1_TRNG_PS ","GEN","*MMS MarketName*"
+102,"SUBSTN.TRNG_PS.GEN.2_TRNG_PS ","GEN","*MMS MarketName*"
+103,"SUBSTN.TRNG_PS.GEN.3_TRNG_PS ","GEN","*MMS MarketName*"
+104,"SUBSTN.TRNG_PS.GEN.4_TRNG_PS ","GEN","*MMS MarketName*"
+105,"SUBSTN.TRNG_PS.GEN.GT_TRNG_PS ","GEN","*MMS MarketName*"
+106,"SUBSTN.STAN_PS.GEN.1_STAN_PS ","GEN","*MMS MarketName*"
+107,"SUBSTN.STAN_PS.GEN.2_STAN_PS ","GEN","*MMS MarketName*"
+108,"SUBSTN.STAN_PS.GEN.3_STAN_PS ","GEN","*MMS MarketName*"
+109,"SUBSTN.STAN_PS.GEN.4_STAN_PS ","GEN","*MMS MarketName*"
+114,"SUBSTN.CAL_B_PS.GEN.1_CAL_B_PS ","GEN","*MMS MarketName*"
+115,"SUBSTN.CAL_B_PS.GEN.2_CAL_B_PS ","GEN","*MMS MarketName*"
+116,"SUBSTN.CAL_C_PS.GEN.3_CAL_C_PS ","GEN","*MMS MarketName*"
+117,"SUBSTN.GLAD_PS.GEN.1_GLAD_PS ","GEN","*MMS MarketName*"
+118,"SUBSTN.GLAD_PS.GEN.2_GLAD_PS ","GEN","*MMS MarketName*"
+119,"SUBSTN.GLAD_PS.GEN.3_GLAD_PS ","GEN","*MMS MarketName*"
+120,"SUBSTN.GLAD_PS.GEN.4_GLAD_PS ","GEN","*MMS MarketName*"
+121,"SUBSTN.GLAD_PS.GEN.5_GLAD_PS ","GEN","*MMS MarketName*"
+122,"SUBSTN.GLAD_PS.GEN.6_GLAD_PS ","GEN","*MMS MarketName*"
+128,"SUBSTN.T36_INVA.GEN.1_T36_INVA ","GEN","*MMS MarketName*"
+141,"SUBSTN.MT_S_PS.GEN.1_MT_S_PS ","GEN","*MMS MarketName*"
+142,"SUBSTN.MT_S_PS.GEN.2_MT_S_PS ","GEN","*MMS MarketName*"
+143,"SUBSTN.TVLLE_PS.GEN.GT_TVLLE_PS ","GEN","*MMS MarketName*"
+144,"SUBSTN.OAKEY_PS.GEN.1_OAK_PS ","GEN","*MMS MarketName*"
+145,"SUBSTN.OAKEY_PS.GEN.2_OAK_PS ","GEN","*MMS MarketName*"
+146,"SUBSTN.ROMA_PS.GEN.7_ROMA_PS ","GEN","*MMS MarketName*"
+147,"SUBSTN.ROMA_PS.GEN.8_ROMA_PS ","GEN","*MMS MarketName*"
+149,"SUBSTN.BCDE_PS.GEN.GT_BCDE_PS ","GEN","*MMS MarketName*"
+151,"SUBSTN.WIV_PS.GEN.1_WIV_PS ","GEN","*MMS MarketName*"
+152,"SUBSTN.WIV_PS.GEN.2_WIV_PS ","GEN","*MMS MarketName*"
+153,"SUBSTN.KAR_H_PS.GEN.1_KAR_H_PS ","GEN","*MMS MarketName*"
+154,"SUBSTN.KAR_H_PS.GEN.2_KAR_H_PS ","GEN","*MMS MarketName*"
+155,"SUBSTN.KAR_H_PS.GEN.3_KAR_H_PS ","GEN","*MMS MarketName*"
+156,"SUBSTN.KAR_H_PS.GEN.4_KAR_H_PS ","GEN","*MMS MarketName*"
+157,"SUBSTN.KAR_H_PS.GEN.5_KAR_H_PS ","GEN","*MMS MarketName*"
+158,"SUBSTN.T54_BG_H.GEN.1_B_G_H_PS ","GEN","*MMS MarketName*"
+159,"SUBSTN.T54_BG_H.GEN.2_B_G_H_PS ","GEN","*MMS MarketName*"
+160,"SUBSTN.BDL_PS.GEN.1GEN ","GEN","*MMS MarketName*"
+161,"SUBSTN.TUMUT3.LOAD.SUMM_PUMPS ","GEN","*MMS MarketName*"
+162,"SUBSTN.SITHE.UNIT.SITHE01 ","GEN","*MMS MarketName*"
+165,"SUBSTN.SHOALHAV.UNIT.BENDEELA_KAN ","GEN","*MMS MarketName*"
+166,"SUBSTN.SHOALHAV.SUMM.PUMPS ","GEN","*MMS MarketName*"
+167,"SUBSTN.WIV_PS.LOAD.1PUMP ","GEN","*MMS MarketName*"
+168,"SUBSTN.WIV_PS.LOAD.2PUMP ","GEN","*MMS MarketName*"
+169,"SUBSTN.CAL_C_PS.GEN.4_CAL_C_PS ","GEN","*MMS MarketName*"
+170,"SUBSTN.QPS.GEN.QP_1 ","GEN","*MMS MarketName*"
+171,"SUBSTN.QPS.GEN.QP_2 ","GEN","*MMS MarketName*"
+172,"SUBSTN.QPS.GEN.QP_3 ","GEN","*MMS MarketName*"
+173,"SUBSTN.QPS.GEN.QP_4 ","GEN","*MMS MarketName*"
+180,"SUBSTN.HALLET.GEN.HA_1 ","GEN","*MMS MarketName*"
+181,"SUBSTN.BDL_PS.GEN.2GEN ","GEN","*MMS MarketName*"
+182,"SUBSTN.SPS.GEN.GEN_1_2_3_4 ","GEN","*MMS MarketName*"
+183,"SUBSTN.SBK_E_PS.GEN.1_SBK_E_PS ","GEN","*MMS MarketName*"
+184,"SUBSTN.MILLM_PS.GEN.1_MILLM_PS ","GEN","*MMS MarketName*"
+185,"SUBSTN.MILLM_PS.GEN.2_MILLM_PS ","GEN","*MMS MarketName*"
+186,"SUBSTN.TRNGN_PS.GEN.1_TRNGN_PS ","GEN","*MMS MarketName*"
+189,"SUBSTN.TVLLE_PS.GEN.2_TVLLE_PS ","GEN","*MMS MarketName*"
+190,"SUBSTN.TUNGATIN.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+191,"SUBSTN.TRIBUTE.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+192,"SUBSTN.TREVALYN.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+193,"SUBSTN.TARRALEA.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+194,"SUBSTN.REECE.UNIT.AGGREGATE_1 ","GEN","*MMS MarketName*"
+195,"SUBSTN.REECE.UNIT.AGGREGATE_2 ","GEN","*MMS MarketName*"
+196,"SUBSTN.POATINA.UNIT.AGGREGATE_1 ","GEN","*MMS MarketName*"
+197,"SUBSTN.POATINA.UNIT.AGGREGATE_2 ","GEN","*MMS MarketName*"
+198,"SUBSTN.MDWBANK.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+199,"SUBSTN.MAKNTOSH.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+200,"SUBSTN.L_ECHO.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+201,"SUBSTN.LI_WY_CA.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+202,"SUBSTN.LEM_WIL.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+203,"SUBSTN.JBUTTERS.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+204,"SUBSTN.GORDON.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+205,"SUBSTN.FISHER.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+206,"SUBSTN.DEVILS_G.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+207,"SUBSTN.CETHANA.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+208,"SUBSTN.BASTYAN.UNIT.AGGREGATE ","GEN","*MMS MarketName*"
+212,"SUBSTN.BRAE_PS.GEN.1_BRAE_PS ","GEN","*MMS MarketName*"
+213,"SUBSTN.BRAE_PS.GEN.2_BRAE_PS ","GEN","*MMS MarketName*"
+214,"SUBSTN.BRAE_PS.GEN.3_BRAE_PS ","GEN","*MMS MarketName*"
+215,"SUBSTN.TAMAR_PS.UNIT.G101 ","GEN","*MMS MarketName*"
+216,"SUBSTN.TAMAR_PS.UNIT.G102 ","GEN","*MMS MarketName*"
+217,"SUBSTN.TAMAR_PS.UNIT.G103 ","GEN","*MMS MarketName*"
+219,"SUBSTN.MAYURA.GEN.LAKE_BONY2 ","GEN","*MMS MarketName*"
+220,"SUBSTN.WATT_P.GEN.WP_1 ","GEN","*MMS MarketName*"
+221,"SUBSTN.MT_MIL.TF.T_1 ","GEN","*MMS MarketName*"
+222,"SUBSTN.BUTLERS.UNIT.M/C ","GEN","*MMS MarketName*"
+223,"SUBSTN.CLUNY.UNIT.M/C ","GEN","*MMS MarketName*"
+224,"SUBSTN.PALOONA.UNIT.M/C ","GEN","*MMS MarketName*"
+225,"SUBSTN.MAYURA.LINE.MAYLBY_1 ","GEN","*MMS MarketName*"
+226,"SUBSTN.CATH_R.GEN.CR_1 ","GEN","*MMS MarketName*"
+227,"SUBSTN.TAS1.SUMM.WOOLNORTH ","GEN","*MMS MarketName*"
+228,"SUBSTN.YWF.GEN.YWF ","GEN","*MMS MarketName*"
+229,"SUBSTN.KOGAN_PS.GEN.1_KOGAN_PS ","GEN","*MMS MarketName*"
+230,"SUBSTN.HAL_WF.GEN.HAL1 ","GEN","*MMS MarketName*"
+231,"SUBSTN.SNW_WF.GEN.ST_1 ","GEN","*MMS MarketName*"
+232,"SUBSTN.ERARING.UNIT.GT1 ","GEN","*MMS MarketName*"
+233,"SUBSTN.QPS.GEN.QP_5 ","GEN","*MMS MarketName*"
+234,"SUBSTN.TALAWARA.UNIT.1 ","GEN","*MMS MarketName*"
+235,"SUBSTN.URANQNTY.UNIT.U11 ","GEN","*MMS MarketName*"
+236,"SUBSTN.URANQNTY.UNIT.U12 ","GEN","*MMS MarketName*"
+237,"SUBSTN.URANQNTY.UNIT.U13 ","GEN","*MMS MarketName*"
+238,"SUBSTN.URANQNTY.UNIT.U14 ","GEN","*MMS MarketName*"
+239,"SUBSTN.T200CMPS.GEN.STN_CMPS ","GEN","*MMS MarketName*"
+240,"SUBSTN.WBTS.GEN.GEN ","GEN","*MMS MarketName*"
+241,"SUBSTN.H48_B2PS.GEN.1_B2PS ","GEN","*MMS MarketName*"
+242,"SUBSTN.H48_B2PS.GEN.2_B2PS ","GEN","*MMS MarketName*"
+243,"SUBSTN.H48_B2PS.GEN.3_B2PS ","GEN","*MMS MarketName*"
+244,"SUBSTN.COLONGRA.UNIT.1 ","GEN","*MMS MarketName*"
+245,"SUBSTN.COLONGRA.UNIT.2 ","GEN","*MMS MarketName*"
+246,"SUBSTN.COLONGRA.UNIT.3 ","GEN","*MMS MarketName*"
+247,"SUBSTN.COLONGRA.UNIT.4 ","GEN","*MMS MarketName*"
+248,"SUBSTN.TAMAR_PS.UNIT.G104 ","GEN","*MMS MarketName*"
+249,"SUBSTN.CULLERIN.UNIT.1 ","GEN","*MMS MarketName*"
+250,"SUBSTN.CAPITAL.UNIT.C1 ","GEN","*MMS MarketName*"
+251,"SUBSTN.CLEM_G.GEN.CG_1 ","GEN","*MMS MarketName*"
+252,"SUBSTN.HALHIL.GEN.HLH_1 ","GEN","*MMS MarketName*"
+253,"SUBSTN.TAMAR_PS.UNIT.CC ","GEN","*MMS MarketName*"
+254,"SUBSTN.MKPS.GEN.MCKAY1 ","GEN","*MMS MarketName*"
+255,"SUBSTN.MT_S_PS.GEN.3_MT_S_PS ","GEN","*MMS MarketName*"
+256,"SUBSTN.H71_DDPS.GEN.STN_DDPS ","GEN","*MMS MarketName*"
+257,"SUBSTN.YARWUN.GEN.1_YARWUN ","GEN","*MMS MarketName*"
+258,"SUBSTN.NO_B_H.GEN.NBH_1 ","GEN","*MMS MarketName*"
+259,"SUBSTN.MAYURA.GEN.LAKE_BONY3 ","GEN","*MMS MarketName*"
+260,"SUBSTN.PT_L_T.GEN.PL_3 ","GEN","*MMS MarketName*"
+261,"SUBSTN.WAT_WF.GEN.WW_1 ","GEN","*MMS MarketName*"
+262,"SUBSTN.GUNNING.TRANS.TCS1 ","GEN","*MMS MarketName*"
+263,"SUBSTN.WOODLAWN.UNIT.1 ","GEN","*MMS MarketName*"
+264,"SUBSTN.MOPS.GEN.GEN11 ","GEN","*MMS MarketName*"
+265,"SUBSTN.MOPS.GEN.GEN12 ","GEN","*MMS MarketName*"
+266,"SUBSTN.BLUFF_WF.GEN.PPR_1 ","GEN","*MMS MarketName*"
+267,"SUBSTN.OWF.GEN.OWF ","GEN","*MMS MarketName*"
+268,"SUBSTN.MWF.GEN.MACARTH ","GEN","*MMS MarketName*"
+270,"SUBSTN.MLWF.TRANS.TRANS1 ","GEN","*MMS MarketName*"
+271,"SUBSTN.MUSSELRO.GEN.WTG_ABCD ","GEN","*MMS MarketName*"
+272,"SUBSTN.SNW_NTH.GEN.SNOWNTH1 ","GEN","*MMS MarketName*"
+273,"SUBSTN.SNW_STH.GEN.SNOWSTH1 ","GEN","*MMS MarketName*"
+274,"SUBSTN.GRWF.GEN.GULLRWF1 ","GEN","*MMS MarketName*"
+275,"SUBSTN.MEWF.GEN.MERCER01 ","GEN","*MMS MarketName*"
+276,"SUBSTN.BOCOROCK.UNIT.BOCORWF1 ","GEN","*MMS MarketName*"
+277,"SUBSTN.NYNGN_SF.UNIT.NYNGAN1 ","GEN","*MMS MarketName*"
+278,"SUBSTN.TARALGA.UNIT.TARALGA1 ","GEN","*MMS MarketName*"
+279,"SUBSTN.BHWF.GEN.BALDHWF1 ","GEN","*MMS MarketName*"
+281,"SUBSTN.BRKNHILL.UNIT.GTGEN_SUMM ","GEN","*MMS MarketName*"
+282,"SUBSTN.GERM_CRK.GEN.GEN_AGGR ","GEN","*MMS MarketName*"
+283,"SUBSTN.MORANBAH.GEN.MBH_NTH ","GEN","*MMS MarketName*"
+284,"SUBSTN.REPULSE.UNIT.M/C ","GEN","*MMS MarketName*"
+285,"SUBSTN.SSRPT_PS.GEN.1_SSRPT_PS ","GEN","*MMS MarketName*"
+286,"SUBSTN.CAPE_J.SUMM.STAR_HILL ","GEN","*MMS MarketName*"
+287,"SUBSTN.LNGS.UNIT.1 ","GEN","*MMS MarketName*"
+288,"SUBSTN.LNGS.UNIT.2 ","GEN","*MMS MarketName*"
+289,"SUBSTN.VPGS.UNIT.1 ","GEN","*MMS MarketName*"
+290,"SUBSTN.VPGS.UNIT.2 ","GEN","*MMS MarketName*"
+291,"SUBSTN.VPGS.UNIT.3 ","GEN","*MMS MarketName*"
+292,"SUBSTN.VPGS.UNIT.4 ","GEN","*MMS MarketName*"
+293,"SUBSTN.VPGS.UNIT.5 ","GEN","*MMS MarketName*"
+294,"SUBSTN.VPGS.UNIT.6 ","GEN","*MMS MarketName*"
+295,"SUBSTN.BRKNH_SF.UNIT.BROKENH1 ","GEN","*MMS MarketName*"
+296,"SUBSTN.SNUGGY.SUMM.CANUNDA ","GEN","*MMS MarketName*"
+297,"SUBSTN.ROWALLAN.UNIT.M/C ","GEN","*MMS MarketName*"
+298,"SUBSTN.JOUNAMA.UNIT.1 ","GEN","*MMS MarketName*"
+299,"SUBSTN.MOREE_SF.UNIT.MOREESF1 ","GEN","*MMS MarketName*"
+300,"SUBSTN.PT_STA.GEN.PS_1 ","GEN","*MMS MarketName*"
+301,"SUBSTN.PT_STA.GEN.LONSDALE ","GEN","*MMS MarketName*"
+309,"SUBSTN.ROYALLA.GEN.ROYALLA ","GEN","*MMS MarketName*"
+311,"SUBSTN.ANGAST.GEN.ANGAS ","GEN","*MMS MarketName*"
+312,"SUBSTN.HRN_WF.GEN.HDWF1 ","GEN","*MMS MarketName*"
+313,"SUBSTN.ARWF.GEN.ARWF1 ","GEN","*MMS MarketName*"
+314,"SUBSTN.MUGGA_SF.GEN.MUGGALANESF ","GEN","*MMS MarketName*"
+315,"SUBSTN.BASOL_SF.GEN.BASOL_SF ","GEN","*MMS MarketName*"
+316,"SUBSTN.HRN_WF.GEN.HDWF2 ","GEN","*MMS MarketName*"
+317,"SUBSTN.WRWF.GEN.WRWF1 ","GEN","*MMS MarketName*"
+318,"SUBSTN.GRWF.GEN.GULLRSF1 ","GEN","*MMS MarketName*"
+319,"SUBSTN.HRN_WF.GEN.HDWF3 ","GEN","*MMS MarketName*"
+320,"SUBSTN.PARK_SF.GEN.PSF1 ","GEN","*MMS MarketName*"
+321,"SUBSTN.GRIFF_SF.GEN.GRIFSF1 ","GEN","*MMS MarketName*"
+322,"SUBSTN.TG_STH.GEN.SATGS1 ","GEN","*MMS MarketName*"
+324,"SUBSTN.KWF.GEN.KIATAWF ","GEN","*MMS MarketName*"
+327,"SUBSTN.SAPWF.GEN.SAPWF01 ","GEN","*MMS MarketName*"
+328,"SUBSTN.T239_KSF.GEN.KSP1 ","GEN","*MMS MarketName*"
+329,"SUBSTN.YSW.GEN.YSWF1 ","GEN","*MMS MarketName*"
+332,"SUBSTN.CLPS.SUMM.HYDRO ","GEN","*MMS MarketName*"
+333,"SUBSTN.MANLD_SF.GEN.MANSLR1 ","GEN","*MMS MarketName*"
+334,"SUBSTN.HUGHN_SF.GEN.HUGSF1 ","GEN","*MMS MarketName*"
+335,"SUBSTN.GSF.GEN.GANNSF1 ","GEN","*MMS MarketName*"
+336,"SUBSTN.SILVERWF.GEN.STWF1 ","GEN","*MMS MarketName*"
+337,"SUBSTN.T240_CSF.GEN.CLARESF ","GEN","*MMS MarketName*"
+338,"SUBSTN.SCWF.GEN.SALTCRK1 ","GEN","*MMS MarketName*"
+339,"SUBSTN.LRSF.GEN.LRSF1 ","GEN","*MMS MarketName*"
+342,"SUBSTN.WILLOG.GEN.WGWF1 ","GEN","*MMS MarketName*"
+343,"SUBSTN.BUNGAL.GEN.BNGSF1 ","GEN","*MMS MarketName*"
+344,"SUBSTN.Z3_HAMSF.GEN.HAMISF1 ","GEN","*MMS MarketName*"
+345,"SUBSTN.Z2_WHSF.GEN.WHITSF1 ","GEN","*MMS MarketName*"
+346,"SUBSTN.MGW.GEN.MTGELWF1 ","GEN","*MMS MarketName*"
+347,"SUBSTN.CROOKWF.GEN.CROOKWF2 ","GEN","*MMS MarketName*"
+348,"SUBSTN.BODANGWF.GEN.BODWF1 ","GEN","*MMS MarketName*"
+349,"SUBSTN.WRWF.GEN.WRSF1 ","GEN","*MMS MarketName*"
+350,"SUBSTN.BSP.GEN.BANN1 ","GEN","*MMS MarketName*"
+351,"SUBSTN.Z7_RRSF.GEN.RRSF1 ","GEN","*MMS MarketName*"
+352,"SUBSTN.H86_DDSF.GEN.DDSF1 ","GEN","*MMS MarketName*"
+353,"SUBSTN.TVLZ_SF.GEN.SMCSF1 ","GEN","*MMS MarketName*"
+354,"SUBSTN.COLMB_SF.GEN.COLEASF1 ","GEN","*MMS MarketName*"
+355,"SUBSTN.BUNGAL.GEN.BNGSF2 ","GEN","*MMS MarketName*"
+356,"SUBSTN.H84_MTEW.GEN.MEWF1 ","GEN","*MMS MarketName*"
+357,"SUBSTN.COLL_SF.GEN.CSPVPS1 ","GEN","*MMS MarketName*"
+358,"SUBSTN.EMRLD_SF.GEN.EMERASF1 ","GEN","*MMS MarketName*"
+359,"SUBSTN.WSF.GEN.WEMENSF1 ","GEN","*MMS MarketName*"
+360,"SUBSTN.KSF.GEN.KARSF1 ","GEN","*MMS MarketName*"
+361,"SUBSTN.Z5_HAYSF.GEN.HAYMSF1 ","GEN","*MMS MarketName*"
+362,"SUBSTN.Z6_DAYSF.GEN.DAYDSF1 ","GEN","*MMS MarketName*"
+367,"SUBSTN.LIN_GP.GEN.LGAPWF1 ","GEN","*MMS MarketName*"
+368,"SUBSTN.BGR.SUMM.CHALLICUM ","GEN","*MMS MarketName*"
+369,"SUBSTN.CWWF.GEN.CROWLWF1 ","GEN","*MMS MarketName*"
+370,"SUBSTN.CLRMT_SF.GEN.CLERMSF1 ","GEN","*MMS MarketName*"
+371,"SUBSTN.T255_RRS.GEN.RGBYRSF1 ","GEN","*MMS MarketName*"
+372,"SUBSTN.Z8_LV1SF.GEN.LILYSF1 ","GEN","*MMS MarketName*"
+373,"SUBSTN.OAKY1_SF.GEN.OAKEY1SF ","GEN","*MMS MarketName*"
+374,"SUBSTN.Z4_CGWF.GEN.COOPGWF1 ","GEN","*MMS MarketName*"
+375,"SUBSTN.SSNRV_SF.GEN.SRSF1 ","GEN","*MMS MarketName*"
+376,"SUBSTN.CHLDR_SF.GEN.CHILDSF1 ","GEN","*MMS MarketName*"
+377,"SUBSTN.COOR_S.GEN.TBSF1 ","GEN","*MMS MarketName*"
+378,"SUBSTN.BGWF.GEN.BULGANA1 ","GEN","*MMS MarketName*"
+380,"SUBSTN.NSF.GEN.NUMURSF1 ","GEN","*MMS MarketName*"
+381,"SUBSTN.BERYL_SF.GEN.BERYLSF1 ","GEN","*MMS MarketName*"
+382,"SUBSTN.YDW.GEN.YENDWF1 ","GEN","*MMS MarketName*"
+383,"SUBSTN.OAKY2_SF.GEN.OAKEY2SF ","GEN","*MMS MarketName*"
+384,"SUBSTN.H96HN2SF.GEN.HAUGHT1 ","GEN","*MMS MarketName*"
+387,"SUBSTN.MOORAWF.GEN.MOORAWF1 ","GEN","*MMS MarketName*"
+388,"SUBSTN.LIMN2_SF.GEN.LIMOSF21 ","GEN","*MMS MarketName*"
+389,"SUBSTN.FINLY_SF.GEN.FINLYSF1 ","GEN","*MMS MarketName*"
+390,"SUBSTN.YARRN_SF.GEN.YARANSF1 ","GEN","*MMS MarketName*"
+391,"SUBSTN.KEPG_SF.GEN.KEPSF1 ","GEN","*MMS MarketName*"
+392,"SUBSTN.KEPG_WF.GEN.KEPWF1 ","GEN","*MMS MarketName*"
+393,"SUBSTN.YSF.GEN.YATSF1 ","GEN","*MMS MarketName*"
+394,"SUBSTN.CATHILL.GEN.CTHLWF1 ","GEN","*MMS MarketName*"
+395,"SUBSTN.BARK_I.GEN.BARKIPS1 ","GEN","*MMS MarketName*"
+396,"SUBSTN.NEVRT_SF.GEN.NEVERSF1 ","GEN","*MMS MarketName*"
+397,"SUBSTN.SUNRY_SF.GEN.SUNRSF1 ","GEN","*MMS MarketName*"
+398,"SUBSTN.MARYR_SF.GEN.MARYRSF1 ","GEN","*MMS MarketName*"
+399,"SUBSTN.GRANWF.GEN.GRANWF1 ","GEN","*MMS MarketName*"
+400,"SUBSTN.SHWF.GEN.STOCKYD1 ","GEN","*MMS MarketName*"
+401,"SUBSTN.LIMN1_SF.GEN.LIMOSF11 ","GEN","*MMS MarketName*"
+402,"SUBSTN.DARLP_SF.GEN.DARLSF1 ","GEN","*MMS MarketName*"
+403,"SUBSTN.KIAMALSF.GEN.KIAMSF1 ","GEN","*MMS MarketName*"
+404,"SUBSTN.ELWF.GEN.ELAINWF1 ","GEN","*MMS MarketName*"
+405,"SUBSTN.CTWF.GEN.CHYTWF1 ","GEN","*MMS MarketName*"
+406,"SUBSTN.CSF.GEN.COHUNSF1 ","GEN","*MMS MarketName*"
+407,"SUBSTN.DDWF.GEN.DUNDWF1 ","GEN","*MMS MarketName*"
+408,"SUBSTN.DDWF.GEN.DUNDWF2 ","GEN","*MMS MarketName*"
+409,"SUBSTN.DDWF.GEN.DUNDWF3 ","GEN","*MMS MarketName*"
+410,"SUBSTN.BOMEN_SF.GEN.BOMENSF1 ","GEN","*MMS MarketName*"
+411,"SUBSTN.WRCK1_SF.GEN.WARWSF1 ","GEN","*MMS MarketName*"
+412,"SUBSTN.WRCK2_SF.GEN.WARWSF2 ","GEN","*MMS MarketName*"
+413,"SUBSTN.MMNT_SF.GEN.MIDDLSF1 ","GEN","*MMS MarketName*"
+414,"SUBSTN.GOON_SF.GEN.GOONSF1 ","GEN","*MMS MarketName*"
+415,"SUBSTN.GRWF2.GEN.GULLRWF2 ","GEN","*MMS MarketName*"
+416,"SUBSTN.BRYBNKWF.GEN.BRYB1WF1 ","GEN","*MMS MarketName*"
+417,"SUBSTN.WELLNGTN.GEN.WELLSF1 ","GEN","*MMS MarketName*"
+418,"SUBSTN.MOLNG_SF.GEN.MOLNGSF1 ","GEN","*MMS MarketName*"
+419,"SUBSTN.COLLECWF.GEN.COLWF01 ","GEN","*MMS MarketName*"
+420,"SUBSTN.BANGOWF.GEN.BANGOWF1 ","GEN","*MMS MarketName*"
+421,"SUBSTN.BANGOWF.GEN.BANGOWF2 ","GEN","*MMS MarketName*"
+422,"SUBSTN.GWSF.GEN.GLRWNSF1 ","GEN","*MMS MarketName*"
+423,"SUBSTN.WISF.GEN.WINTSF1 ","GEN","*MMS MarketName*"
+424,"SUBSTN.MW_3.GEN.MWPS3PV1 ","GEN","*MMS MarketName*"
+425,"SUBSTN.MW_4.GEN.MWPS4PV1 ","GEN","*MMS MarketName*"
+426,"SUBSTN.CRUDNEWF.GEN.CRURWF1 ","GEN","*MMS MarketName*"
+427,"SUBSTN.JEMAL_SF.GEN.JEMALNG1 ","GEN","*MMS MarketName*"
+428,"SUBSTN.MW_1.GEN.MWPS1PV1 ","GEN","*MMS MarketName*"
+429,"SUBSTN.MW_2.GEN.MWPS2PV1 ","GEN","*MMS MarketName*"
+430,"SUBSTN.MA_2.GEN.MAPS2PV1 ","GEN","*MMS MarketName*"
+431,"SUBSTN.MA_3.GEN.MAPS3PV1 ","GEN","*MMS MarketName*"
+432,"SUBSTN.CORWA_SF.GEN.CRWASF1 ","GEN","*MMS MarketName*"
+433,"SUBSTN.TAB_MILL.TRANS.TG1H66","GEN","*MMS MarketName*"
+434,"SUBSTN.T273_GGS.GEN.GANGARR1 ","GEN","*MMS MarketName*"
+435,"SUBSTN.H104_WDS.GEN.WDGPH1 ","GEN","*MMS MarketName*"
+436,"SUBSTN.ADP.GEN.ADPPV1 ","GEN","*MMS MarketName*"
+439,"SUBSTN.WAGGN_SF.GEN.WAGGNSF1 ","GEN","*MMS MarketName*"
+440,"SUBSTN.JUNEE_SF.GEN.JUNEESF1 ","GEN","*MMS MarketName*"
+441,"SUBSTN.GUNDH_SF.GEN.GNNDHSF1 ","GEN","*MMS MarketName*"
+442,"SUBSTN.MBH_2.GEN.MBPS2PV1 ","GEN","*MMS MarketName*"
+449,"SUBSTN.T272_CLB.GEN.COLUMSF1 ","GEN","*MMS MarketName*"
+450,"SUBSTN.SUNTP_SF.GEN.SUNTPSF1 ","GEN","*MMS MarketName*"
+451,"SUBSTN.HILST_SF.GEN.HILLSTN1 ","GEN","*MMS MarketName*"
+452,"SUBSTN.MRW1.GEN.MUWAWF1 ","GEN","*MMS MarketName*"
+453,"SUBSTN.MRW2.GEN.MUWAWF2 ","GEN","*MMS MarketName*"
+454,"SUBSTN.SEBAS_SF.GEN.SEBSF1 ","GEN","*MMS MarketName*"
+455,"SUBSTN.LIN_GP.GEN.LGAPWF2 ","GEN","*MMS MarketName*"
+456,"SUBSTN.BOLIVAR.GEN.BOWWPV1 ","GEN","*MMS MarketName*"
+461,"SUBSTN.METZ_SF.GEN.METZSF1 ","GEN","*MMS MarketName*"
+462,"SUBSTN.Z34_BGSF.GEN.BGSF1 ","GEN","*MMS MarketName*"
+463,"SUBSTN.WLGA_SF.GEN.WLGASF1 ","GEN","*MMS MarketName*"
+464,"SUBSTN.P_PA_N.GEN.PAREPW1 ","GEN","*MMS MarketName*"
+465,"SUBSTN.P_PA_N.GEN.PAREPS1 ","GEN","*MMS MarketName*"
+466,"SUBSTN.SNAP_P.GEN.SNAPPER1 ","GEN","*MMS MarketName*"
+467,"SUBSTN.VIC1.NSGEN.DIAPURWF ","GEN","*MMS MarketName*"
+468,"SUBSTN.ST_LENRD.LOAD.SLDCBLK1 ","GEN","*MMS MarketName*"
+473,"SUBSTN.SAW_HV.GEN.HVWWPV1 ","GEN","*MMS MarketName*"
+474,"SUBSTN.MLSW.GEN.MRTLSWF1 ","GEN","*MMS MarketName*"
+475,"SUBSTN.BRYBNKWF.GEN.BRYB2WF2 ","GEN","*MMS MarketName*"
+476,"SUBSTN.H116_KWF.GEN.KABWF1 ","GEN","*MMS MarketName*"
+477,"SUBSTN.EDVL_SF.GEN.EDVLSF1 ","GEN","*MMS MarketName*"
+478,"SUBSTN.MOUR_SF.GEN.MOURSF1 ","GEN","*MMS MarketName*"
+481,"SUBSTN.WWYAL_SF.GEN.WSTWYSF1 ","GEN","*MMS MarketName*"
+482,"SUBSTN.NEWEN_SF.GEN.NEWENSF1 ","GEN","*MMS MarketName*"
+483,"SUBSTN.NEWEN_SF.GEN.NEWENSF2 ","GEN","*MMS MarketName*"
+488,"SUBSTN.BLVR_PS.GEN.BOLIVPS1 ","GEN","*MMS MarketName*"
+491,"SUBSTN.WAND_SF.GEN.WANDSF1 ","GEN","*MMS MarketName*"
+492,"SUBSTN.AVL_SF.GEN.AVLSF1 ","GEN","*MMS MarketName*"
+501,"SUBSTN.COOR_2.GEN.TB2SF1 ","GEN","*MMS MarketName*"
+502,"SUBSTN.WYA_SF.GEN.WYASF1 ","GEN","*MMS MarketName*"
+503,"SUBSTN.T287_DWF.GEN.DULAWF1 ","GEN","*MMS MarketName*"
+508,"SUBSTN.RYE_P_WF.GEN.RYEPARK1 ","GEN","*MMS MarketName*"
+509,"SUBSTN.T48_TLLY.GEN.TLLY_S_MILL ","GEN","*MMS MarketName*"
+510,"SUBSTN.GNSF.GEN.GLENSF1 ","GEN","*MMS MarketName*"
+511,"SUBSTN.FLYCR_WF.GEN.FLYCRKWF ","GEN","*MMS MarketName*"
+512,"SUBSTN.GYDR_WF.GEN.GSWF1A ","GEN","*MMS MarketName*"
+515,"SUBSTN.MANN_R.GEN.MANNSF2 ","GEN","*MMS MarketName*"
+516,"SUBSTN.GYDR_WF.GEN.GSWF1B1 ","GEN","*MMS MarketName*"
+517,"SUBSTN.TALAWARA.UNIT.2 ","GEN","*MMS MarketName*"
+518,"SUBSTN.KURRI.GEN.HEZ1 ","GEN","*MMS MarketName*"
+519,"SUBSTN.WEL_N_SF.GEN.WELNTH1 ","GEN","*MMS MarketName*"
+522,"SUBSTN.NSW1.NSGEN.SHOAL1 ","GEN","*MMS MarketName*"
+523,"SUBSTN.CROOKWF.GEN.CROOKWF3 ","GEN","*MMS MarketName*"
+524,"SUBSTN.KING_SF.GEN.KINGASF1 ","GEN","*MMS MarketName*"
+525,"SUBSTN.MACNT_WF.GEN.MCINTYR1 ","GEN","*MMS MarketName*"
+526,"SUBSTN.H102_CKC.GEN.CLRKCWF1 ","GEN","*MMS MarketName*"
+527,"SUBSTN.H100_CKN.GEN.CLRKCWF2 ","GEN","*MMS MarketName*"
+528,"SUBSTN.WALA_SF.GEN.WLWLSF1 ","GEN","*MMS MarketName*"
+529,"SUBSTN.WALA_SF.GEN.WLWLSF2 ","GEN","*MMS MarketName*"
+530,"SUBSTN.RCWF.GEN.RYANCWF1 ","GEN","*MMS MarketName*"
+531,"SUBSTN.GGS.GEN.GIRGSF ","GEN","*MMS MarketName*"
+532,"SUBSTN.WNS.GEN.WUNUSF1 ","GEN","*MMS MarketName*"
+535,"SUBSTN.H121_WDB.GEN.WDBESSG ","GEN","*MMS MarketName*"
+536,"SUBSTN.WALGBESS.GEN.WALGGEN ","GEN","*MMS MarketName*"
+537,"SUBSTN.BRK_BESS.GEN.BRKBESSGEN ","GEN","*MMS MarketName*"
+538,"SUBSTN.CBESS.GEN.CBESSGEN ","GEN","*MMS MarketName*"
+539,"SUBSTN.DPESS.GEN.DPESSGEN ","GEN","*MMS MarketName*"
+540,"SUBSTN.QBYNBESS.GEN.QBYNGEN ","GEN","*MMS MarketName*"
+541,"SUBSTN.RESS1.GEN.RESS1GEN ","GEN","*MMS MarketName*"
+542,"SUBSTN.RESS2.GEN.RESS2GEN ","GEN","*MMS MarketName*"
+543,"SUBSTN.Z13_BCMB.GEN.BCMBESSG ","GEN","*MMS MarketName*"
+544,"SUBSTN.T271_WSB.GEN.WANDBG ","GEN","*MMS MarketName*"
+545,"SUBSTN.Z35_CCB.GEN.CHINBESSG ","GEN","*MMS MarketName*"
+546,"SUBSTN.ADP.GEN.ADPGEN ","GEN","*MMS MarketName*"
+547,"SUBSTN.BOLIVAR.GEN.BOLGEN ","GEN","*MMS MarketName*"
+548,"SUBSTN.CHRI_B.GEN.CHBGEN ","GEN","*MMS MarketName*"
+549,"SUBSTN.SAW_HV.GEN.HVYGEN ","GEN","*MMS MarketName*"
+550,"SUBSTN.COOR_2.GEN.TB2BGEN ","GEN","*MMS MarketName*"
+551,"SUBSTN.DALR_N.GEN.DPRGEN ","GEN","*MMS MarketName*"
+552,"SUBSTN.HRN_WF.GEN.HPRGEN ","GEN","*MMS MarketName*"
+553,"SUBSTN.LBBESS.GEN.LPRGEN ","GEN","*MMS MarketName*"
+554,"SUBSTN.TI_BESS.GEN.TIBGEN ","GEN","*MMS MarketName*"
+555,"SUBSTN.HWBS.GEN.HBESSGEN ","GEN","*MMS MarketName*"
+556,"SUBSTN.VBB.GEN.VBBGEN ","GEN","*MMS MarketName*"
+557,"SUBSTN.BESS.GEN.BALBGEN ","GEN","*MMS MarketName*"
+558,"SUBSTN.BGWF.GEN.BGWFBESS ","GEN","*MMS MarketName*"
+559,"SUBSTN.GESS.GEN.GANNBESS ","GEN","*MMS MarketName*"
+560,"SUBSTN.PHIBESS.GEN.PICESBGEN ","GEN","*MMS MarketName*"
+561,"SUBSTN.HDWF.GEN.HD1WF1 ","GEN","*MMS MarketName*"
+562,"SUBSTN.HASTINGS.GEN.HASTING1 ","GEN","*MMS MarketName*"
+563,"SUBSTN.HASTINGS.GEN.HASTING2 ","GEN","*MMS MarketName*"
+564,"SUBSTN.HASTINGS.GEN.HASTING3 ","GEN","*MMS MarketName*"
+565,"SUBSTN.WOLLAR_W.GEN.WOLARSF1 ","GEN","*MMS MarketName*"
+566,"SUBSTN.STUBO_SF.GEN.STUBSF1 ","GEN","*MMS MarketName*"
+567,"SUBSTN.STUBO_SF.GEN.STUBSF2 ","GEN","*MMS MarketName*"
+568,"SUBSTN.GPEC.GEN.GPWFEST1 ","GEN","*MMS MarketName*"
+569,"SUBSTN.GPEC.GEN.GPWFEST2 ","GEN","*MMS MarketName*"
+570,"SUBSTN.GPEC.GEN.GPWFEST3 ","GEN","*MMS MarketName*"
+571,"SUBSTN.RBES.GEN.RANGEB1 ","GEN","*MMS MarketName*"
+572,"SUBSTN.BLYBSS.GEN.BLYTHB1 ","GEN","*MMS MarketName*"
+573,"SUBSTN.WAR_BESS.GEN.WTAHB1 ","GEN","*MMS MarketName*"
+574,"SUBSTN.Z36_GBB.GEN.GREENB1 ","GEN","*MMS MarketName*"
+575,"SUBSTN.ERA_BESS.GEN.ERB01 ","GEN","*MMS MarketName*"
+576,"SUBSTN.MKSF.GEN.MOKOSF1 ","GEN","*MMS MarketName*"
+577,"SUBSTN.KSP.GEN.KERNGSP1 ","GEN","*MMS MarketName*"
+578,"SUBSTN.LVES.GEN.LVES1 ","GEN","*MMS MarketName*"
+579,"SUBSTN.KRES.GEN.KESSB1 ","GEN","*MMS MarketName*"
+580,"SUBSTN.H138_TRB.GEN.TARBESS1 ","GEN","*MMS MarketName*"
+581,"SUBSTN.H98_WBWF.GEN.WAMBOWF1 ","GEN","*MMS MarketName*"
+582,"SUBSTN.CAPRAL.GEN.HUNTER1 ","GEN","*MMS MarketName*"
+583,"SUBSTN.CAPRAL.GEN.HUNTER2 ","GEN","*MMS MarketName*"
+584,"SUBSTN.H113_ALD.GEN.ALDGASF1 ","GEN","*MMS MarketName*"
+585,"SUBSTN.H157_ULB.GEN.ULPBESS1 ","GEN","*MMS MarketName*"
+586,"SUBSTN.CULC_SF.GEN.CUSF1 ","GEN","*MMS MarketName*"
+587,"SUBSTN.WNSF.GEN.WNSF1 ","GEN","*MMS MarketName*"
+588,"SUBSTN.MREH.GEN.MREHA1 ","GEN","*MMS MarketName*"
+589,"SUBSTN.MREH.GEN.MREHA2 ","GEN","*MMS MarketName*"
+590,"SUBSTN.MREH.GEN.MREHA3 ","GEN","*MMS MarketName*"
+591,"SUBSTN.MNM_BESS.GEN.MANNUMB1 ","GEN","*MMS MarketName*"
+592,"SUBSTN.TP_BESS.GEN.TEMPB1 ","GEN","*MMS MarketName*"
+30001,"AGC Mode NEM SOUTH ","AGC"," * No MarketName * "
+30002,"AGC Mode NEM NORTH ","AGC"," * No MarketName * "
+31001,"ACE NEM SOUTH ","AGC"," * No MarketName * "
+31002,"ACEFIL NEM SOUTH ","AGC"," * No MarketName * "
+31003,"ACEINT NEM SOUTH ","AGC"," * No MarketName * "
+31004,"TIME_ERR NEM SOUTH ","AGC"," * No MarketName * "
+31005,"ACE NEM NORTH ","AGC"," * No MarketName * "
+31006,"ACEFIL NEM NORTH ","AGC"," * No MarketName * "
+31007,"ACEINT NEM NORTH ","AGC"," * No MarketName * "
+31008,"TIME_ERR NEM NORTH ","AGC"," * No MarketName * "
+32001,"FREQ NEM SOUTH ","SYSTEM"," * No MarketName * "
+32002,"FREQ_NOM NEM SOUTH ","SYSTEM"," * No MarketName * "
+32003,"FREQ_DEV NEM SOUTH ","SYSTEM"," * No MarketName * "
+32004,"FREQ_OFFSET NEM SOUTH ","SYSTEM"," * No MarketName * "
+32005,"FREQ NEM NORTH ","SYSTEM"," * No MarketName * "
+32006,"FREQ_NOM NEM NORTH ","SYSTEM"," * No MarketName * "
+32007,"FREQ_DEV NEM NORTH ","SYSTEM"," * No MarketName * "
+32008,"FREQ_OFFSET NEM NORTH ","SYSTEM"," * No MarketName * "
+40001,"AGC Mode TASNEM SOUTH ","AGC"," * No MarketName * "
+40002,"AGC Mode TASNEM NORTH ","AGC"," * No MarketName * "
+41001,"ACE TASNEM SOUTH ","AGC"," * No MarketName * "
+41002,"ACEFIL TASNEM SOUTH ","AGC"," * No MarketName * "
+41003,"ACEINT TASNEM SOUTH ","AGC"," * No MarketName * "
+41004,"TIME_ERR TASNEM SOUTH ","AGC"," * No MarketName * "
+41005,"ACE TASNEM NORTH ","AGC"," * No MarketName * "
+41006,"ACEFIL TASNEM NORTH ","AGC"," * No MarketName * "
+41007,"ACEINT TASNEM NORTH ","AGC"," * No MarketName * "
+41008,"TIME_ERR TASNEM NORTH ","AGC"," * No MarketName * "
+42001,"FREQ TASNEM SOUTH ","SYSTEM"," * No MarketName * "
+42002,"FREQ_NOM TASNEM SOUTH ","SYSTEM"," * No MarketName * "
+42003,"FREQ_DEV TASNEM SOUTH ","SYSTEM"," * No MarketName * "
+42004,"FREQ_OFFSET TASNEM SOUTH ","SYSTEM"," * No MarketName * "
+42005,"FREQ TASNEM NORTH ","SYSTEM"," * No MarketName * "
+42006,"FREQ_NOM TASNEM NORTH ","SYSTEM"," * No MarketName * "
+42007,"FREQ_DEV TASNEM NORTH ","SYSTEM"," * No MarketName * "
+42008,"FREQ_OFFSET TASNEM NORTH ","SYSTEM"," * No MarketName * "
+33001,"SPD SECONDS","SPD"," * No MarketName * "
+20001,"SA-VIC HYTS-SESS","INTERCONNECTOR","*MarketName*"
+20002,"VIC-SNOWY DDTS-MSS","INTERCONNECTOR","*MarketName*"
+20003,"VIC-SNOWY RCTS-Buronga","INTERCONNECTOR","*MarketName*"
+20004,"VIC-SNOWY WOTS-JIND","INTERCONNECTOR","*MarketName*"
+20005,"SNOWY-NSW WOTS-JIND","INTERCONNECTOR","*MarketName*"
+20006,"SNOWY-NSW RCTS-Buronga","INTERCONNECTOR","*MarketName*"
+20007,"SNOWY-NSW T1&2_CBA","INTERCONNECTOR","*MarketName*"
+20008,"SNOWY-NSW T1&2_YASS","INTERCONNECTOR","*MarketName*"
+20009,"SNOWY-NSW T3_YASS","INTERCONNECTOR","*MarketName*"
+20010,"SNOWY-NSW T3_CBA","INTERCONNECTOR","*MarketName*"
+20011,"SNOWY-NSW T3_Wagga","INTERCONNECTOR","*MarketName*"
+20012,"SNOWY-NSW GGA_Munyang","INTERCONNECTOR","*MarketName*"
+20013,"NSW-QLD Directlink","INTERCONNECTOR","*MarketName*"
+20014,"NSW-QLD QNI","INTERCONNECTOR","*MarketName*"
+20015,"SPD VIC-SA ","INTERCONNECTOR","*MarketName*"
+20016,"SPD VIC-SNOWY ","INTERCONNECTOR","*MarketName*"
+20017,"SPD NSW-SNOWY ","INTERCONNECTOR","*MarketName*"
+20018,"SPD NSW-Directlink ","INTERCONNECTOR","*MarketName*"
+20019,"SPD NSW-QNI ","INTERCONNECTOR","*MarketName*"
+20020,"SPD SA-Murraylink ","INTERCONNECTOR","*MarketName*"
+20021,"SPD VIC-NSW ","INTERCONNECTOR","*MarketName*"
+20022,"SPD Basslink ","INTERCONNECTOR","*MarketName*"
+10001,"APD SUMM POTLINES","LOAD","*MarketName*"
+10006,"TOMAGO SUMM POTLINES","LOAD","*MarketName*"
+10013,"H8_BOYNE POTLINES SUMM","LOAD","*MarketName*"
diff --git a/tests/fixtures/data/Reports/Current/Causer_Pays_Elements/index.html b/tests/fixtures/data/Reports/Current/Causer_Pays_Elements/index.html
new file mode 100644
index 0000000..3827663
--- /dev/null
+++ b/tests/fixtures/data/Reports/Current/Causer_Pays_Elements/index.html
@@ -0,0 +1,3 @@
+
+Elements_FCAS_202504151310.csv
+
\ No newline at end of file
diff --git a/tests/fixtures/data/Reports/Current/Daily_Reports/PUBLIC_DAILY_202603140000_20260315040503.zip b/tests/fixtures/data/Reports/Current/Daily_Reports/PUBLIC_DAILY_202603140000_20260315040503.zip
new file mode 100644
index 0000000..4ef62be
Binary files /dev/null and b/tests/fixtures/data/Reports/Current/Daily_Reports/PUBLIC_DAILY_202603140000_20260315040503.zip differ
diff --git a/tests/fixtures/data/Reports/Current/Daily_Reports/PUBLIC_DAILY_202603150000_20260316040504.zip b/tests/fixtures/data/Reports/Current/Daily_Reports/PUBLIC_DAILY_202603150000_20260316040504.zip
new file mode 100644
index 0000000..4522146
Binary files /dev/null and b/tests/fixtures/data/Reports/Current/Daily_Reports/PUBLIC_DAILY_202603150000_20260316040504.zip differ
diff --git a/tests/fixtures/data/Reports/Current/Daily_Reports/index.html b/tests/fixtures/data/Reports/Current/Daily_Reports/index.html
new file mode 100644
index 0000000..73c53b3
--- /dev/null
+++ b/tests/fixtures/data/Reports/Current/Daily_Reports/index.html
@@ -0,0 +1,4 @@
+
+PUBLIC_DAILY_202603140000_20260315040503.zip
+PUBLIC_DAILY_202603150000_20260316040504.zip
+
\ No newline at end of file
diff --git a/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/PUBLIC_NEXT_DAY_DISPATCH_20260314_0000000507927925.zip b/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/PUBLIC_NEXT_DAY_DISPATCH_20260314_0000000507927925.zip
new file mode 100644
index 0000000..c302d57
Binary files /dev/null and b/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/PUBLIC_NEXT_DAY_DISPATCH_20260314_0000000507927925.zip differ
diff --git a/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/PUBLIC_NEXT_DAY_DISPATCH_20260315_0000000508079749.zip b/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/PUBLIC_NEXT_DAY_DISPATCH_20260315_0000000508079749.zip
new file mode 100644
index 0000000..dec3fef
Binary files /dev/null and b/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/PUBLIC_NEXT_DAY_DISPATCH_20260315_0000000508079749.zip differ
diff --git a/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/index.html b/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/index.html
new file mode 100644
index 0000000..cd113f4
--- /dev/null
+++ b/tests/fixtures/data/Reports/Current/NEXT_DAY_DISPATCH/index.html
@@ -0,0 +1,4 @@
+
+PUBLIC_NEXT_DAY_DISPATCH_20260314_0000000507927925.zip
+PUBLIC_NEXT_DAY_DISPATCH_20260315_0000000508079749.zip
+
\ No newline at end of file
diff --git a/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260314_0000000507927930.zip b/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260314_0000000507927930.zip
new file mode 100644
index 0000000..f8c6b80
Binary files /dev/null and b/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260314_0000000507927930.zip differ
diff --git a/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260315_0000000508079754.zip b/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260315_0000000508079754.zip
new file mode 100644
index 0000000..c902895
Binary files /dev/null and b/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260315_0000000508079754.zip differ
diff --git a/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/index.html b/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/index.html
new file mode 100644
index 0000000..9c56b6d
--- /dev/null
+++ b/tests/fixtures/data/Reports/Current/Next_Day_Intermittent_Gen_Scada/index.html
@@ -0,0 +1,4 @@
+
+PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260314_0000000507927930.zip
+PUBLIC_NEXT_DAY_INTERMITTENT_GEN_SCADA_20260315_0000000508079754.zip
+
\ No newline at end of file
diff --git a/tests/fixtures/spec.py b/tests/fixtures/spec.py
new file mode 100644
index 0000000..ad86269
--- /dev/null
+++ b/tests/fixtures/spec.py
@@ -0,0 +1,160 @@
+"""Declarative spec of what the NEMOSIS test fixtures cover.
+
+The fixture build script reads this module and is otherwise self-contained —
+if you want to add an era or a table to the test matrix, edit here and re-run
+the build script.
+
+Adding a new era:
+ 1. Add a dated entry to ERAS.
+ 2. Append the era key to each table's `eras` list in DYNAMIC_TABLES.
+ 3. Run `python tests/fixtures/build.py` to download, filter, and write.
+ 4. Commit the new files under tests/fixtures/data/.
+"""
+from datetime import date
+
+
+# Each era is a single calendar month pinned by its first day.
+# Dates are chosen to straddle known AEMO schema / format transitions so
+# the tests exercise every historical variant NEMOSIS needs to handle.
+ERAS = {
+ "2018-05": date(2018, 5, 1), # pre-5-min-trading baseline
+ "2020-01": date(2020, 1, 1), # year boundary, pre-5-min, monthly bidding format
+ "2021-02": date(2021, 2, 1), # last month the MMS archive still publishes bidding
+ "2021-05": date(2021, 5, 1), # first month of daily BIDMOVE_COMPLETE layout
+ "2021-10": date(2021, 10, 1), # 5MS cutover for TRADINGPRICE/TRADINGINTERCONNECT (extra_eras only)
+ "2022-01": date(2022, 1, 1), # year boundary, 5-min dispatch, 30-min trading, bidding gap
+ "2022-06": date(2022, 6, 1), # arbitrary post-5MS month for trading tables (5MS itself cut over 2021-10)
+ "2024-08": date(2024, 8, 1), # PUBLIC_DVD_ → PUBLIC_ARCHIVE# cutover; `into` straddles the format change
+ "2024-09": date(2024, 9, 1), # first safely-past-cutover month; used by bidding tables (see note)
+ "2025-01": date(2025, 1, 1), # year boundary, PUBLIC_ARCHIVE# format, post-bidding-gap
+ "recent": date(2026, 3, 15), # inside AEMO's current-data scrape window (see note)
+}
+# Year-boundary eras (2020-01, 2022-01, 2025-01) exist for the boundary test
+# matrix in tests/end_to_end_table_tests/_boundaries.py — they exercise
+# NEMOSIS's Dec→Jan stitch and the date generator's `month == 1` buffer-wrap
+# branch. Each is added only to the dynamic time-series tables that have
+# AEMO data in that month (no bidding tables for 2022-01, no MNSP_PEROFFER
+# past 2021-02 due to issue #68, etc.).
+# Note on "recent": AEMO's current-data pages (bidmove_complete, daily reports,
+# etc.) only retain ~a few months. Any date we pick here must be recent enough
+# to still be served at fixture-build time. When the commit ages out, update
+# this date and rerun `build.py --rebuild` for the scrape-pattern tables.
+
+
+# Entities kept when filtering rows. Diverse enough to exercise table
+# behaviour, small enough that fixture files stay under a few hundred KB each.
+DUIDS = ["AGLHAL", "HDWF2"] # OCGT + wind farm
+REGIONS = ["SA1", "NSW1"]
+INTERCONNECTORS = ["VIC1-NSW1"]
+CONSTRAINTS = ["DATASNAP_DFS_Q_CLST"] # CONSTRAINTID, used by DISPATCHCONSTRAINT
+GENCON_IDS = ["#NSW1-QLD1_RAMP_I_F"] # GENCONID, used by GENCONDATA / SPD* tables
+SPDCPC_GENCON_IDS = ["S>>MKRB_NIL_WEMWP4"] # smaller-volume GENCONID for SPDCPC, EFFECTIVEDATE pre-May-2021
+PARTICIPANTS = ["AGLE", "INFIGENH", "ERMPOWER"] # AGL SA + Infigen (Hornsdale) + ERM Power (LASTCHANGED moves between 2018/2021)
+MNSP_LINKS = ["BLNKTAS", "BLNKVIC"] # Basslink, both directions
+
+
+# Dynamic tables. Each entry says which eras to fixture and how to filter
+# rows down. The build script figures out the URL / download path from the
+# table name and era date — the spec itself stays path-agnostic.
+#
+# Starting with a representative cross-section covering the main patterns:
+# - DISPATCHPRICE: MMS monthly archive, region filter
+# - DISPATCHLOAD: MMS monthly archive, DUID filter
+# - BIDPEROFFER_D: MMS before 2021-04, BIDMOVE_COMPLETE scrape after
+# - DAILY_REGION_SUMMARY: scrape-pattern, current-data page only
+# More tables get added as each one lands end-to-end.
+DYNAMIC_TABLES = {
+ # Five-minute dispatch tables — the workhorse set.
+ "DISPATCHPRICE": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"REGIONID": REGIONS}},
+ "DISPATCHLOAD": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"DUID": DUIDS}},
+ "DISPATCH_UNIT_SCADA": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"DUID": DUIDS}},
+ "DISPATCHREGIONSUM": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"REGIONID": REGIONS}},
+ "DISPATCHINTERCONNECTORRES": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}},
+ "DISPATCHCONSTRAINT": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"CONSTRAINTID": CONSTRAINTS}},
+
+ # Trading tables. TRADINGPRICE/TRADINGINTERCONNECT switched from 30-min to
+ # 5-min at 2021-10-01 (the 5MS reform cutover); TRADINGLOAD/TRADINGREGIONSUM
+ # were discontinued at that point and only have pre-2022 eras.
+ #
+ # The `extra_eras` entry for 2021-10 is a build-only era used by the
+ # dedicated stride-transition test in test_trading_price.py. It doesn't
+ # go into the boundary matrix: the matrix's `into` flavour would query a
+ # window straddling the cutover with rows at two different strides, which
+ # the boundary helper can't express as a single regular grid.
+ "TRADINGPRICE": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2022-06", "2024-08", "2025-01"], "extra_eras": ["2021-10"], "filter": {"REGIONID": REGIONS}},
+ "TRADINGINTERCONNECT": {"eras": ["2018-05", "2020-01", "2021-05", "2022-01", "2022-06", "2024-08", "2025-01"], "extra_eras": ["2021-10"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}},
+ # TRADINGLOAD/TRADINGREGIONSUM were discontinued before 2022 — 2022-01 returns 404.
+ "TRADINGLOAD": {"eras": ["2018-05", "2020-01", "2021-05"], "filter": {"DUID": DUIDS}},
+ "TRADINGREGIONSUM": {"eras": ["2018-05", "2020-01", "2021-05"], "filter": {"REGIONID": REGIONS}},
+
+ # Bidding — MMS archive all the way through. AEMO stopped publishing these
+ # monthly files between March 2021 and July 2024 (see README), so 2022-01
+ # is unavailable and post-2021-02 coverage jumps to 2024-09.
+ #
+ # Non-bidding tables use 2024-08 to straddle the PUBLIC_DVD_ → PUBLIC_ARCHIVE#
+ # filename cutover. Bidding tables stay on 2024-09: 2024-07's bid archive
+ # sits inside the publishing gap and a 2024-08-era prev-month buffer would
+ # 404 at test time.
+ "BIDPEROFFER_D": {"eras": ["2018-05", "2020-01", "2021-02", "2024-09", "2025-01"], "filter": {"DUID": DUIDS}},
+ "BIDDAYOFFER_D": {"eras": ["2018-05", "2020-01", "2021-02", "2024-09", "2025-01"], "filter": {"DUID": DUIDS}},
+
+ # MNSP bidding — same shape as BIDPEROFFER_D / BIDDAYOFFER_D but keyed on
+ # LINKID (MNSP interconnectors bid as directional links).
+ #
+ # MNSP_PEROFFER has coverage gaps NEMOSIS can't currently read past:
+ # - From ~April 2021 AEMO began writing MNSP_BIDOFFERPERIOD data into
+ # the PUBLIC_DVD_MNSP_PEROFFER_* archives with different column names
+ # (TRADINGDATE / OFFERDATETIME instead of SETTLEMENTDATE / OFFERDATE).
+ # - At the Aug-2024 archive format switch AEMO also renamed the file
+ # stem to MNSP_BIDOFFERPERIOD outright.
+ # NEMOSIS's defaults.names knows neither of these transitions, so we cap
+ # MNSP_PEROFFER coverage at 2021-02 (the last clean MMS month) — same
+ # story as BIDPEROFFER_D but without the 2024-09 recovery era.
+ #
+ # MNSP_DAYOFFER kept its column names across both boundaries, so the
+ # standard bidding era set applies — same bidding-gap shape as BIDPEROFFER_D
+ # / BIDDAYOFFER_D above (no 2022-01, recovery at 2024-09).
+ "MNSP_PEROFFER": {"eras": ["2018-05", "2020-01", "2021-02"], "filter": {"LINKID": MNSP_LINKS}},
+ "MNSP_DAYOFFER": {"eras": ["2018-05", "2020-01", "2021-05", "2024-09", "2025-01"], "filter": {"LINKID": MNSP_LINKS}},
+
+ # Scrape-only tables — all live on AEMO's rolling current-data pages.
+ "DAILY_REGION_SUMMARY": {"eras": ["recent"], "filter": {"REGIONID": REGIONS}},
+ "NEXT_DAY_DISPATCHLOAD": {"eras": ["recent"], "filter": {"DUID": DUIDS}},
+ "INTERMITTENT_GEN_SCADA": {"eras": ["recent"], "filter": {"DUID": DUIDS}},
+
+ # Rooftop PV — introduced mid-2019.
+ "ROOFTOP_PV_ACTUAL": {"eras": ["2020-01", "2021-05", "2022-01", "2024-08", "2025-01"], "filter": {"REGIONID": REGIONS}},
+
+ # Effective-date config tables. These publish sparsely — records are
+ # configuration changes, not time-series observations — and NEMOSIS's
+ # "all" search_type would normally iterate every month from July 2009
+ # onward. Tests narrow the scan by monkeypatching
+ # `defaults.nem_data_model_start_time` so only a single era month is
+ # probed. `keep_full_month: True` tells build.py to skip the usual
+ # first-3 / last-2 day time trim, since a sparse table can easily end
+ # up with no rows in that window. All eras pinned to 2021-05 — these
+ # tables don't have format transitions that warrant multi-era coverage.
+ "INTERCONNECTOR": {"eras": ["2021-05"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}, "keep_full_month": True},
+ "MNSP_INTERCONNECTOR": {"eras": ["2021-05"], "filter": {"LINKID": MNSP_LINKS}, "keep_full_month": True},
+ "INTERCONNECTORCONSTRAINT": {"eras": ["2021-05"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}, "keep_full_month": True},
+ "LOSSMODEL": {"eras": ["2021-05"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}, "keep_full_month": True},
+ "LOSSFACTORMODEL": {"eras": ["2021-05"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}, "keep_full_month": True},
+ "DUDETAIL": {"eras": ["2021-05"], "filter": {"DUID": DUIDS}, "keep_full_month": True},
+ "DUDETAILSUMMARY": {"eras": ["2021-05"], "filter": {"DUID": DUIDS}, "keep_full_month": True},
+ "PARTICIPANT": {"eras": ["2018-05", "2021-05"], "filter": {"PARTICIPANTID": PARTICIPANTS}, "keep_full_month": True}, # 2018 pair gives a wide LASTCHANGED span
+ "GENCONDATA": {"eras": ["2021-05"], "filter": {"GENCONID": GENCON_IDS}, "keep_full_month": True},
+ "SPDREGIONCONSTRAINT": {"eras": ["2021-05"], "filter": {"REGIONID": REGIONS}, "keep_full_month": True},
+ "SPDINTERCONNECTORCONSTRAINT": {"eras": ["2021-05"], "filter": {"INTERCONNECTORID": INTERCONNECTORS}, "keep_full_month": True},
+ "SPDCONNECTIONPOINTCONSTRAINT": {"eras": ["2021-05"], "filter": {"GENCONID": SPDCPC_GENCON_IDS}, "keep_full_month": True},
+ # MARKET_PRICE_THRESHOLDS has no group column (only 12 rows total) so no row filter is applied.
+ "MARKET_PRICE_THRESHOLDS": {"eras": ["2021-05"], "filter": {}, "keep_full_month": True},
+}
+
+
+# Static tables have no time dimension; each is downloaded once as current state.
+# The build script snapshots them to the fixture tree.
+STATIC_TABLES = [
+ "ELEMENTS_FCAS_4_SECOND",
+ "VARIABLES_FCAS_4_SECOND",
+ "Generators and Scheduled Loads",
+]
diff --git a/tests/test_data_fetch_methods.py b/tests/test_data_fetch_methods.py
deleted file mode 100644
index b5c6278..0000000
--- a/tests/test_data_fetch_methods.py
+++ /dev/null
@@ -1,1084 +0,0 @@
-import unittest
-import os
-from datetime import datetime, timedelta, date
-from dateutil.relativedelta import relativedelta
-import calendar
-import pandas as pd
-from nemosis import custom_tables, defaults, filters, data_fetch_methods
-from pandas.testing import assert_frame_equal
-from parameterized import parameterized
-
-recent_test_month = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0) - relativedelta(months=3)
-
-previous_month = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0) - relativedelta(months=1)
-
-# Discontinued dates are not precise but should work with current testing dates.
-discontinued_after = {
- "TRADINGLOAD": "2022/01/01 00:00:00",
- "TRADINGREGIONSUM": "2022/01/01 00:00:00",
-}
-
-test_iterations = [
- [recent_test_month],
- [datetime(year=2018, month=5, day=1)]
-]
-
-def get_previous_month(month):
- return month - relativedelta(months=1)
-
-def get_next_month(month):
- return month + relativedelta(months=1)
-
-def last_day_of_month(year, month):
- day_number = date(year, month, calendar.monthrange(year, month)[1]).day
- return str(day_number).zfill(2)
-
-class TestDynamicDataCompilerWithSettlementDateFiltering(unittest.TestCase):
- def setUp(self):
- self.table_names = [
- "BIDDAYOFFER_D",
- "BIDPEROFFER_D",
- "DISPATCHLOAD",
- "DISPATCHCONSTRAINT",
- "DISPATCH_UNIT_SCADA",
- "DISPATCHPRICE",
- "DISPATCHINTERCONNECTORRES",
- "DISPATCHREGIONSUM",
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- "ROOFTOP_PV_ACTUAL",
- ]
-
- self.table_types = {
- "DISPATCHLOAD": "DUID",
- "DISPATCHCONSTRAINT": "CONSTRAINTID",
- "DISPATCH_UNIT_SCADA": "DUIDONLY",
- "DISPATCHPRICE": "REGIONID",
- "DISPATCHINTERCONNECTORRES": "INTERCONNECTORID",
- "DISPATCHREGIONSUM": "REGIONID",
- "BIDPEROFFER_D": "DUID-BIDTYPE",
- "BIDDAYOFFER_D": "DUID-BIDTYPE",
- "TRADINGLOAD": "DUIDONLY",
- "TRADINGPRICE": "REGIONIDONLY",
- "TRADINGREGIONSUM": "REGIONIDONLY",
- "TRADINGINTERCONNECT": "INTERCONNECTORIDONLY",
- "ROOFTOP_PV_ACTUAL": "REGIONID-PVVALUETYPE",
- }
-
- self.table_filters = {
- "DISPATCHLOAD": ["DUID", "INTERVENTION"],
- "DISPATCHCONSTRAINT": ["CONSTRAINTID", "INTERVENTION"],
- "DISPATCH_UNIT_SCADA": ["DUID"],
- "DISPATCHPRICE": ["REGIONID", "INTERVENTION"],
- "DISPATCHINTERCONNECTORRES": ["INTERCONNECTORID", "INTERVENTION"],
- "DISPATCHREGIONSUM": ["REGIONID", "INTERVENTION"],
- "BIDPEROFFER_D": ["DUID", "BIDTYPE"],
- "BIDDAYOFFER_D": ["DUID", "BIDTYPE"],
- "TRADINGLOAD": ["DUID"],
- "TRADINGPRICE": ["REGIONID"],
- "TRADINGREGIONSUM": ["REGIONID"],
- "TRADINGINTERCONNECT": ["INTERCONNECTORID"],
- "ROOFTOP_PV_ACTUAL": ["REGIONID", "TYPE"],
- }
-
- self.filter_values = {
- "DUID": (["AGLHAL"], [0]),
- "DUIDONLY": (["AGLHAL"],),
- "REGIONID": (["SA1"], [0]),
- "REGIONIDONLY": (["SA1"],),
- "INTERCONNECTORID": (["VIC1-NSW1"], [0]),
- "INTERCONNECTORIDONLY": (["VIC1-NSW1"],),
- "CONSTRAINTID": (["DATASNAP_DFS_Q_CLST"], [0]),
- "DUID-BIDTYPE": (["AGLHAL"], ["ENERGY"]),
- "REGIONID-TYPE": (['NSW1'], ['DAILY']),
- "REGIONID-PVVALUETYPE": (['NSW1'], ['SATELLITE', 'DAILY']),
- }
- self.half_hourly_tables = [
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- "ROOFTOP_PV_ACTUAL"
- ]
- self.change_to_five_min_table = [
- "TRADINGPRICE",
- "TRADINGINTERCONNECT",
- ]
- self.change_to_five_min_year = 2022
-
- def _pv_extra_clean_up(self, data):
- pv_types = data['TYPE'].unique()
- if 'DAILY' in pv_types and 'SATELLITE' in pv_types:
- data = data[data['TYPE'] == 'SATELLITE'].copy()
- return data
-
- @parameterized.expand(test_iterations)
- def test_dispatch_tables_start_of_month(self, test_month):
- start_time = f"{test_month.year}/{test_month.month}/01 00:00:00"
- end_time = f"{test_month.year}/{test_month.month}/01 05:15:00"
- for table in self.table_names:
- if table in discontinued_after and discontinued_after[table] < start_time:
- continue
- print(f"Testing {table} returning values at start of month.")
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- filter_cols = self.table_filters[table]
- cols = [dat_col, *filter_cols]
- expected_length = 63
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if (table in self.half_hourly_tables and
- not (table in self.change_to_five_min_table and test_month.year > self.change_to_five_min_year)):
- expected_length = 10
- expected_first_time = f"{test_month.year}/{test_month.month}/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_last_time = f"{test_month.year}/{test_month.month}/01 05:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- if "ONLY" not in table_type:
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- expected_length = 2
- expected_last_time = f"{test_month.year}/{test_month.month}/01 00:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- previous_month = get_previous_month(test_month)
- day = last_day_of_month(previous_month.year, previous_month.month)
- expected_first_time = f"{previous_month.year}/{previous_month.month}/{day} 00:00:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- print(start_time)
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- )
- if table == 'ROOFTOP_PV_ACTUAL':
- data = self._pv_extra_clean_up(data)
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- @parameterized.expand(test_iterations)
- def test_dispatch_tables_end_of_month(self, test_month):
- day = last_day_of_month(test_month.year, test_month.month)
- start_time = f"{test_month.year}/{test_month.month}/{day} 21:00:00"
- following_month = get_next_month(test_month)
- end_time = f"{following_month.year}/{following_month.month}/01 00:00:00"
- for table in self.table_names:
- if table in discontinued_after and discontinued_after[table] < start_time:
- continue
- print("Testing {} returing values at end of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- filter_cols = self.table_filters[table]
- cols = [dat_col, *filter_cols]
- expected_length = 36
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if (table in self.half_hourly_tables and
- not (table in self.change_to_five_min_table and test_month.year > self.change_to_five_min_year)):
- expected_length = 6
- expected_first_time = f"{test_month.year}/{test_month.month}/{day} 21:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- if "ONLY" not in table_type:
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- expected_length = 1
- expected_last_time = expected_first_time.replace(hour=0, minute=0)
- expected_first_time = expected_first_time.replace(hour=0, minute=0)
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- )
- if table == 'ROOFTOP_PV_ACTUAL':
- data = self._pv_extra_clean_up(data)
- data = data.sort_values(dat_col)
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- @parameterized.expand(test_iterations)
- def test_dispatch_tables_straddle_2_months(self, test_month):
- day = last_day_of_month(test_month.year, test_month.month)
- start_time = f"{test_month.year}/{test_month.month}/{day} 21:00:00"
- following_month = get_next_month(test_month)
- end_time = f"{following_month.year}/{following_month.month}/01 21:00:00"
- for table in self.table_names:
- if table in discontinued_after and discontinued_after[table] < start_time:
- continue
- print(f"Testing {table} returing values from adjacent months.")
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- filter_cols = self.table_filters[table]
- cols = [dat_col, *filter_cols]
- expected_length = 288
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if (table in self.half_hourly_tables and
- not (table in self.change_to_five_min_table and test_month.year > self.change_to_five_min_year)):
- expected_length = 48
- expected_first_time = f"{test_month.year}/{test_month.month}/{day} 21:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- if "ONLY" not in table_type:
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- expected_length = 2
- expected_last_time = expected_last_time.replace(hour=0, minute=0)
- expected_first_time = expected_first_time.replace(hour=0, minute=0)
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- )
- if table == 'ROOFTOP_PV_ACTUAL':
- data = self._pv_extra_clean_up(data)
- data = data.sort_values(dat_col)
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- @parameterized.expand(test_iterations)
- def test_dispatch_tables_start_of_year(self, test_month):
- start_time = f"{test_month.year}/01/01 00:00:00"
- following_month = get_next_month(test_month)
- end_time = f"{following_month.year}/01/01 01:00:00"
- for table in self.table_names:
- if table in discontinued_after and discontinued_after[table] < start_time:
- continue
- print("Testing {} returing values at start of year.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- filter_cols = self.table_filters[table]
- cols = [dat_col, *filter_cols]
- expected_length = 12
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if (table in self.half_hourly_tables and
- not (table in self.change_to_five_min_table and test_month.year > self.change_to_five_min_year)):
- expected_length = 2
- expected_first_time = f"{test_month.year}/01/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- if "ONLY" not in table_type:
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- expected_length = 1
- expected_last_time = expected_last_time.replace(
- hour=0, minute=0
- ) - timedelta(days=1)
- expected_first_time = expected_first_time.replace(
- hour=0, minute=0
- ) - timedelta(days=1)
-
- if table in ["BIDDAYOFFER_D", "BIDPEROFFER_D"]:
- """Skip because this test is currently in bidding data gap."""
- continue
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- )
- if table == 'ROOFTOP_PV_ACTUAL':
- data = self._pv_extra_clean_up(data)
- data = data.sort_values(dat_col)
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- @parameterized.expand(test_iterations)
- def test_dispatch_tables_end_of_year(self, test_month):
- start_time = f"{test_month.year - 1}/12/31 23:00:00"
- following_month = get_next_month(test_month)
- end_time = f"{following_month.year}/01/01 00:00:00"
- for table in self.table_names:
- if table in discontinued_after and discontinued_after[table] < start_time:
- continue
- print("Testing {} returing values at end of year.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- filter_cols = self.table_filters[table]
- cols = [dat_col, *filter_cols]
- expected_length = 12
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if (table in self.half_hourly_tables and
- not (table in self.change_to_five_min_table and test_month.year > self.change_to_five_min_year)):
- expected_length = 2
- expected_first_time = f"{test_month.year - 1}/12/31 23:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- if "ONLY" not in table_type:
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- expected_length = 1
- expected_first_time = expected_first_time.replace(hour=0, minute=0)
- expected_last_time = expected_first_time
-
- if table in ["BIDDAYOFFER_D", "BIDPEROFFER_D"]:
- """Skip because this test is currently in bidding data gap."""
- continue
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- )
- if table == 'ROOFTOP_PV_ACTUAL':
- data = self._pv_extra_clean_up(data)
- data = data.sort_values(dat_col)
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- @parameterized.expand(test_iterations)
- def test_dispatch_tables_straddle_years(self, test_month):
- start_time = f"{test_month.year - 1}/12/31 23:00:00"
- following_month = get_next_month(test_month)
- end_time = f"{following_month.year}/01/01 01:00:00"
- for table in self.table_names:
- if table in discontinued_after and discontinued_after[table] < start_time:
- continue
- print(f"Testing {table} returning values from adjacent years.")
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- filter_cols = self.table_filters[table]
- cols = [dat_col, *filter_cols]
- expected_length = 24
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if (table in self.half_hourly_tables and
- not (table in self.change_to_five_min_table and test_month.year > self.change_to_five_min_year)):
- expected_length = 4
- expected_first_time = f"{test_month.year - 1}/12/31 23:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- if "ONLY" not in table_type:
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- expected_length = 1
- expected_first_time = expected_first_time.replace(hour=0, minute=0)
- expected_last_time = expected_first_time
- if table in ["BIDDAYOFFER_D", "BIDPEROFFER_D"]:
- """Skip because this test is currently in bidding data gap."""
- continue
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- )
- if table == 'ROOFTOP_PV_ACTUAL':
- data = self._pv_extra_clean_up(data)
- data = data.sort_values(dat_col)
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
-
-class TestDynamicDataCompilerWithSettlementDateFilteringNextDayTables(
- unittest.TestCase
-):
-
- def setUp(self):
- self.table_names = [
- "DAILY_REGION_SUMMARY",
- "NEXT_DAY_DISPATCHLOAD",
- "INTERMITTENT_GEN_SCADA"
- ]
-
- self.table_filters = {
- "DAILY_REGION_SUMMARY": ["REGIONID"],
- "NEXT_DAY_DISPATCHLOAD": ["DUID"],
- "INTERMITTENT_GEN_SCADA": ["DUID", "SCADA_TYPE"]
- }
-
- # Filter for bids at the start of the 2021-06-01 file and the end of the 2021-05-31, to make sure that we aren't
- # skipping any of the data file rows.
- self.filter_values = {
- "DAILY_REGION_SUMMARY": (
- ["NSW1"],
- ),
- "NEXT_DAY_DISPATCHLOAD": (
- ['AGLHAL'],
- ),
- "INTERMITTENT_GEN_SCADA": (
- ['ADPPV1'],
- ['ELAV']
- )
- }
-
- def test_dispatch_tables_start_of_month(self):
- test_month = previous_month
- start_time = f"{test_month.year}/{test_month.month}/01 00:00:00"
- end_time = f"{test_month.year}/{test_month.month}/01 05:15:00"
- for table in self.table_names:
- print(f"Testing {table} returning values at start of month one.")
- dat_col = defaults.primary_date_columns[table]
- expected_length = 63 * 1
- expected_number_of_columns = len(defaults.table_columns[table])
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=True,
- filter_cols=self.table_filters[table],
- filter_values=self.filter_values[table]
- )
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- def test_dispatch_tables_middle_of_month_and_day(self):
- test_month = previous_month
- start_time = f"{test_month.year}/{test_month.month}/05 12:00:00"
- end_time = f"{test_month.year}/{test_month.month}/05 17:15:00"
- for table in self.table_names:
- print(f"Testing {table} returning values at start of month one.")
- dat_col = defaults.primary_date_columns[table]
- expected_length = 63 * 1
- expected_number_of_columns = len(defaults.table_columns[table])
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=True,
- filter_cols=self.table_filters[table],
- filter_values=self.filter_values[table]
- )
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- def test_dispatch_tables_start_market_day(self):
- test_month = previous_month
- start_time = f"{test_month.year}/{test_month.month}/05 04:00:00"
- end_time = f"{test_month.year}/{test_month.month}/05 04:05:00"
- for table in self.table_names:
- print(f"Testing {table} returning values at start of month one.")
- dat_col = defaults.primary_date_columns[table]
- expected_length = 1
- expected_number_of_columns = len(defaults.table_columns[table])
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=True,
- filter_cols=self.table_filters[table],
- filter_values=self.filter_values[table]
- )
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- def test_dispatch_tables_end_market_day(self):
- test_month = previous_month
- start_time = f"{test_month.year}/{test_month.month}/05 03:55:00"
- end_time = f"{test_month.year}/{test_month.month}/05 04:00:00"
- for table in self.table_names:
- print(f"Testing {table} returning values at start of month one.")
- dat_col = defaults.primary_date_columns[table]
- expected_length = 1
- expected_number_of_columns = len(defaults.table_columns[table])
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=True,
- filter_cols=self.table_filters[table],
- filter_values=self.filter_values[table]
- )
- data = data.reset_index(drop=True)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
-
-class TestDynamicDataCompilerWithEffectiveDateFiltering(unittest.TestCase):
- def setUp(self):
- self.table_names = [
- "GENCONDATA",
- "SPDREGIONCONSTRAINT",
- "SPDCONNECTIONPOINTCONSTRAINT",
- "SPDINTERCONNECTORCONSTRAINT",
- ]
-
- @parameterized.expand([
- [recent_test_month],
- [datetime(year=2018, month=5, day=1)]
- ])
- def test_filtering_for_one_interval_returns(self, test_month):
- start_time = f"{test_month.year}/{test_month.month}/20 23:00:00"
- end_time = f"{test_month.year}/{test_month.month}/20 23:05:00"
- for table in self.table_names:
- print("Testing {} returing values for 1 interval.".format(table))
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=False,
- select_columns=defaults.table_primary_keys[table],
- )
- group_cols = [
- col
- for col in defaults.table_primary_keys[table]
- if col != "EFFECTIVEDATE"
- ]
- contains_duplicates = data.duplicated(group_cols).any()
- self.assertEqual(False, contains_duplicates)
- not_empty = data.shape[0] > 0
- self.assertEqual(True, not_empty)
- print("Passed")
-
-
-class TestCacheCompiler(unittest.TestCase):
- def setUp(self):
- self.table_names = [
- "BIDDAYOFFER_D",
- "BIDPEROFFER_D",
- "DISPATCHLOAD",
- "DISPATCHCONSTRAINT",
- "DISPATCH_UNIT_SCADA",
- "DISPATCHPRICE",
- "DISPATCHINTERCONNECTORRES",
- "DISPATCHREGIONSUM",
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- ]
- self.id_cols = {
- "DISPATCHLOAD": "DUID",
- "DISPATCHCONSTRAINT": "CONSTRAINTID",
- "DISPATCH_UNIT_SCADA": "DUID",
- "DISPATCHPRICE": "REGIONID",
- "DISPATCHINTERCONNECTORRES": "INTERCONNECTORID",
- "DISPATCHREGIONSUM": "REGIONID",
- "BIDPEROFFER_D": "DUID",
- "BIDDAYOFFER_D": "DUID",
- "TRADINGLOAD": "DUID",
- "TRADINGPRICE": "REGIONID",
- "TRADINGREGIONSUM": "REGIONID",
- "TRADINGINTERCONNECT": "INTERCONNECTORID",
- }
-
- def test_caching_and_typing_works_feather(self):
- start_time = "2018/02/20 23:00:00"
- end_time = "2018/02/20 23:30:00"
- for table in self.table_names:
- dat_col = defaults.primary_date_columns[table]
- id_col = self.id_cols[table]
- print(f"Testing {table} returing values for 1 interval.")
- data_fetch_methods.cache_compiler(
- start_time, end_time, table, defaults.raw_data_cache, fformat="feather"
- )
- data = data_fetch_methods.dynamic_data_compiler(
- start_time, end_time, table, defaults.raw_data_cache, fformat="feather"
- )
- dat_col_type = data[dat_col].dtype
- id_col_type = data[id_col].dtype
- not_empty = data.shape[0] > 0
- not_typed = all(data.dtypes == "object")
- self.assertTrue(not_empty)
- self.assertFalse(not_typed)
- self.assertEqual(dat_col_type, " 0
- not_typed = all(data.dtypes == "object")
- self.assertTrue(not_empty)
- self.assertFalse(not_typed)
- self.assertEqual(dat_col_type, " 0
- self.assertEqual(True, not_empty)
- print("Passed")
-
-
-class TestDynamicDataCompilerWithLastChangedFiltering(unittest.TestCase):
- @parameterized.expand([
- [recent_test_month],
- [datetime(year=2018, month=5, day=1)]
- ])
- def test_that_a_narrow_time_range_returns_one_entry_per_participantid(self, test_month):
- start_time = f"{test_month.year}/{test_month.month}/01 00:00:00"
- end_time = f"{test_month.year}/{test_month.month}/01 00:05:00"
- table = "PARTICIPANT"
- cols = ["PARTICIPANTID", "NAME", "LASTCHANGED"]
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- )
- data = data.groupby(['PARTICIPANTID'], as_index=False).count()
- assert (data['NAME'] == 1).all()
- print("Passed")
-
- def test_that_a_wide_time_range_returns_more_than_one_entry_for_at_least_some_participantids(self):
- table = "PARTICIPANT"
- data = data_fetch_methods.dynamic_data_compiler(
- "2013/01/01 00:00:00",
- "2023/01/01 00:00:00",
- table,
- defaults.raw_data_cache,
- )
- data = data.groupby(['PARTICIPANTID'], as_index=False).count()
- assert (data['NAME'] > 1).any()
- print("Passed")
-
- def test_filter_of_raw_data(self):
- table = "PARTICIPANT"
- start_time_str = defaults.nem_data_model_start_time
- end_time_str = "2023/01/01 00:00:00"
- start_search = defaults.nem_data_model_start_time
- start_time = datetime.strptime(start_time_str, "%Y/%m/%d %H:%M:%S")
- end_time = datetime.strptime(end_time_str, "%Y/%m/%d %H:%M:%S")
- start_search = datetime.strptime(start_search, "%Y/%m/%d %H:%M:%S")
- data = data_fetch_methods.dynamic_data_compiler(
- start_time_str,
- end_time_str,
- table,
- defaults.raw_data_cache,
- )
- raw_data = data_fetch_methods._dynamic_data_fetch_loop(
- start_search,
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=['PARTICIPANTID', 'NAME', 'PARTICIPANTCLASSID', 'LASTCHANGED'],
- date_filter=filters.filter_on_last_changed,
- fformat='feather',
- keep_csv=True,
- rebuild=False)
- raw_data = pd.concat(raw_data)
- raw_data = raw_data.groupby(['PARTICIPANTID', 'LASTCHANGED'], as_index=False).count().\
- groupby(['PARTICIPANTID'], as_index=False).count().sort_values('PARTICIPANTID').reset_index(drop=True)
- data = data.groupby(['PARTICIPANTID', 'LASTCHANGED'], as_index=False).count().\
- groupby(['PARTICIPANTID'], as_index=False).count().sort_values('PARTICIPANTID').reset_index(drop=True)
- assert_frame_equal(raw_data, data)
-
-
-class TestFCAS4SecondData(unittest.TestCase):
- def setUp(self):
- self.start_day = (datetime.now() - timedelta(30)).replace(
- hour=0, minute=0, second=0, microsecond=0
- )
- self.start_month = self.start_day.replace(
- day=1, hour=0, minute=0, second=0, microsecond=0
- )
- self.start_year = self.start_day.replace(
- month=1, day=1, hour=0, minute=0, second=0, microsecond=0
- )
- pass
-
- def test_dispatch_tables_start_of_month(self):
- table = "FCAS_4_SECOND"
- start_time = self.start_day
- minute_offset = 5
- end_time = start_time + timedelta(minutes=minute_offset)
- start_str = start_time.strftime("%Y/%m/%d %H:%M:%S")
- end_str = end_time.strftime("%Y/%m/%d %H:%M:%S")
-
- print("Testing {} returing values at start of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- cols = [dat_col, "ELEMENTNUMBER", "VARIABLENUMBER"]
- # expected length assumes first data point is at 00:00:00
- expected_length = 15 * minute_offset
- length_check = False
- expected_number_of_columns = 3
- data = data_fetch_methods.dynamic_data_compiler(
- start_str,
- end_str,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=False,
- select_columns=cols,
- )
- length_array = data[dat_col].drop_duplicates()
- if length_array.shape[0] == expected_length:
- length_check = True
- # case if first data point is 00:00:01/02/03
- elif length_array.shape[0] == expected_length - 1:
- length_check = True
- self.assertTrue(length_check)
- self.assertEqual(expected_number_of_columns, data.shape[1])
- print("Passed")
-
- def test_fcas_tables_end_of_month(self):
- table = "FCAS_4_SECOND"
- minute_offset = 5
- start_time = self.start_month - timedelta(minutes=minute_offset)
- end_time = start_time + timedelta(minutes=minute_offset * 2)
- start_str = start_time.strftime("%Y/%m/%d %H:%M:%S")
- end_str = end_time.strftime("%Y/%m/%d %H:%M:%S")
-
- print("Testing {} returing values at end of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- cols = [dat_col, "ELEMENTNUMBER", "VARIABLENUMBER"]
- # expected length assumes first data point is at 00:00:00
- expected_length = 15 * (minute_offset * 2)
- length_check = False
- expected_number_of_columns = 3
- data = data_fetch_methods.dynamic_data_compiler(
- start_str,
- end_str,
- table,
- defaults.raw_data_cache,
- fformat="feather",
- keep_csv=False,
- select_columns=cols,
- )
- length_array = data[dat_col].drop_duplicates()
- if length_array.shape[0] == expected_length:
- length_check = True
- # case if first data point is 00:00:01/02/03
- elif length_array.shape[0] == expected_length - 1:
- length_check = True
- print(length_array.shape[0])
- self.assertTrue(length_check)
- self.assertEqual(expected_number_of_columns, data.shape[1])
- print("Passed")
-
- @unittest.skipIf(
- (datetime.now() - datetime(year=datetime.now().year, month=1, day=1)).days > 60,
- "start of year data not available: > 60 days ago",
- )
- def test_fcas_tables_end_of_year(self):
- table = "FCAS_4_SECOND"
- minute_offset = 5
- start_time = self.start_year - timedelta(minutes=minute_offset)
- end_time = start_time + timedelta(minutes=minute_offset * 2)
- start_str = start_time.strftime("%Y/%m/%d %H:%M:%S")
- end_str = end_time.strftime("%Y/%m/%d %H:%M:%S")
-
- print("Testing {} returing values at end of year.".format(table))
- dat_col = defaults.primary_date_columns[table]
- cols = [dat_col, "ELEMENTNUMBER", "VARIABLENUMBER"]
- expected_length = 15 * (minute_offset * 2)
- length_check = False
- expected_number_of_columns = 3
- data = data_fetch_methods.dynamic_data_compiler(
- start_str,
- end_str,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- fformat="feather",
- keep_csv=False,
- )
- length_array = data[dat_col].drop_duplicates()
- if length_array.shape[0] == expected_length:
- length_check = True
- # case if first data point is 00:00:01/02/03
- elif length_array.shape[0] == expected_length - 1:
- length_check = True
- self.assertTrue(length_check)
- self.assertEqual(expected_number_of_columns, data.shape[1])
- print("Passed")
-
-
-class TestStaticTables(unittest.TestCase):
- def setUp(self):
- pass
-
- def test_fcas_elements_table(self):
- start_time = "2017/12/31 23:55:04"
- end_time = "2018/01/01 00:05:00"
- table = "ELEMENTS_FCAS_4_SECOND"
- cols = ["ELEMENTNUMBER", "EMSNAME"]
- filter_cols = ("ELEMENTNUMBER",)
- func = data_fetch_methods._static_table_wrapper_for_gui
- data = func(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=(["1"],),
- )
- expected_length = 1
- expected_number_of_columns = 2
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- print("Passed")
-
- def test_fcas_variable_table(self):
- start_time = "2018/12/31 23:55:04"
- end_time = "2018/01/01 00:05:00"
- table = "VARIABLES_FCAS_4_SECOND"
- cols = ["VARIABLENUMBER", "VARIABLETYPE"]
- filter_cols = ("VARIABLENUMBER",)
- func = data_fetch_methods._static_table_wrapper_for_gui
- data = func(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=(["2"],),
- )
- expected_length = 1
- expected_number_of_columns = 2
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- print("Passed")
-
- def test_registration_list(self):
- table = "Generators and Scheduled Loads"
- cols = ["DUID", "Technology Type - Primary"]
- filter_cols = ("DUID",)
- data = data_fetch_methods.static_table_xl(
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=(["AGLHAL"],),
- )
- expected_length = 1
- expected_number_of_columns = 2
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- print("Passed")
-
-
-class TestCustomTables(unittest.TestCase):
- def setUp(self):
- pass
-
- def test_works_on_recent_data(self):
- table = "FCAS_4_SECOND"
- minute_offset = 5
- start_time = datetime(
- year=datetime.now().year,
- month=datetime.now().month,
- day=1
- ) - timedelta(days=40)
- end_time = start_time + timedelta(minutes=minute_offset * 2)
- start_time = start_time.strftime("%Y/%m/%d %H:%M:%S")
- end_time = end_time.strftime("%Y/%m/%d %H:%M:%S")
- print("Testing custom table {}.".format(table))
- data = custom_tables.fcas4s_scada_match(
- start_time, end_time, table, defaults.raw_data_cache
- )
- data = data.reset_index(drop=True)
- contains_duplicates = data.duplicated(["MARKETNAME"]).any()
- self.assertEqual(False, contains_duplicates)
- contains_duplicates = data.duplicated(["ELEMENTNUMBER"]).any()
- self.assertEqual(False, contains_duplicates)
- not_empty = data.shape[0] > 0
- self.assertEqual(True, not_empty)
- print("Passed")
diff --git a/tests/test_errors.py b/tests/test_errors.py
new file mode 100644
index 0000000..f2b6bdb
--- /dev/null
+++ b/tests/test_errors.py
@@ -0,0 +1,176 @@
+"""Offline tests for NEMOSIS error paths and cache idempotency.
+
+Covers argument-validation errors (`UserInputError`) and network/data-
+availability errors (`NoDataToReturn`) for `dynamic_data_compiler`,
+`cache_compiler`, and `static_table`. Also verifies that after a
+successful fetch populates the cache, a second call with the same
+arguments reads from the cache rather than re-fetching.
+
+All tests run against the `nemosis_fixture` dummy server — no network.
+The NoDataToReturn cases are triggered either by querying a date range
+the fixture doesn't cover (dummy server 404s) or by patching the
+static-table URL to an invalid path.
+"""
+import pytest
+
+from nemosis import cache_compiler, defaults, dynamic_data_compiler, static_table
+from nemosis.custom_errors import NoDataToReturn, UserInputError
+
+
+# ---------------------------------------------------------------------------
+# Argument-validation: dynamic_data_compiler
+# ---------------------------------------------------------------------------
+
+def test_dynamic_bad_table_name(nemosis_fixture):
+ with pytest.raises(UserInputError, match="not a dynamic table"):
+ dynamic_data_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "NOTATABLE", str(nemosis_fixture),
+ )
+
+
+def test_dynamic_bad_fformat(nemosis_fixture):
+ with pytest.raises(UserInputError, match="fformat must be"):
+ dynamic_data_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ fformat="db",
+ )
+
+
+def test_dynamic_filter_col_not_in_select_columns(nemosis_fixture):
+ with pytest.raises(UserInputError, match="Filter columns not valid"):
+ dynamic_data_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+
+
+def test_dynamic_filter_col_not_a_real_column(nemosis_fixture):
+ """When `select_columns` is None the filter is validated against the
+ table's default columns — a nonexistent column should still fail."""
+ with pytest.raises(UserInputError, match="Filter columns not valid"):
+ dynamic_data_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ filter_cols=["NOTACOLUMN"],
+ filter_values=(["0"],),
+ )
+
+
+def test_dynamic_select_columns_all_requires_csv(nemosis_fixture):
+ with pytest.raises(UserInputError, match="select_columns='all'"):
+ dynamic_data_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ select_columns="all",
+ fformat="feather",
+ )
+
+
+# ---------------------------------------------------------------------------
+# Argument-validation: cache_compiler
+# ---------------------------------------------------------------------------
+
+def test_cache_bad_table_name(nemosis_fixture):
+ with pytest.raises(UserInputError, match="not a dynamic table"):
+ cache_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "NOTATABLE", str(nemosis_fixture),
+ )
+
+
+def test_cache_bad_fformat(nemosis_fixture):
+ """`cache_compiler` rejects 'csv' too — only feather/parquet."""
+ with pytest.raises(UserInputError, match="fformat must be"):
+ cache_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ fformat="csv",
+ )
+
+
+def test_cache_select_columns_requires_rebuild(nemosis_fixture):
+ with pytest.raises(UserInputError, match="rebuild=True"):
+ cache_compiler(
+ "2018/05/01 00:00:00", "2018/05/01 01:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "RRP"],
+ )
+
+
+# ---------------------------------------------------------------------------
+# Argument-validation: static_table
+# ---------------------------------------------------------------------------
+
+def test_static_bad_table_name(nemosis_fixture):
+ with pytest.raises(UserInputError, match="not a static table"):
+ static_table("NOTATABLE", str(nemosis_fixture))
+
+
+def test_static_filter_col_not_in_select_columns(nemosis_fixture):
+ with pytest.raises(UserInputError, match="Filter columns not valid"):
+ static_table(
+ "VARIABLES_FCAS_4_SECOND", str(nemosis_fixture),
+ select_columns=["VARIABLENUMBER"],
+ filter_cols=["VARIABLETYPE"],
+ filter_values=(["MW"],),
+ )
+
+
+# ---------------------------------------------------------------------------
+# Network / availability: NoDataToReturn
+# ---------------------------------------------------------------------------
+
+def test_dynamic_no_data_raises(nemosis_fixture):
+ """Query a date range the fixture doesn't cover — the dummy server
+ 404s every month, NEMOSIS gives up and raises NoDataToReturn."""
+ with pytest.raises(NoDataToReturn, match="Compiling data for table DISPATCHPRICE failed"):
+ dynamic_data_compiler(
+ "2000/01/01 00:00:00", "2000/02/01 00:00:00",
+ "DISPATCHPRICE", str(nemosis_fixture),
+ )
+
+
+def test_static_no_data_raises(nemosis_fixture, monkeypatch):
+ """Redirect a static-table URL to a path the dummy server doesn't
+ serve — NEMOSIS should raise NoDataToReturn, not crash cryptically."""
+ monkeypatch.setitem(
+ defaults.static_table_url,
+ "VARIABLES_FCAS_4_SECOND",
+ "http://127.0.0.1:1/does-not-exist.csv",
+ )
+ with pytest.raises(NoDataToReturn, match="Compiling data for table VARIABLES_FCAS_4_SECOND failed"):
+ static_table("VARIABLES_FCAS_4_SECOND", str(nemosis_fixture))
+
+
+# ---------------------------------------------------------------------------
+# Cache idempotency
+# ---------------------------------------------------------------------------
+
+def test_cache_is_reused_on_second_call(nemosis_fixture, monkeypatch):
+ """After the first call populates the cache with a feather file, a
+ second call with the same arguments must read from that cache rather
+ than re-fetching. We prove it by breaking the HTTP server mid-run:
+ the first call succeeds (fetches and caches), then we redirect the
+ AEMO URL to an invalid path, and the second call still succeeds."""
+ kwargs = dict(
+ start_time="2018/05/01 00:00:00",
+ end_time="2018/05/01 01:00:00",
+ table_name="DISPATCHPRICE",
+ raw_data_location=str(nemosis_fixture),
+ select_columns=["SETTLEMENTDATE", "REGIONID", "RRP", "INTERVENTION"],
+ filter_cols=["INTERVENTION"],
+ filter_values=([0],),
+ )
+ first = dynamic_data_compiler(**kwargs)
+ assert not first.empty
+
+ # Cache is now on disk. Break the network: any new fetch would 404.
+ monkeypatch.setattr(defaults, "aemo_mms_url", "http://127.0.0.1:1/nowhere")
+
+ second = dynamic_data_compiler(**kwargs)
+ assert first.equals(second)
diff --git a/tests/test_errors_and_warnings.py b/tests/test_errors_and_warnings.py
deleted file mode 100644
index 562e404..0000000
--- a/tests/test_errors_and_warnings.py
+++ /dev/null
@@ -1,330 +0,0 @@
-import unittest
-from nemosis import dynamic_data_compiler, cache_compiler, static_table
-from nemosis import defaults
-import os
-from unittest.mock import patch
-from io import StringIO
-
-
-class TestDynamicDataCompilerRaisesExpectedErrors(unittest.TestCase):
- def test_raise_error_for_incorrect_table_name(self):
- with self.assertRaises(Exception) as context:
- dynamic_data_compiler(
- "2000/01/01 00:00:00",
- "2000/02/01 00:00:00",
- "NOTATABLE",
- defaults.raw_data_cache,
- )
- self.assertTrue(
- "Table name provided is not a dynamic table." in str(context.exception)
- )
-
- def test_raise_error_for_no_data_returned(self):
- with self.assertRaises(Exception) as context:
- dynamic_data_compiler(
- "2000/01/01 00:00:00",
- "2000/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- )
- self.assertTrue(
- (
- f"Compiling data for table DISPATCHPRICE failed. "
- + "This probably because none of the requested data "
- + "could be download from AEMO. Check your internet "
- + "connection and that the requested data is archived on: "
- + "https://nemweb.com.au see nemosis.defaults for table specific urls."
- )
- in str(context.exception)
- )
-
- def test_raise_error_for_filter_column_not_in_select_columns(self):
- with self.assertRaises(Exception) as context:
- dynamic_data_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- select_columns=["REGIONID", "SETTLEMENTDATE", "RRP"],
- filter_cols=["INTERVENTION"],
- filter_values=(["0"],),
- )
- self.assertTrue(
- (
- "Filter columns not valid. They must be a part of "
- + "select_columns or the table defaults."
- )
- in str(context.exception)
- )
-
- def test_raise_error_for_filter_column_not_in_default_columns(self):
- with self.assertRaises(Exception) as context:
- dynamic_data_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- select_columns=["REGIONID", "SETTLEMENTDATE", "RRP"],
- filter_cols=["NOTACOLUMN"],
- filter_values=(["0"],),
- )
- self.assertTrue(
- (
- "Filter columns not valid. They must be a part of "
- + "select_columns or the table defaults."
- )
- in str(context.exception)
- )
-
- def test_raise_error_if_fformat_not_in_expected_set(self):
- with self.assertRaises(Exception) as context:
- dynamic_data_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- fformat="db",
- )
- self.assertTrue(
- "Argument fformat must be 'csv', 'feather' or 'parquet'"
- in str(context.exception)
- )
-
- def test_raise_error_if_select_columns_not_in_data(self):
- with self.assertRaises(Exception) as context:
- dynamic_data_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- select_columns=["NOTACOLUMN"],
- )
- self.assertIn(
- (
- f"None of columns ['NOTACOLUMN'] are in D:\\nemosis_test_cache\\PUBLIC_DVD_DISPATCHPRICE_201812010000.feather. "
- "This may be caused by user input if the 'select_columns' "
- "argument is being used, or by changed AEMO data formats. "
- "This error can be avoided by using the argument select_columns='all'."
- ),
- str(context.exception)
- )
-
- def test_using_select_columns_all_does_not_raise_error(self):
- price_data = dynamic_data_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- select_columns="all",
- fformat="csv",
- )
- expected_columns = [
- "I",
- "DISPATCH",
- "PRICE",
- "1",
- "SETTLEMENTDATE",
- "RUNNO",
- "REGIONID",
- "DISPATCHINTERVAL",
- "INTERVENTION",
- "RRP",
- "EEP",
- "ROP",
- "APCFLAG",
- "MARKETSUSPENDEDFLAG",
- "LASTCHANGED",
- "RAISE6SECRRP",
- "RAISE6SECROP",
- "RAISE6SECAPCFLAG",
- "RAISE60SECRRP",
- "RAISE60SECROP",
- "RAISE60SECAPCFLAG",
- "RAISE5MINRRP",
- "RAISE5MINROP",
- "RAISE5MINAPCFLAG",
- "RAISEREGRRP",
- "RAISEREGROP",
- "RAISEREGAPCFLAG",
- "LOWER6SECRRP",
- "LOWER6SECROP",
- "LOWER6SECAPCFLAG",
- "LOWER60SECRRP",
- "LOWER60SECROP",
- "LOWER60SECAPCFLAG",
- "LOWER5MINRRP",
- "LOWER5MINROP",
- "LOWER5MINAPCFLAG",
- "LOWERREGRRP",
- "LOWERREGROP",
- "LOWERREGAPCFLAG",
- "PRICE_STATUS",
- "PRE_AP_ENERGY_PRICE",
- "PRE_AP_RAISE6_PRICE",
- "PRE_AP_RAISE60_PRICE",
- "PRE_AP_RAISE5MIN_PRICE",
- "PRE_AP_RAISEREG_PRICE",
- "PRE_AP_LOWER6_PRICE",
- "PRE_AP_LOWER60_PRICE",
- "PRE_AP_LOWER5MIN_PRICE",
- "PRE_AP_LOWERREG_PRICE",
- "CUMUL_PRE_AP_ENERGY_PRICE",
- "CUMUL_PRE_AP_RAISE6_PRICE",
- "CUMUL_PRE_AP_RAISE60_PRICE",
- "CUMUL_PRE_AP_RAISE5MIN_PRICE",
- "CUMUL_PRE_AP_RAISEREG_PRICE",
- "CUMUL_PRE_AP_LOWER6_PRICE",
- "CUMUL_PRE_AP_LOWER60_PRICE",
- "CUMUL_PRE_AP_LOWER5MIN_PRICE",
- "CUMUL_PRE_AP_LOWERREG_PRICE",
- ]
- self.assertSequenceEqual(list(price_data.columns), expected_columns)
-
-class TestWarnings(unittest.TestCase):
- def test_no_parse_warning(self):
- with patch('sys.stderr', new=StringIO()) as fakeOutput:
- print('hello world')
-
- dynamic_data_compiler(
- start_time='2017/01/01 00:00:00',
- end_time='2017/01/01 00:05:00',
- table_name='DISPATCHPRICE',
- raw_data_location=defaults.raw_data_cache
- )
-
- stderr = fakeOutput.getvalue().strip()
- self.assertNotIn("UserWarning: Could not infer format", stderr)
-
-class TestCacheCompilerRaisesExpectedErrors(unittest.TestCase):
- def test_raise_error_for_incorrect_table_name(self):
- with self.assertRaises(Exception) as context:
- cache_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "NOTATABLE",
- defaults.raw_data_cache,
- fformat="db",
- )
- self.assertTrue(
- "Table name provided is not a dynamic table." in str(context.exception)
- )
-
- def test_raise_error_if_fformat_not_in_expected_set(self):
- with self.assertRaises(Exception) as context:
- cache_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- fformat="db",
- )
- self.assertTrue(
- "Argument fformat must be 'feather' or 'parquet'" in str(context.exception)
- )
-
- def test_raise_error_if_select_columns_used_without_rebuild_true(self):
- with self.assertRaises(Exception) as context:
- cache_compiler(
- "2019/01/01 00:00:00",
- "2019/02/01 00:00:00",
- "DISPATCHPRICE",
- defaults.raw_data_cache,
- select_columns="all",
- )
- self.assertTrue(
- (
- "The select_columns argument must be used with rebuild=True "
- + "to ensure the cache is built with the correct columns."
- )
- in str(context.exception)
- )
-
-
-
-class TestStaticTableRaisesExpectedErrors(unittest.TestCase):
- def test_raise_error_for_incorrect_table_name(self):
- with self.assertRaises(Exception) as context:
- static_table("NOTATABLE", defaults.raw_data_cache)
- self.assertTrue(
- "Table name provided is not a static table." in str(context.exception)
- )
-
- def test_raise_error_for_no_data_returned(self):
- good_url = defaults.static_table_url["VARIABLES_FCAS_4_SECOND"]
- defaults.static_table_url["VARIABLES_FCAS_4_SECOND"] = "bad_url"
- path_and_name = (
- defaults.raw_data_cache + "/" + defaults.names["VARIABLES_FCAS_4_SECOND"]
- )
- if os.path.isfile(path_and_name):
- os.remove(path_and_name)
- with self.assertRaises(Exception) as context:
- static_table("VARIABLES_FCAS_4_SECOND", defaults.raw_data_cache)
- self.assertTrue(
- (
- f"Compiling data for table VARIABLES_FCAS_4_SECOND failed. "
- + "This probably because none of the requested data "
- + "could be download from AEMO. Check your internet "
- + "connection and that the requested data is archived on: "
- + "https://nemweb.com.au see nemosis.defaults for table specific urls."
- )
- in str(context.exception)
- )
- defaults.static_table_url["VARIABLES_FCAS_4_SECOND"] = good_url
-
- def test_raise_error_for_filter_column_not_in_select_columns(self):
- with self.assertRaises(Exception) as context:
- static_table(
- "VARIABLES_FCAS_4_SECOND",
- defaults.raw_data_cache,
- select_columns=["VARIABLENUMBER"],
- filter_cols=["VARIABLETYPE"],
- filter_values=(["0"],),
- )
- self.assertTrue(
- (
- "Filter columns not valid. They must be a part of "
- + "select_columns or the table defaults."
- )
- in str(context.exception)
- )
-
- def test_raise_error_for_filter_column_not_in_default_columns(self):
- with self.assertRaises(Exception) as context:
- static_table(
- "VARIABLES_FCAS_4_SECOND",
- defaults.raw_data_cache,
- select_columns=["VARIABLENUMBER"],
- filter_cols=["NOTACOLUMN"],
- filter_values=(["0"],),
- )
- self.assertTrue(
- (
- "Filter columns not valid. They must be a part of "
- + "select_columns or the table defaults."
- )
- in str(context.exception)
- )
-
- def test_raise_error_if_select_columns_not_in_data(self):
- with self.assertRaises(Exception) as context:
- static_table(
- "VARIABLES_FCAS_4_SECOND",
- defaults.raw_data_cache,
- select_columns=["NOTACOLUMN"],
- )
- self.assertIn(
- (
- f"None of columns ['NOTACOLUMN'] are in D:\\nemosis_test_cache\\Ancillary Services Market Causer Pays Variables File.csv. "
- "This may be caused by user input if the 'select_columns' "
- "argument is being used, or by changed AEMO data formats. "
- "This error can be avoided by using the argument select_columns='all'."
- ),
- str(context.exception)
- )
-
- def test_using_select_columns_all_does_not_raise_error(self):
- price_data = static_table(
- "VARIABLES_FCAS_4_SECOND", defaults.raw_data_cache, select_columns="all"
- )
- expected_columns = ["VARIABLENUMBER", "VARIABLETYPE"]
- self.assertSequenceEqual(list(price_data.columns), expected_columns)
diff --git a/tests/test_format_options.py b/tests/test_format_options.py
deleted file mode 100644
index b6fbe77..0000000
--- a/tests/test_format_options.py
+++ /dev/null
@@ -1,376 +0,0 @@
-import unittest
-from datetime import timedelta
-from nemosis import defaults, data_fetch_methods
-import pandas as pd
-import os
-
-
-class TestFormatOptions(unittest.TestCase):
- def setUp(self):
- # TODO: Clean tests since only one table - BIDDAYOFFER_D is tested
- self.table_names = ["BIDDAYOFFER_D"]
-
- self.table_types = {
- "DISPATCHLOAD": "DUID",
- "DISPATCHCONSTRAINT": "CONSTRAINTID",
- "DISPATCH_UNIT_SCADA": "DUID",
- "DISPATCHPRICE": "REGIONID",
- "DISPATCHINTERCONNECTORRES": "INTERCONNECTORID",
- "DISPATCHREGIONSUM": "REGIONID",
- "BIDPEROFFER_D": "DUID-BIDTYPE",
- "BIDDAYOFFER_D": "DUID-BIDTYPE",
- "TRADINGLOAD": "DUID",
- "TRADINGPRICE": "REGIONID",
- "TRADINGREGIONSUM": "REGIONID",
- "TRADINGINTERCONNECT": "INTERCONNECTORID",
- }
-
- self.filter_values = {
- "DUID": (["AGLHAL"],),
- "REGIONID": (["SA1"],),
- "INTERCONNECTORID": (["VIC1-NSW1"],),
- "CONSTRAINTID": (["DATASNAP_DFS_Q_CLST"],),
- "DUID-BIDTYPE": (["AGLHAL", "ENERGY"],),
- }
-
- def test_dispatch_tables_start_of_month_just_csv_format_dont_keep(self):
- # Empty cache.
- for f in os.listdir(defaults.raw_data_cache):
- os.remove(os.path.join(defaults.raw_data_cache, f))
-
- start_time = "2018/02/01 00:00:00"
- end_time = "2018/02/01 05:15:00"
- for table in self.table_names:
- print("Testing {} returing values at start of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- cols = [dat_col, self.table_types[table]]
- filter_cols = (self.table_types[table],)
- expected_length = 63
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if table in [
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- ]:
- expected_length = 10
- expected_first_time = "2018/02/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_last_time = "2018/02/01 05:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- if table == "BIDPEROFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- expected_length = 2
- expected_last_time = "2018/02/01 00:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_first_time = "2018/01/31 00:00:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- fformat="csv",
- keep_csv=False,
- )
- data = data.reset_index(drop=True)
- print(table)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- self.assertEqual(len(os.listdir(defaults.raw_data_cache)), 0)
- print("Passed")
-
- def test_dispatch_tables_start_of_month_just_csv_format(self):
- # Empty cache.
- for f in os.listdir(defaults.raw_data_cache):
- os.remove(os.path.join(defaults.raw_data_cache, f))
-
- start_time = "2018/02/01 00:00:00"
- end_time = "2018/02/01 05:15:00"
- for table in self.table_names:
- print("Testing {} returing values at start of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- cols = [dat_col, self.table_types[table]]
- filter_cols = (self.table_types[table],)
- expected_length = 63
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if table in [
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- ]:
- expected_length = 10
- expected_first_time = "2018/02/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_last_time = "2018/02/01 05:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- if table == "BIDPEROFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- expected_length = 2
- expected_last_time = "2018/02/01 00:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_first_time = "2018/01/31 00:00:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- fformat="csv",
- )
- data = data.reset_index(drop=True)
- print(table)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- self.assertNotEqual(len(os.listdir(defaults.raw_data_cache)), 0)
- print("Passed")
-
- # Test that also works on second pass.
- start_time = "2018/02/01 00:00:00"
- end_time = "2018/02/01 05:15:00"
- for table in self.table_names:
- print("Testing {} returing values at start of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- cols = [dat_col, self.table_types[table]]
- filter_cols = (self.table_types[table],)
- expected_length = 63
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(
- end_time, format="%Y/%m/%d %H:%M:%S"
- )
- if table in [
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- ]:
- expected_length = 10
- expected_first_time = "2018/02/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_last_time = "2018/02/01 05:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- if table == "BIDPEROFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- expected_length = 2
- expected_last_time = "2018/02/01 00:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_first_time = "2018/01/31 00:00:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- fformat="csv",
- )
- data = data.reset_index(drop=True)
- print(table)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- self.assertNotEqual(len(os.listdir(defaults.raw_data_cache)), 0)
- print("Passed")
-
- def test_dispatch_tables_start_of_month_feather_format(self):
- start_time = "2018/02/01 00:00:00"
- end_time = "2018/02/01 05:15:00"
- for table in self.table_names:
- print("Testing {} returing values at start of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- cols = [dat_col, self.table_types[table]]
- filter_cols = (self.table_types[table],)
- expected_length = 63
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if table in [
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- ]:
- expected_length = 10
- expected_first_time = "2018/02/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_last_time = "2018/02/01 05:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- if table == "BIDPEROFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- expected_length = 2
- expected_last_time = "2018/02/01 00:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_first_time = "2018/01/31 00:00:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- print(table)
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- fformat="feather",
- )
- data = data.reset_index(drop=True)
- print(table)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- print("Passed")
-
- def test_dispatch_tables_start_of_month_parquet_format(self):
- start_time = "2018/02/01 00:00:00"
- end_time = "2018/02/01 05:15:00"
- for table in self.table_names:
- print("Testing {} returing values at start of month.".format(table))
- dat_col = defaults.primary_date_columns[table]
- table_type = self.table_types[table]
- cols = [dat_col, self.table_types[table]]
- filter_cols = (self.table_types[table],)
- expected_length = 63
- expected_number_of_columns = 2
- expected_first_time = pd.to_datetime(
- start_time, format="%Y/%m/%d %H:%M:%S"
- ) + timedelta(minutes=5)
- expected_last_time = pd.to_datetime(end_time, format="%Y/%m/%d %H:%M:%S")
- if table in [
- "TRADINGLOAD",
- "TRADINGPRICE",
- "TRADINGREGIONSUM",
- "TRADINGINTERCONNECT",
- ]:
- expected_length = 10
- expected_first_time = "2018/02/01 00:30:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_last_time = "2018/02/01 05:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- if table == "BIDPEROFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- if table == "BIDDAYOFFER_D":
- cols = [dat_col, "DUID", "BIDTYPE"]
- filter_cols = ("DUID", "BIDTYPE")
- expected_number_of_columns = 3
- expected_length = 2
- expected_last_time = "2018/02/01 00:00:00"
- expected_last_time = pd.to_datetime(
- expected_last_time, format="%Y/%m/%d %H:%M:%S"
- )
- expected_first_time = "2018/01/31 00:00:00"
- expected_first_time = pd.to_datetime(
- expected_first_time, format="%Y/%m/%d %H:%M:%S"
- )
- data = data_fetch_methods.dynamic_data_compiler(
- start_time,
- end_time,
- table,
- defaults.raw_data_cache,
- select_columns=cols,
- filter_cols=filter_cols,
- filter_values=self.filter_values[table_type],
- fformat="parquet",
- parse_data_types=True,
- )
- data = data.reset_index(drop=True)
- print(table)
- self.assertEqual(expected_length, data.shape[0])
- self.assertEqual(expected_number_of_columns, data.shape[1])
- self.assertEqual(expected_first_time, data[dat_col][0])
- self.assertEqual(expected_last_time, data[dat_col].iloc[-1])
- self.assertFalse(all(object == data.dtypes))
- print("Passed")
diff --git a/tests/test_processing_info_maps.py b/tests/test_processing_info_maps.py
index 6c2e4c1..4294f4a 100644
--- a/tests/test_processing_info_maps.py
+++ b/tests/test_processing_info_maps.py
@@ -1,219 +1,279 @@
-import unittest
+"""Offline validation of `processing_info_maps.search_type` against AEMO's
+actual storage patterns.
+
+For each table classified in `search_type`, the classification determines how
+NEMOSIS iterates monthly archives and whether it deduplicates on read:
+
+ start_to_end each monthly archive covers its own month's rows only —
+ no overlap between months (with narrow exceptions where
+ AEMO publishes a day or two into the next month).
+ Library concatenates directly.
+ all each monthly archive is a cumulative snapshot (for true
+ registration tables like PARTICIPANT) or a change-log
+ (for constraint tables like GENCONDATA). In either case
+ the library deduplicates via `drop_duplicates_by_primary_key`.
+ end the single end-time archive is cumulative back to table
+ origin, so only one fetch is needed.
+
+This file reads raw AEMO CSVs from the fixture zips directly (no
+`dynamic_data_compiler`, no HTTP server) and checks AEMO's stored data
+actually behaves the way each classification assumes. A mis-classified table
+or a wrong `table_primary_keys` entry surfaces here even when no per-table
+test covers the affected table.
+
+Scrape-pattern tables (DAILY_REGION_SUMMARY, NEXT_DAY_DISPATCHLOAD,
+INTERMITTENT_GEN_SCADA) are excluded — their per-day file layout doesn't
+fit the cross-month semantics probed here, and their classification is
+exercised by their per-table end-to-end tests.
+"""
+from __future__ import annotations
+
+import io
+import zipfile
+from pathlib import Path
-from nemosis import processing_info_maps, defaults, query_wrappers, data_fetch_methods
import pandas as pd
-from datetime import datetime, timedelta
+import pytest
+
+from nemosis import defaults
+from nemosis.processing_info_maps import search_type
+from nemosis.query_wrappers import drop_duplicates_by_primary_key
+
+
+FIXTURE_ROOT = Path(__file__).resolve().parent / "fixtures" / "data"
+MMS_DATA_DIR = (
+ "Data_Archive/Wholesale_Electricity/MMSDM/{year}/"
+ "MMSDM_{year}_{month:02d}/MMSDM_Historical_Data_SQLLoader/DATA"
+)
+
+_SCRAPE_TABLES = {"DAILY_REGION_SUMMARY", "NEXT_DAY_DISPATCHLOAD", "INTERMITTENT_GEN_SCADA"}
+_PARKED = {"FCAS_4_SECOND"} # upstream AEMO outage — issue #64
+_NON_MMS = _SCRAPE_TABLES | _PARKED
+
+_TIME_COLS = {"SETTLEMENTDATE", "INTERVAL_DATETIME", "TIMESTAMP", "RUN_DATETIME"}
+
+# `start_to_end` tables whose AEMO archives genuinely overlap at month
+# boundaries (the published archive for month N extends a day or two into
+# month N+1). Excluded from the disjointness check — this is AEMO's format,
+# not a classification bug. NEMOSIS handles the overlap via the SETTLEMENTDATE
+# range filter applied after fetch.
+_ARCHIVES_OVERLAP_INTO_NEXT_MONTH = {"ROOFTOP_PV_ACTUAL"}
+
+
+# ---------------------------------------------------------------------------
+# Fixture zip helpers
+# ---------------------------------------------------------------------------
+
+def _mms_zip_path(table: str, year: int, month: int) -> Path | None:
+ """Find an MMS fixture zip for `table` at `year-month`. Tries both the
+ pre-2024-08 `PUBLIC_DVD_` filename and the post-cutover `PUBLIC_ARCHIVE#`
+ one. Returns None if neither exists."""
+ base = FIXTURE_ROOT / MMS_DATA_DIR.format(year=year, month=month)
+ for name in (
+ f"PUBLIC_DVD_{table}_{year}{month:02d}010000.zip",
+ f"PUBLIC_ARCHIVE#{table}#FILE01#{year}{month:02d}010000.zip",
+ ):
+ if (base / name).exists():
+ return base / name
+ return None
+
+
+def _read_mms_zip(path: Path) -> pd.DataFrame:
+ """Extract the single CSV from an MMS archive zip and return just the
+ D-row content as a DataFrame. AEMO CSVs have `C,...` header metadata,
+ one `I,...` row with column names, and many `D,...` data rows. The
+ first 4 fields of I/D rows are structural (row-type, market section,
+ table name, version) — this helper strips them so the frame has only
+ the actual table columns."""
+ with zipfile.ZipFile(path) as z:
+ csv_name = next(n for n in z.namelist() if n.endswith(".CSV"))
+ content = z.read(csv_name).decode()
+
+ lines = content.splitlines()
+ header = next(l for l in lines if l.startswith("I,"))
+ data_rows = [l for l in lines if l.startswith("D,")]
+ csv_body = header + "\n" + "\n".join(data_rows)
+ df = pd.read_csv(io.StringIO(csv_body))
+ return df.iloc[:, 4:]
+
+
+def _fixtured_months(table: str) -> list[tuple[int, int]]:
+ """Every (year, month) pair for which a fixture zip exists for `table`,
+ sorted chronologically. Used to pick consecutive-month pairs for
+ storage-pattern checks."""
+ months: list[tuple[int, int]] = []
+ mms_root = FIXTURE_ROOT / "Data_Archive/Wholesale_Electricity/MMSDM"
+ if not mms_root.exists():
+ return months
+ for year_dir in sorted(mms_root.iterdir()):
+ for month_dir in sorted(year_dir.iterdir()):
+ parts = month_dir.name.split("_")
+ if len(parts) != 3 or parts[0] != "MMSDM":
+ continue
+ try:
+ year, month = int(parts[1]), int(parts[2])
+ except ValueError:
+ continue
+ if _mms_zip_path(table, year, month) is not None:
+ months.append((year, month))
+ return months
+
+def _consecutive_pair(months: list[tuple[int, int]]) -> tuple | None:
+ """Pick the first consecutive-month pair in `months`, or None if
+ none of the fixtured months are adjacent."""
+ for (y1, m1), (y2, m2) in zip(months, months[1:]):
+ if (y2, m2) == ((y1 + 1, 1) if m1 == 12 else (y1, m1 + 1)):
+ return (y1, m1), (y2, m2)
+ return None
-class TestSearchTypeValidity(unittest.TestCase):
- def setUp(self):
- self.time_yesterday = (datetime.now() - timedelta(days=1)).replace(
- hour=0, minute=0, second=0, microsecond=0
+
+def _shared_pk_columns(table: str, *dfs: pd.DataFrame) -> list[str]:
+ """PK columns present in every supplied frame. Drops any PK column
+ that's missing from older archives (handles AEMO schema drift like
+ BIDPEROFFER_D's `DIRECTION`, added mid-history)."""
+ full_pk = defaults.table_primary_keys[table]
+ return [c for c in full_pk if all(c in df.columns for df in dfs)]
+
+
+# ---------------------------------------------------------------------------
+# Table groupings by classification
+# ---------------------------------------------------------------------------
+
+_START_TO_END_TABLES = sorted(
+ t for t, st in search_type.items()
+ if st == "start_to_end" and t not in _NON_MMS
+)
+_ALL_TABLES = sorted(t for t, st in search_type.items() if st == "all")
+_END_TABLES = sorted(t for t, st in search_type.items() if st == "end")
+
+
+# ---------------------------------------------------------------------------
+# Metadata invariants — classification must be consistent with schema.
+# Pure unit tests: no fixture needed, just library config.
+# ---------------------------------------------------------------------------
+
+@pytest.mark.parametrize("table", _START_TO_END_TABLES)
+def test_start_to_end_table_has_a_time_column(table):
+ """`start_to_end` implies each row has a unique timestamp, so the table
+ must have a SETTLEMENTDATE/INTERVAL_DATETIME/TIMESTAMP/RUN_DATETIME
+ column. Missing it suggests misclassification (should probably be
+ `all` instead)."""
+ cols = set(defaults.table_columns[table])
+ assert cols & _TIME_COLS, (
+ f"{table} classified as start_to_end but has no time column "
+ f"(looked for {_TIME_COLS})"
+ )
+
+
+@pytest.mark.parametrize("table", _ALL_TABLES)
+def test_all_table_has_no_time_column(table):
+ """`all` tables are effective-date config/change-log tables, not time
+ series. A time column here would suggest the table should probably
+ be classified as `start_to_end`."""
+ cols = set(defaults.table_columns[table])
+ leaked = cols & _TIME_COLS
+ assert not leaked, (
+ f"{table} classified as 'all' but has time column(s) {leaked} — "
+ f"may be misclassified (should likely be start_to_end)"
+ )
+
+
+# ---------------------------------------------------------------------------
+# Storage-pattern validation — read raw archives and check AEMO's actual
+# behaviour matches what the classification assumes.
+# ---------------------------------------------------------------------------
+
+@pytest.mark.parametrize("table", _START_TO_END_TABLES)
+def test_start_to_end_archives_are_disjoint_across_months(table):
+ """`start_to_end` tables should have no overlapping rows between
+ consecutive monthly archives — each row lands in exactly one month's
+ file, so concatenation gives zero PK duplicates."""
+ if table in _ARCHIVES_OVERLAP_INTO_NEXT_MONTH:
+ pytest.skip(f"{table}: AEMO archives legitimately overlap at month boundary")
+ months = _fixtured_months(table)
+ pair = _consecutive_pair(months)
+ if pair is None:
+ pytest.skip(
+ f"{table}: fixture lacks a consecutive-month pair "
+ f"(have {[f'{y}-{m:02d}' for y, m in months]})"
)
- pass
-
- def test_start_to_end_no_duplication_between_batches(self):
- for table_name in processing_info_maps.search_type.keys():
- if processing_info_maps.search_type[table_name] == "start_to_end":
- print("Validating start_to_end type for table {}".format(table_name))
- start_time = datetime.strptime(
- "2018/06/01 00:00:00", "%Y/%m/%d %H:%M:%S"
- )
- end_time = datetime.strptime("2018/09/01 00:00:00", "%Y/%m/%d %H:%M:%S")
- if table_name in ["DAILY_REGION_SUMMARY", "NEXT_DAY_DISPATCHLOAD", "INTERMITTENT_GEN_SCADA"]:
- end_time = self.time_yesterday
- start_time = self.time_yesterday - timedelta(days=8)
- if table_name in ["FCAS_4_SECOND"]:
- end_time = self.time_yesterday
- start_time = self.time_yesterday - timedelta(hours=2)
- data_tables = data_fetch_methods._dynamic_data_fetch_loop(
- start_search=start_time,
- start_time=start_time,
- end_time=end_time,
- table_name=table_name,
- raw_data_location=defaults.raw_data_cache,
- select_columns=defaults.table_primary_keys[table_name],
- date_filter=None,
- keep_csv=False,
- )
- all_data = pd.concat(data_tables, sort=False)
- contains_duplicates = all_data.duplicated().any()
- self.assertEqual(
- False, contains_duplicates, "table {}".format(table_name)
- )
- print("Type valid, no duplicates found.")
-
- def test_start_to_end_has_settlement_or_interval_col(self):
- for table_name in processing_info_maps.search_type.keys():
- if processing_info_maps.search_type[table_name] == "start_to_end":
- has_settlement_date_col = (
- "SETTLEMENTDATE" in defaults.table_columns[table_name]
- )
- has_interval_datetime_col = (
- "INTERVAL_DATETIME" in defaults.table_columns[table_name]
- )
- has_interval_timestamp_col = (
- "TIMESTAMP" in defaults.table_columns[table_name]
- )
- has_run_datetime_col = (
- "RUN_DATETIME" in defaults.table_columns[table_name]
- )
- has_either = (
- has_interval_datetime_col
- or has_settlement_date_col
- or has_interval_timestamp_col
- or has_run_datetime_col
- )
- self.assertEqual(True, has_either)
- print(
- "{} is valid candidate for type start_to_end as there is a SETTLEMENTDATE, "
- "INTERVAL_DATETIME or TIMESTAMP column to filter on.".format(
- table_name
- )
- )
-
- def test_all_no_duplication_between_batches(self):
- for table_name in processing_info_maps.search_type.keys():
- if processing_info_maps.search_type[table_name] == "all":
- print("Validating all type for table {}".format(table_name))
- if table_name in [
- "GENCONDATA",
- "SPDCONNECTIONPOINTCONSTRAINT",
- "SPDINTERCONNECTORCONSTRAINT",
- "DUDETAILSUMMARY",
- "LOSSMODEL",
- "LOSSFACTORMODEL",
- "MNSP_DAYOFFER",
- "MNSP_PEROFFER",
- "MNSP_INTERCONNECTOR",
- "INTERCONNECTOR",
- "INTERCONNECTORCONSTRAINT",
- "DUDETAIL",
- "MARKET_PRICE_THRESHOLDS",
- "PARTICIPANT"
- ]:
- print(
- "{} is known to contain duplicate entries and is exempted from this test, a finalise "
- "data processing step is included in dynamic data fetch to clean up these duplicates.".format(
- table_name
- )
- )
- continue
- start_test_window = defaults.nem_data_model_start_time
- start_time = datetime.strptime(start_test_window, "%Y/%m/%d %H:%M:%S")
- end_time = datetime.strptime("2018/01/01 00:00:00", "%Y/%m/%d %H:%M:%S")
- start_search = datetime.strptime(start_test_window, "%Y/%m/%d %H:%M:%S")
- data_tables = data_fetch_methods._dynamic_data_fetch_loop(
- start_search=start_search,
- start_time=start_time,
- end_time=end_time,
- table_name=table_name,
- raw_data_location=defaults.raw_data_cache,
- select_columns=defaults.table_primary_keys[table_name],
- date_filter=None,
- keep_csv=False,
- )
- all_data = pd.concat(data_tables, sort=False)
- contains_duplicates = all_data.duplicated().any()
- self.assertEqual(
- False, contains_duplicates, "table {}".format(table_name)
- )
- print("Type valid, no duplicates found.")
-
- def test_all_no_duplication_between_batches_with_finalise_step(self):
- for table_name in processing_info_maps.search_type.keys():
- if processing_info_maps.search_type[table_name] == "all":
- print("Testing duplicate removal for table {}".format(table_name))
- start_test_window = defaults.nem_data_model_start_time
- # start_test_window = '2018/01/01 00:00:00'
- start_time = datetime.strptime(start_test_window, "%Y/%m/%d %H:%M:%S")
- end_time = datetime.strptime("2018/01/01 00:00:00", "%Y/%m/%d %H:%M:%S")
- start_search = datetime.strptime(start_test_window, "%Y/%m/%d %H:%M:%S")
- data_tables = data_fetch_methods._dynamic_data_fetch_loop(
- start_search=start_search,
- start_time=start_time,
- end_time=end_time,
- table_name=table_name,
- raw_data_location=defaults.raw_data_cache,
- select_columns=defaults.table_primary_keys[table_name],
- date_filter=None,
- keep_csv=False,
- )
- all_data = pd.concat(data_tables, sort=False)
- all_data = query_wrappers.drop_duplicates_by_primary_key(
- all_data, start_time, table_name
- )
- contains_duplicates = all_data.duplicated().any()
- self.assertEqual(False, contains_duplicates)
- print("Type valid, no duplicates found.")
-
- def test_start_to_end_has_no_settlement_interval_or_timestamp_col(self):
- for table_name in processing_info_maps.search_type.keys():
- if processing_info_maps.search_type[table_name] == "all":
- has_settlement_date_col = (
- "SETTLEMENTDATE" in defaults.table_columns[table_name]
- )
- has_interval_datetime_col = (
- "INTERVAL_DATETIME" in defaults.table_columns[table_name]
- )
- has_interval_timestamp_col = (
- "TIMESTAMP" in defaults.table_columns[table_name]
- )
- has_either = (
- has_interval_datetime_col
- or has_settlement_date_col
- or has_interval_timestamp_col
- )
- self.assertEqual(False, has_either, "table {}".format(table_name))
- print(
- "{} is valid candidate for type all as there is not a SETTLEMENTDATE, "
- "INTERVAL_DATETIME or TIMESTAMP column to filter on".format(
- table_name
- )
- )
-
- def test_last_contains_data_from_first(self):
- for table_name in processing_info_maps.search_type.keys():
- if processing_info_maps.search_type[table_name] == "end":
- start_test_window = defaults.nem_data_model_start_time
- # start_test_window = '2018/01/01 00:00:00'
- start_time = datetime.strptime(start_test_window, "%Y/%m/%d %H:%M:%S")
- end_time = datetime.strptime("2018/01/01 00:00:00", "%Y/%m/%d %H:%M:%S")
- start_search = datetime.strptime(start_test_window, "%Y/%m/%d %H:%M:%S")
- select_columns = None
- (
- _,
- _,
- select_columns,
- _,
- _,
- ) = data_fetch_methods._set_up_dynamic_compilers(
- table_name, start_time, end_time, select_columns
- )
- data_tables = data_fetch_methods._dynamic_data_fetch_loop(
- start_search=start_search,
- start_time=start_time,
- end_time=end_time,
- table_name=table_name,
- raw_data_location=defaults.raw_data_cache,
- select_columns=select_columns,
- date_filter=None,
- keep_csv=False,
- )
- first_data_table = data_tables[35].loc[
- :, defaults.table_primary_keys[table_name]
- ]
- last_data_table = data_tables[-1]
- comp = pd.merge(
- first_data_table,
- last_data_table,
- "left",
- defaults.table_primary_keys[table_name],
- )
- non_primary_col = [
- col
- for col in defaults.table_columns[table_name]
- if col not in defaults.table_primary_keys[table_name]
- ][0]
- missing_from_last = comp[comp[non_primary_col].isnull()]
- self.assertEqual(False, missing_from_last.empty)
+ (y1, m1), (y2, m2) = pair
+ df1 = _read_mms_zip(_mms_zip_path(table, y1, m1))
+ df2 = _read_mms_zip(_mms_zip_path(table, y2, m2))
+ pk = _shared_pk_columns(table, df1, df2)
+ assert pk, f"{table}: no PK columns shared between {y1}-{m1:02d} and {y2}-{m2:02d} frames"
+
+ combined = pd.concat([df1, df2])
+ dupes = combined.duplicated(pk).sum()
+ assert dupes == 0, (
+ f"{table}: concat of {y1}-{m1:02d} and {y2}-{m2:02d} has "
+ f"{dupes} PK duplicates on {pk} — either archives aren't "
+ f"disjoint (should be 'all'?) or the primary-key config is wrong"
+ )
+
+
+@pytest.mark.parametrize("table", _ALL_TABLES)
+def test_all_dedup_collapses_cleanly(table):
+ """The core invariant for `all`-type tables: after fetching multiple
+ monthly archives and running `drop_duplicates_by_primary_key`, the
+ result must have no PK duplicates. This catches wrong `table_primary_keys`
+ configs (where the PK isn't actually unique) and classification
+ mismatches (where the dedup step fails to converge). Covers both
+ true snapshot tables like PARTICIPANT (where archives overlap heavily)
+ and change-log tables like GENCONDATA (where archives don't overlap
+ much but dedup still needs to produce clean output)."""
+ months = _fixtured_months(table)
+ if len(months) < 2:
+ pytest.skip(f"{table}: need 2 fixtured months")
+ (y1, m1), (y2, m2) = months[0], months[-1]
+ df1 = _read_mms_zip(_mms_zip_path(table, y1, m1))
+ df2 = _read_mms_zip(_mms_zip_path(table, y2, m2))
+ combined = pd.concat([df1, df2])
+ pk = _shared_pk_columns(table, df1, df2)
+ assert pk, f"{table}: no PK columns shared between {y1}-{m1:02d} and {y2}-{m2:02d} frames"
+
+ start_time = pd.Timestamp(f"{y1}-{m1:02d}-01")
+ deduped = drop_duplicates_by_primary_key(combined, start_time, table)
+ assert not deduped.duplicated(pk).any(), (
+ f"{table}: drop_duplicates_by_primary_key left PK duplicates on {pk}"
+ )
+
+
+def test_participant_all_archives_do_overlap():
+ """Canary for the `all`-type snapshot pattern. PARTICIPANT is a true
+ registration list — every registered participant appears in every
+ monthly archive with identical content. If concat shows zero PK
+ overlap across two distant months, either AEMO stopped publishing
+ snapshots or the PK config is wrong. We probe PARTICIPANT specifically
+ because other `all` tables are change-logs that don't overlap much
+ over a 2-month window."""
+ df1 = _read_mms_zip(_mms_zip_path("PARTICIPANT", 2018, 5))
+ df2 = _read_mms_zip(_mms_zip_path("PARTICIPANT", 2021, 5))
+ pk = _shared_pk_columns("PARTICIPANT", df1, df2)
+ combined = pd.concat([df1, df2])
+ dupes = combined.duplicated(pk).sum()
+ assert dupes > 0, (
+ f"PARTICIPANT: concat of 2018-05 and 2021-05 has 0 PK duplicates on "
+ f"{pk} — snapshot pattern no longer holds, library's `all` assumption "
+ f"is wrong, or the PK definition is off"
+ )
+
+
+@pytest.mark.parametrize("table", _END_TABLES)
+def test_end_archive_contains_historical_rows(table):
+ """`end` tables are classified such that only the end-time archive is
+ fetched. That only works if each archive is cumulative back to the
+ table's origin. Probe: the 2021-05 archive should contain rows with
+ effective dates well earlier than the archive month itself."""
+ zip_path = _mms_zip_path(table, 2021, 5)
+ if zip_path is None:
+ pytest.skip(f"{table}: no 2021-05 fixture")
+ df = _read_mms_zip(zip_path)
+ date_col = defaults.primary_date_columns[table]
+ df[date_col] = pd.to_datetime(df[date_col], format="%Y/%m/%d %H:%M:%S")
+ min_date = df[date_col].min()
+ assert min_date < pd.Timestamp("2020-01-01"), (
+ f"{table} 2021-05 archive min {date_col}={min_date} — expected "
+ f"historical rows from well before 2020 (cumulative semantics)"
+ )
\ No newline at end of file
diff --git a/uv.lock b/uv.lock
index c26c9d0..d929b62 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,18 +1,19 @@
version = 1
-requires-python = ">=3.9"
+revision = 3
+requires-python = ">=3.10"
resolution-markers = [
- "python_full_version < '3.11'",
- "python_full_version == '3.11.*'",
"python_full_version >= '3.12'",
+ "python_full_version == '3.11.*'",
+ "python_full_version < '3.11'",
]
[[package]]
name = "altgraph"
version = "0.17.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418 }
+sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418, upload-time = "2023-09-25T09:04:52.164Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212 },
+ { url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212, upload-time = "2023-09-25T09:04:50.691Z" },
]
[[package]]
@@ -22,119 +23,106 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "soupsieve" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181, upload-time = "2024-01-17T16:53:17.902Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 },
+ { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925, upload-time = "2024-01-17T16:53:12.779Z" },
]
[[package]]
name = "certifi"
version = "2024.12.14"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 }
+sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010, upload-time = "2024-12-14T13:52:38.02Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 },
+ { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927, upload-time = "2024-12-14T13:52:36.114Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 },
- { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 },
- { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 },
- { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 },
- { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 },
- { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 },
- { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 },
- { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 },
- { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 },
- { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 },
- { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 },
- { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 },
- { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 },
- { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 },
- { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 },
- { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 },
- { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 },
- { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 },
- { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 },
- { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 },
- { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 },
- { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 },
- { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 },
- { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 },
- { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 },
- { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 },
- { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
- { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
- { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
- { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
- { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
- { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
- { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
- { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
- { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
- { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
- { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
- { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
- { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
- { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
- { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
- { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
- { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
- { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
- { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
- { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
- { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
- { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
- { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
- { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
- { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
- { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
- { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 },
- { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 },
- { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 },
- { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 },
- { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 },
- { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 },
- { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 },
- { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 },
- { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 },
- { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 },
- { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 },
- { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 },
- { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 },
- { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
+sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013, upload-time = "2024-12-24T18:09:43.671Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285, upload-time = "2024-12-24T18:09:48.113Z" },
+ { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449, upload-time = "2024-12-24T18:09:50.845Z" },
+ { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892, upload-time = "2024-12-24T18:09:52.078Z" },
+ { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123, upload-time = "2024-12-24T18:09:54.575Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943, upload-time = "2024-12-24T18:09:57.324Z" },
+ { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063, upload-time = "2024-12-24T18:09:59.794Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578, upload-time = "2024-12-24T18:10:02.357Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629, upload-time = "2024-12-24T18:10:03.678Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778, upload-time = "2024-12-24T18:10:06.197Z" },
+ { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453, upload-time = "2024-12-24T18:10:08.848Z" },
+ { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479, upload-time = "2024-12-24T18:10:10.044Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790, upload-time = "2024-12-24T18:10:11.323Z" },
+ { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995, upload-time = "2024-12-24T18:10:12.838Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471, upload-time = "2024-12-24T18:10:14.101Z" },
+ { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831, upload-time = "2024-12-24T18:10:15.512Z" },
+ { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335, upload-time = "2024-12-24T18:10:18.369Z" },
+ { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862, upload-time = "2024-12-24T18:10:19.743Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673, upload-time = "2024-12-24T18:10:21.139Z" },
+ { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211, upload-time = "2024-12-24T18:10:22.382Z" },
+ { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039, upload-time = "2024-12-24T18:10:24.802Z" },
+ { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939, upload-time = "2024-12-24T18:10:26.124Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075, upload-time = "2024-12-24T18:10:30.027Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340, upload-time = "2024-12-24T18:10:32.679Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205, upload-time = "2024-12-24T18:10:34.724Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441, upload-time = "2024-12-24T18:10:37.574Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" },
+ { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" },
+ { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" },
+ { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" },
+ { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" },
+ { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" },
+ { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" },
+ { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" },
+ { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" },
+ { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" },
+ { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" },
+ { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "et-xmlfile"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234 }
+sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059 },
+ { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" },
]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
+sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883, upload-time = "2024-07-12T22:26:00.161Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
+ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" },
]
[[package]]
@@ -144,36 +132,24 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyarrow" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/67/e8/ee99f142f19d35588501943510f8217f9dd77184574b0c933c53218e0f19/feather-format-0.4.1.tar.gz", hash = "sha256:45f67e3745d394d4f160ca6d636bbfd4f8b68d01199dc1649b6e487d3e878903", size = 3151 }
+sdist = { url = "https://files.pythonhosted.org/packages/67/e8/ee99f142f19d35588501943510f8217f9dd77184574b0c933c53218e0f19/feather-format-0.4.1.tar.gz", hash = "sha256:45f67e3745d394d4f160ca6d636bbfd4f8b68d01199dc1649b6e487d3e878903", size = 3151, upload-time = "2020-04-27T21:56:00.48Z" }
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
+sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "8.6.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "zipp", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 },
+ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" },
]
[[package]]
@@ -183,14 +159,14 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309 }
+sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094 },
+ { url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" },
]
[[package]]
name = "nemosis"
-version = "4.0.0"
+version = "3.8.1"
source = { editable = "." }
dependencies = [
{ name = "beautifulsoup4" },
@@ -229,54 +205,150 @@ dev = [
[[package]]
name = "numpy"
-version = "2.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245 },
- { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540 },
- { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623 },
- { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774 },
- { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081 },
- { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451 },
- { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572 },
- { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722 },
- { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170 },
- { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558 },
- { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137 },
- { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552 },
- { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957 },
- { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573 },
- { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330 },
- { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895 },
- { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253 },
- { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074 },
- { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640 },
- { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230 },
- { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803 },
- { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835 },
- { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499 },
- { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497 },
- { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158 },
- { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173 },
- { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174 },
- { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701 },
- { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313 },
- { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179 },
- { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942 },
- { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512 },
- { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976 },
- { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494 },
- { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596 },
- { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099 },
- { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823 },
- { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424 },
- { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809 },
- { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314 },
- { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288 },
- { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793 },
- { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885 },
- { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784 },
+version = "2.2.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
+ { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
+ { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
+ { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
+ { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
+ { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
+ { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
+ { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
+ { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
+ { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
+ { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
+ { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
+ { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
+ { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
+ { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
+ { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
+ { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
+ { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
+ { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
+ { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
+ { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.4.4"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.12'",
+ "python_full_version == '3.11.*'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" },
+ { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" },
+ { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" },
+ { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" },
+ { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" },
+ { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" },
+ { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" },
+ { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" },
+ { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" },
+ { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" },
+ { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" },
+ { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" },
+ { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" },
+ { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" },
+ { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" },
+ { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" },
+ { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" },
+ { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" },
+ { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" },
+ { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" },
+ { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" },
+ { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" },
+ { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" },
+ { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" },
+ { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" },
]
[[package]]
@@ -286,18 +358,18 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "et-xmlfile" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 }
+sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 },
+ { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950, upload-time = "2024-11-08T09:47:47.202Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
+ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" },
]
[[package]]
@@ -305,130 +377,117 @@ name = "pandas"
version = "2.2.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "numpy" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "python-dateutil" },
{ name = "pytz" },
{ name = "tzdata" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827 },
- { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897 },
- { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908 },
- { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210 },
- { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292 },
- { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379 },
- { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471 },
- { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222 },
- { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274 },
- { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836 },
- { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505 },
- { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420 },
- { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457 },
- { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166 },
- { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 },
- { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 },
- { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 },
- { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 },
- { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 },
- { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 },
- { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 },
- { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 },
- { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 },
- { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 },
- { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 },
- { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 },
- { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 },
- { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 },
- { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 },
- { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 },
- { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 },
- { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 },
- { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 },
- { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 },
- { url = "https://files.pythonhosted.org/packages/ca/8c/8848a4c9b8fdf5a534fe2077af948bf53cd713d77ffbcd7bd15710348fd7/pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39", size = 12595535 },
- { url = "https://files.pythonhosted.org/packages/9c/b9/5cead4f63b6d31bdefeb21a679bc5a7f4aaf262ca7e07e2bc1c341b68470/pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30", size = 11319822 },
- { url = "https://files.pythonhosted.org/packages/31/af/89e35619fb573366fa68dc26dad6ad2c08c17b8004aad6d98f1a31ce4bb3/pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c", size = 15625439 },
- { url = "https://files.pythonhosted.org/packages/3d/dd/bed19c2974296661493d7acc4407b1d2db4e2a482197df100f8f965b6225/pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c", size = 13068928 },
- { url = "https://files.pythonhosted.org/packages/31/a3/18508e10a31ea108d746c848b5a05c0711e0278fa0d6f1c52a8ec52b80a5/pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea", size = 16783266 },
- { url = "https://files.pythonhosted.org/packages/c4/a5/3429bd13d82bebc78f4d78c3945efedef63a7cd0c15c17b2eeb838d1121f/pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761", size = 14450871 },
- { url = "https://files.pythonhosted.org/packages/2f/49/5c30646e96c684570925b772eac4eb0a8cb0ca590fa978f56c5d3ae73ea1/pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e", size = 11618011 },
+sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213, upload-time = "2024-09-20T13:10:04.827Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/70/c853aec59839bceed032d52010ff5f1b8d87dc3114b762e4ba2727661a3b/pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", size = 12580827, upload-time = "2024-09-20T13:08:42.347Z" },
+ { url = "https://files.pythonhosted.org/packages/99/f2/c4527768739ffa4469b2b4fff05aa3768a478aed89a2f271a79a40eee984/pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", size = 11303897, upload-time = "2024-09-20T13:08:45.807Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/12/86c1747ea27989d7a4064f806ce2bae2c6d575b950be087837bdfcabacc9/pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", size = 66480908, upload-time = "2024-09-20T18:37:13.513Z" },
+ { url = "https://files.pythonhosted.org/packages/44/50/7db2cd5e6373ae796f0ddad3675268c8d59fb6076e66f0c339d61cea886b/pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", size = 13064210, upload-time = "2024-09-20T13:08:48.325Z" },
+ { url = "https://files.pythonhosted.org/packages/61/61/a89015a6d5536cb0d6c3ba02cebed51a95538cf83472975275e28ebf7d0c/pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", size = 16754292, upload-time = "2024-09-20T19:01:54.443Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/0d/4cc7b69ce37fac07645a94e1d4b0880b15999494372c1523508511b09e40/pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", size = 14416379, upload-time = "2024-09-20T13:08:50.882Z" },
+ { url = "https://files.pythonhosted.org/packages/31/9e/6ebb433de864a6cd45716af52a4d7a8c3c9aaf3a98368e61db9e69e69a9c/pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", size = 11598471, upload-time = "2024-09-20T13:08:53.332Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/44/d9502bf0ed197ba9bf1103c9867d5904ddcaf869e52329787fc54ed70cc8/pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", size = 12602222, upload-time = "2024-09-20T13:08:56.254Z" },
+ { url = "https://files.pythonhosted.org/packages/52/11/9eac327a38834f162b8250aab32a6781339c69afe7574368fffe46387edf/pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", size = 11321274, upload-time = "2024-09-20T13:08:58.645Z" },
+ { url = "https://files.pythonhosted.org/packages/45/fb/c4beeb084718598ba19aa9f5abbc8aed8b42f90930da861fcb1acdb54c3a/pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", size = 15579836, upload-time = "2024-09-20T19:01:57.571Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", size = 13058505, upload-time = "2024-09-20T13:09:01.501Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/57/708135b90391995361636634df1f1130d03ba456e95bcf576fada459115a/pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", size = 16744420, upload-time = "2024-09-20T19:02:00.678Z" },
+ { url = "https://files.pythonhosted.org/packages/86/4a/03ed6b7ee323cf30404265c284cee9c65c56a212e0a08d9ee06984ba2240/pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", size = 14440457, upload-time = "2024-09-20T13:09:04.105Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/8c/87ddf1fcb55d11f9f847e3c69bb1c6f8e46e2f40ab1a2d2abadb2401b007/pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", size = 11617166, upload-time = "2024-09-20T13:09:06.917Z" },
+ { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893, upload-time = "2024-09-20T13:09:09.655Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475, upload-time = "2024-09-20T13:09:14.718Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645, upload-time = "2024-09-20T19:02:03.88Z" },
+ { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445, upload-time = "2024-09-20T13:09:17.621Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235, upload-time = "2024-09-20T19:02:07.094Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756, upload-time = "2024-09-20T13:09:20.474Z" },
+ { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248, upload-time = "2024-09-20T13:09:23.137Z" },
+ { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643, upload-time = "2024-09-20T13:09:25.522Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573, upload-time = "2024-09-20T13:09:28.012Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085, upload-time = "2024-09-20T19:02:10.451Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809, upload-time = "2024-09-20T13:09:30.814Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316, upload-time = "2024-09-20T19:02:13.825Z" },
+ { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055, upload-time = "2024-09-20T13:09:33.462Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175, upload-time = "2024-09-20T13:09:35.871Z" },
+ { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650, upload-time = "2024-09-20T13:09:38.685Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177, upload-time = "2024-09-20T13:09:41.141Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526, upload-time = "2024-09-20T19:02:16.905Z" },
+ { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013, upload-time = "2024-09-20T13:09:44.39Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620, upload-time = "2024-09-20T19:02:20.639Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436, upload-time = "2024-09-20T13:09:48.112Z" },
]
[[package]]
name = "parameterized"
version = "0.9.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1", size = 24351 }
+sdist = { url = "https://files.pythonhosted.org/packages/ea/49/00c0c0cc24ff4266025a53e41336b79adaa5a4ebfad214f433d623f9865e/parameterized-0.9.0.tar.gz", hash = "sha256:7fc905272cefa4f364c1a3429cbbe9c0f98b793988efb5bf90aac80f08db09b1", size = 24351, upload-time = "2023-03-27T02:01:11.592Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475 },
+ { url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475, upload-time = "2023-03-27T02:01:09.31Z" },
]
[[package]]
name = "pefile"
version = "2023.2.7"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854 }
+sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791 },
+ { url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" },
]
[[package]]
name = "pluggy"
version = "1.5.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
+sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
+ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" },
]
[[package]]
name = "pyarrow"
version = "18.1.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7f/7b/640785a9062bb00314caa8a387abce547d2a420cf09bd6c715fe659ccffb/pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73", size = 1118671 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1a/bb/8d4a1573f66e0684f190dd2b55fd0b97a7214de8882d58a3867e777bf640/pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c", size = 29531620 },
- { url = "https://files.pythonhosted.org/packages/30/90/893acfad917533b624a97b9e498c0e8393908508a0a72d624fe935e632bf/pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4", size = 30836521 },
- { url = "https://files.pythonhosted.org/packages/a3/2a/526545a7464b5fb2fa6e2c4bad16ca90e59e1843025c534fd907b7f73e5a/pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b", size = 39213905 },
- { url = "https://files.pythonhosted.org/packages/8a/77/4b3fab91a30e19e233e738d0c5eca5a8f6dd05758bc349a2ca262c65de79/pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71", size = 40128881 },
- { url = "https://files.pythonhosted.org/packages/aa/e2/a88e16c5e45e562449c52305bd3bc2f9d704295322d3434656e7ccac1444/pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470", size = 38627517 },
- { url = "https://files.pythonhosted.org/packages/6d/84/8037c20005ccc7b869726465be0957bd9c29cfc88612962030f08292ad06/pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56", size = 40060187 },
- { url = "https://files.pythonhosted.org/packages/2a/38/d6435c723ff73df8ae74626ea778262fbcc2b9b0d1a4f3db915b61711b05/pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812", size = 25118314 },
- { url = "https://files.pythonhosted.org/packages/9e/4d/a4988e7d82f4fbc797715db4185939a658eeffb07a25bab7262bed1ea076/pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854", size = 29554860 },
- { url = "https://files.pythonhosted.org/packages/59/03/3a42c5c1e4bd4c900ab62aa1ff6b472bdb159ba8f1c3e5deadab7222244f/pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c", size = 30867076 },
- { url = "https://files.pythonhosted.org/packages/75/7e/332055ac913373e89256dce9d14b7708f55f7bd5be631456c897f0237738/pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21", size = 39212135 },
- { url = "https://files.pythonhosted.org/packages/8c/64/5099cdb325828722ef7ffeba9a4696f238eb0cdeae227f831c2d77fcf1bd/pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6", size = 40125195 },
- { url = "https://files.pythonhosted.org/packages/83/88/1938d783727db1b178ff71bc6a6143d7939e406db83a9ec23cad3dad325c/pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe", size = 38641884 },
- { url = "https://files.pythonhosted.org/packages/5e/b5/9e14e9f7590e0eaa435ecea84dabb137284a4dbba7b3c337b58b65b76d95/pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0", size = 40076877 },
- { url = "https://files.pythonhosted.org/packages/4d/a3/817ac7fe0891a2d66e247e223080f3a6a262d8aefd77e11e8c27e6acf4e1/pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a", size = 25119811 },
- { url = "https://files.pythonhosted.org/packages/6a/50/12829e7111b932581e51dda51d5cb39207a056c30fe31ef43f14c63c4d7e/pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d", size = 29514620 },
- { url = "https://files.pythonhosted.org/packages/d1/41/468c944eab157702e96abab3d07b48b8424927d4933541ab43788bb6964d/pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee", size = 30856494 },
- { url = "https://files.pythonhosted.org/packages/68/f9/29fb659b390312a7345aeb858a9d9c157552a8852522f2c8bad437c29c0a/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992", size = 39203624 },
- { url = "https://files.pythonhosted.org/packages/6e/f6/19360dae44200e35753c5c2889dc478154cd78e61b1f738514c9f131734d/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54", size = 40139341 },
- { url = "https://files.pythonhosted.org/packages/bb/e6/9b3afbbcf10cc724312e824af94a2e993d8ace22994d823f5c35324cebf5/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33", size = 38618629 },
- { url = "https://files.pythonhosted.org/packages/3a/2e/3b99f8a3d9e0ccae0e961978a0d0089b25fb46ebbcfb5ebae3cca179a5b3/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30", size = 40078661 },
- { url = "https://files.pythonhosted.org/packages/76/52/f8da04195000099d394012b8d42c503d7041b79f778d854f410e5f05049a/pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99", size = 25092330 },
- { url = "https://files.pythonhosted.org/packages/cb/87/aa4d249732edef6ad88899399047d7e49311a55749d3c373007d034ee471/pyarrow-18.1.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b", size = 29497406 },
- { url = "https://files.pythonhosted.org/packages/3c/c7/ed6adb46d93a3177540e228b5ca30d99fc8ea3b13bdb88b6f8b6467e2cb7/pyarrow-18.1.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2", size = 30835095 },
- { url = "https://files.pythonhosted.org/packages/41/d7/ed85001edfb96200ff606943cff71d64f91926ab42828676c0fc0db98963/pyarrow-18.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191", size = 39194527 },
- { url = "https://files.pythonhosted.org/packages/59/16/35e28eab126342fa391593415d79477e89582de411bb95232f28b131a769/pyarrow-18.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa", size = 40131443 },
- { url = "https://files.pythonhosted.org/packages/0c/95/e855880614c8da20f4cd74fa85d7268c725cf0013dc754048593a38896a0/pyarrow-18.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c", size = 38608750 },
- { url = "https://files.pythonhosted.org/packages/54/9d/f253554b1457d4fdb3831b7bd5f8f00f1795585a606eabf6fec0a58a9c38/pyarrow-18.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c", size = 40066690 },
- { url = "https://files.pythonhosted.org/packages/2f/58/8912a2563e6b8273e8aa7b605a345bba5a06204549826f6493065575ebc0/pyarrow-18.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181", size = 25081054 },
- { url = "https://files.pythonhosted.org/packages/82/f9/d06ddc06cab1ada0c2f2fd205ac8c25c2701182de1b9c4bf7a0a44844431/pyarrow-18.1.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc", size = 29525542 },
- { url = "https://files.pythonhosted.org/packages/ab/94/8917e3b961810587ecbdaa417f8ebac0abb25105ae667b7aa11c05876976/pyarrow-18.1.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386", size = 30829412 },
- { url = "https://files.pythonhosted.org/packages/5e/e3/3b16c3190f3d71d3b10f6758d2d5f7779ef008c4fd367cedab3ed178a9f7/pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324", size = 39119106 },
- { url = "https://files.pythonhosted.org/packages/1d/d6/5d704b0d25c3c79532f8c0639f253ec2803b897100f64bcb3f53ced236e5/pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8", size = 40090940 },
- { url = "https://files.pythonhosted.org/packages/37/29/366bc7e588220d74ec00e497ac6710c2833c9176f0372fe0286929b2d64c/pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9", size = 38548177 },
- { url = "https://files.pythonhosted.org/packages/c8/11/fabf6ecabb1fe5b7d96889228ca2a9158c4c3bb732e3b8ee3f7f6d40b703/pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba", size = 40043567 },
- { url = "https://files.pythonhosted.org/packages/fd/9b/60516e3876ec6f25b0909afa70f90a15de83b48c7c0d8042fac4e64c4411/pyarrow-18.1.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:0b331e477e40f07238adc7ba7469c36b908f07c89b95dd4bd3a0ec84a3d1e21e", size = 29543752 },
- { url = "https://files.pythonhosted.org/packages/14/a7/bd08b6f1a2bd2e71dc6bb0451fc1872607e44c83daf1ee63c82764a2d233/pyarrow-18.1.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:2c4dd0c9010a25ba03e198fe743b1cc03cd33c08190afff371749c52ccbbaf76", size = 30850753 },
- { url = "https://files.pythonhosted.org/packages/84/c9/62ef9c6281c0e5b4ee1afa9d7bd556e72e06da6706b7906c32c15e69b3d6/pyarrow-18.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f97b31b4c4e21ff58c6f330235ff893cc81e23da081b1a4b1c982075e0ed4e9", size = 39226870 },
- { url = "https://files.pythonhosted.org/packages/b2/99/a6e89e71655a38475e76b060777c8bf69c078b772bec3b7daf7361440f05/pyarrow-18.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a4813cb8ecf1809871fd2d64a8eff740a1bd3691bbe55f01a3cf6c5ec869754", size = 40139114 },
- { url = "https://files.pythonhosted.org/packages/64/a9/06d79923890682e4fe7a16524abee307407008a413115354aaf3226b8410/pyarrow-18.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:05a5636ec3eb5cc2a36c6edb534a38ef57b2ab127292a716d00eabb887835f1e", size = 38639231 },
- { url = "https://files.pythonhosted.org/packages/3b/8c/4c3ed19026a00740b81fe1c87f3ff235b2763a0a1ddf5711a9d026b775ce/pyarrow-18.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:73eeed32e724ea3568bb06161cad5fa7751e45bc2228e33dcb10c614044165c7", size = 40070949 },
- { url = "https://files.pythonhosted.org/packages/87/d8/94161a7ca5c55199484e926165e9e33f318ea1d1b0d7cdbcbc3652b933ec/pyarrow-18.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a1880dd6772b685e803011a6b43a230c23b566859a6e0c9a276c1e0faf4f4052", size = 25301373 },
+sdist = { url = "https://files.pythonhosted.org/packages/7f/7b/640785a9062bb00314caa8a387abce547d2a420cf09bd6c715fe659ccffb/pyarrow-18.1.0.tar.gz", hash = "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73", size = 1118671, upload-time = "2024-11-26T02:01:48.62Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/bb/8d4a1573f66e0684f190dd2b55fd0b97a7214de8882d58a3867e777bf640/pyarrow-18.1.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c", size = 29531620, upload-time = "2024-11-26T01:58:27.03Z" },
+ { url = "https://files.pythonhosted.org/packages/30/90/893acfad917533b624a97b9e498c0e8393908508a0a72d624fe935e632bf/pyarrow-18.1.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4", size = 30836521, upload-time = "2024-11-26T01:58:34.607Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2a/526545a7464b5fb2fa6e2c4bad16ca90e59e1843025c534fd907b7f73e5a/pyarrow-18.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b", size = 39213905, upload-time = "2024-11-26T01:58:40.558Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/77/4b3fab91a30e19e233e738d0c5eca5a8f6dd05758bc349a2ca262c65de79/pyarrow-18.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71", size = 40128881, upload-time = "2024-11-26T01:58:45.561Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/e2/a88e16c5e45e562449c52305bd3bc2f9d704295322d3434656e7ccac1444/pyarrow-18.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470", size = 38627517, upload-time = "2024-11-26T01:58:50.922Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/84/8037c20005ccc7b869726465be0957bd9c29cfc88612962030f08292ad06/pyarrow-18.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56", size = 40060187, upload-time = "2024-11-26T01:58:56.848Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/38/d6435c723ff73df8ae74626ea778262fbcc2b9b0d1a4f3db915b61711b05/pyarrow-18.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812", size = 25118314, upload-time = "2024-11-26T01:59:02.303Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/4d/a4988e7d82f4fbc797715db4185939a658eeffb07a25bab7262bed1ea076/pyarrow-18.1.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854", size = 29554860, upload-time = "2024-11-26T01:59:06.94Z" },
+ { url = "https://files.pythonhosted.org/packages/59/03/3a42c5c1e4bd4c900ab62aa1ff6b472bdb159ba8f1c3e5deadab7222244f/pyarrow-18.1.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c", size = 30867076, upload-time = "2024-11-26T01:59:11.475Z" },
+ { url = "https://files.pythonhosted.org/packages/75/7e/332055ac913373e89256dce9d14b7708f55f7bd5be631456c897f0237738/pyarrow-18.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21", size = 39212135, upload-time = "2024-11-26T01:59:16.045Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/64/5099cdb325828722ef7ffeba9a4696f238eb0cdeae227f831c2d77fcf1bd/pyarrow-18.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6", size = 40125195, upload-time = "2024-11-26T01:59:21.267Z" },
+ { url = "https://files.pythonhosted.org/packages/83/88/1938d783727db1b178ff71bc6a6143d7939e406db83a9ec23cad3dad325c/pyarrow-18.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe", size = 38641884, upload-time = "2024-11-26T01:59:26.672Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/b5/9e14e9f7590e0eaa435ecea84dabb137284a4dbba7b3c337b58b65b76d95/pyarrow-18.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0", size = 40076877, upload-time = "2024-11-26T01:59:31.926Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/a3/817ac7fe0891a2d66e247e223080f3a6a262d8aefd77e11e8c27e6acf4e1/pyarrow-18.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a", size = 25119811, upload-time = "2024-11-26T01:59:35.669Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/50/12829e7111b932581e51dda51d5cb39207a056c30fe31ef43f14c63c4d7e/pyarrow-18.1.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d", size = 29514620, upload-time = "2024-11-26T01:59:39.797Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/41/468c944eab157702e96abab3d07b48b8424927d4933541ab43788bb6964d/pyarrow-18.1.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee", size = 30856494, upload-time = "2024-11-26T01:59:44.725Z" },
+ { url = "https://files.pythonhosted.org/packages/68/f9/29fb659b390312a7345aeb858a9d9c157552a8852522f2c8bad437c29c0a/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992", size = 39203624, upload-time = "2024-11-26T01:59:49.189Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/f6/19360dae44200e35753c5c2889dc478154cd78e61b1f738514c9f131734d/pyarrow-18.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54", size = 40139341, upload-time = "2024-11-26T01:59:54.849Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/e6/9b3afbbcf10cc724312e824af94a2e993d8ace22994d823f5c35324cebf5/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33", size = 38618629, upload-time = "2024-11-26T01:59:59.966Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/2e/3b99f8a3d9e0ccae0e961978a0d0089b25fb46ebbcfb5ebae3cca179a5b3/pyarrow-18.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30", size = 40078661, upload-time = "2024-11-26T02:00:04.55Z" },
+ { url = "https://files.pythonhosted.org/packages/76/52/f8da04195000099d394012b8d42c503d7041b79f778d854f410e5f05049a/pyarrow-18.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99", size = 25092330, upload-time = "2024-11-26T02:00:09.576Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/87/aa4d249732edef6ad88899399047d7e49311a55749d3c373007d034ee471/pyarrow-18.1.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b", size = 29497406, upload-time = "2024-11-26T02:00:14.469Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/c7/ed6adb46d93a3177540e228b5ca30d99fc8ea3b13bdb88b6f8b6467e2cb7/pyarrow-18.1.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2", size = 30835095, upload-time = "2024-11-26T02:00:19.347Z" },
+ { url = "https://files.pythonhosted.org/packages/41/d7/ed85001edfb96200ff606943cff71d64f91926ab42828676c0fc0db98963/pyarrow-18.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191", size = 39194527, upload-time = "2024-11-26T02:00:24.085Z" },
+ { url = "https://files.pythonhosted.org/packages/59/16/35e28eab126342fa391593415d79477e89582de411bb95232f28b131a769/pyarrow-18.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa", size = 40131443, upload-time = "2024-11-26T02:00:29.483Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/95/e855880614c8da20f4cd74fa85d7268c725cf0013dc754048593a38896a0/pyarrow-18.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c", size = 38608750, upload-time = "2024-11-26T02:00:34.069Z" },
+ { url = "https://files.pythonhosted.org/packages/54/9d/f253554b1457d4fdb3831b7bd5f8f00f1795585a606eabf6fec0a58a9c38/pyarrow-18.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c", size = 40066690, upload-time = "2024-11-26T02:00:39.603Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/58/8912a2563e6b8273e8aa7b605a345bba5a06204549826f6493065575ebc0/pyarrow-18.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181", size = 25081054, upload-time = "2024-11-26T02:00:43.611Z" },
+ { url = "https://files.pythonhosted.org/packages/82/f9/d06ddc06cab1ada0c2f2fd205ac8c25c2701182de1b9c4bf7a0a44844431/pyarrow-18.1.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc", size = 29525542, upload-time = "2024-11-26T02:00:48.094Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/94/8917e3b961810587ecbdaa417f8ebac0abb25105ae667b7aa11c05876976/pyarrow-18.1.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386", size = 30829412, upload-time = "2024-11-26T02:00:52.458Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/e3/3b16c3190f3d71d3b10f6758d2d5f7779ef008c4fd367cedab3ed178a9f7/pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324", size = 39119106, upload-time = "2024-11-26T02:00:57.219Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/d6/5d704b0d25c3c79532f8c0639f253ec2803b897100f64bcb3f53ced236e5/pyarrow-18.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8", size = 40090940, upload-time = "2024-11-26T02:01:02.31Z" },
+ { url = "https://files.pythonhosted.org/packages/37/29/366bc7e588220d74ec00e497ac6710c2833c9176f0372fe0286929b2d64c/pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9", size = 38548177, upload-time = "2024-11-26T02:01:07.371Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/11/fabf6ecabb1fe5b7d96889228ca2a9158c4c3bb732e3b8ee3f7f6d40b703/pyarrow-18.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba", size = 40043567, upload-time = "2024-11-26T02:01:12.931Z" },
]
[[package]]
@@ -437,7 +496,6 @@ version = "6.11.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
- { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "macholib", marker = "sys_platform == 'darwin'" },
{ name = "packaging" },
{ name = "pefile", marker = "sys_platform == 'win32'" },
@@ -445,19 +503,19 @@ dependencies = [
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
{ name = "setuptools" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/55/d4/54f5f5c73b803e6256ea97ffc6ba8a305d9a5f57f85f9b00b282512bf18a/pyinstaller-6.11.1.tar.gz", hash = "sha256:491dfb4d9d5d1d9650d9507daec1ff6829527a254d8e396badd60a0affcb72ef", size = 4249772 }
+sdist = { url = "https://files.pythonhosted.org/packages/55/d4/54f5f5c73b803e6256ea97ffc6ba8a305d9a5f57f85f9b00b282512bf18a/pyinstaller-6.11.1.tar.gz", hash = "sha256:491dfb4d9d5d1d9650d9507daec1ff6829527a254d8e396badd60a0affcb72ef", size = 4249772, upload-time = "2024-11-10T17:01:25.415Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/96/15/b0f1c0985ee32fcd2f6ad9a486ef94e4db3fef9af025a3655e76cb708009/pyinstaller-6.11.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:44e36172de326af6d4e7663b12f71dbd34e2e3e02233e181e457394423daaf03", size = 991780 },
- { url = "https://files.pythonhosted.org/packages/fd/0f/9f54cb18abe2b1d89051bc9214c0cb40d7b5f4049c151c315dacc067f4a2/pyinstaller-6.11.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6d12c45a29add78039066a53fb05967afaa09a672426072b13816fe7676abfc4", size = 711739 },
- { url = "https://files.pythonhosted.org/packages/32/f7/79d10830780eff8339bfa793eece1df4b2459e35a712fc81983e8536cc29/pyinstaller-6.11.1-py3-none-manylinux2014_i686.whl", hash = "sha256:ddc0fddd75f07f7e423da1f0822e389a42af011f9589e0269b87e0d89aa48c1f", size = 714053 },
- { url = "https://files.pythonhosted.org/packages/25/f7/9961ef02cdbd2dbb1b1a215292656bd0ea72a83aafd8fb6373513849711e/pyinstaller-6.11.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0d6475559c4939f0735122989611d7f739ed3bf02f666ce31022928f7a7e4fda", size = 719133 },
- { url = "https://files.pythonhosted.org/packages/6f/4d/7f854842a1ce798de762a0b0bc5d5a4fc26ad06164a98575dc3c54abed1f/pyinstaller-6.11.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:e21c7806e34f40181e7606926a14579f848bfb1dc52cbca7eea66eccccbfe977", size = 709591 },
- { url = "https://files.pythonhosted.org/packages/7f/e0/00d29fc90c3ba50620c61554e26ebb4d764569507be7cd1c8794aa696f9a/pyinstaller-6.11.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:32c742a24fe65d0702958fadf4040f76de85859c26bec0008766e5dbabc5b68f", size = 710068 },
- { url = "https://files.pythonhosted.org/packages/3e/57/d14b44a69f068d2caaee49d15e45f9fa0f37c6a2d2ad778c953c1722a1ca/pyinstaller-6.11.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:208c0ef6dab0837a0a273ea32d1a3619a208e3d1fe3fec3785eea71a77fd00ce", size = 714439 },
- { url = "https://files.pythonhosted.org/packages/88/01/256824bb57ca208099c86c2fb289f888ca7732580e91ced48fa14e5903b2/pyinstaller-6.11.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:ad84abf465bcda363c1d54eafa76745d77b6a8a713778348377dc98d12a452f7", size = 710457 },
- { url = "https://files.pythonhosted.org/packages/7c/f0/98c9138f5f0ff17462f1ad6d712dcfa643b9a283d6238d464d8145bc139d/pyinstaller-6.11.1-py3-none-win32.whl", hash = "sha256:2e8365276c5131c9bef98e358fbc305e4022db8bedc9df479629d6414021956a", size = 1280261 },
- { url = "https://files.pythonhosted.org/packages/7d/08/f43080614b3e8bce481d4dfd580e579497c7dcdaf87656d9d2ad912e5796/pyinstaller-6.11.1-py3-none-win_amd64.whl", hash = "sha256:7ac83c0dc0e04357dab98c487e74ad2adb30e7eb186b58157a8faf46f1fa796f", size = 1340482 },
- { url = "https://files.pythonhosted.org/packages/ed/56/953c6594cb66e249563854c9cc04ac5a055c6c99d1614298feeaeaa9b87e/pyinstaller-6.11.1-py3-none-win_arm64.whl", hash = "sha256:35e6b8077d240600bb309ed68bb0b1453fd2b7ab740b66d000db7abae6244423", size = 1267519 },
+ { url = "https://files.pythonhosted.org/packages/96/15/b0f1c0985ee32fcd2f6ad9a486ef94e4db3fef9af025a3655e76cb708009/pyinstaller-6.11.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:44e36172de326af6d4e7663b12f71dbd34e2e3e02233e181e457394423daaf03", size = 991780, upload-time = "2024-11-10T17:00:17.242Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/0f/9f54cb18abe2b1d89051bc9214c0cb40d7b5f4049c151c315dacc067f4a2/pyinstaller-6.11.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6d12c45a29add78039066a53fb05967afaa09a672426072b13816fe7676abfc4", size = 711739, upload-time = "2024-11-10T17:00:21.734Z" },
+ { url = "https://files.pythonhosted.org/packages/32/f7/79d10830780eff8339bfa793eece1df4b2459e35a712fc81983e8536cc29/pyinstaller-6.11.1-py3-none-manylinux2014_i686.whl", hash = "sha256:ddc0fddd75f07f7e423da1f0822e389a42af011f9589e0269b87e0d89aa48c1f", size = 714053, upload-time = "2024-11-10T17:00:25.773Z" },
+ { url = "https://files.pythonhosted.org/packages/25/f7/9961ef02cdbd2dbb1b1a215292656bd0ea72a83aafd8fb6373513849711e/pyinstaller-6.11.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0d6475559c4939f0735122989611d7f739ed3bf02f666ce31022928f7a7e4fda", size = 719133, upload-time = "2024-11-10T17:00:30.342Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/4d/7f854842a1ce798de762a0b0bc5d5a4fc26ad06164a98575dc3c54abed1f/pyinstaller-6.11.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:e21c7806e34f40181e7606926a14579f848bfb1dc52cbca7eea66eccccbfe977", size = 709591, upload-time = "2024-11-10T17:00:34.932Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/e0/00d29fc90c3ba50620c61554e26ebb4d764569507be7cd1c8794aa696f9a/pyinstaller-6.11.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:32c742a24fe65d0702958fadf4040f76de85859c26bec0008766e5dbabc5b68f", size = 710068, upload-time = "2024-11-10T17:00:39.655Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/57/d14b44a69f068d2caaee49d15e45f9fa0f37c6a2d2ad778c953c1722a1ca/pyinstaller-6.11.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:208c0ef6dab0837a0a273ea32d1a3619a208e3d1fe3fec3785eea71a77fd00ce", size = 714439, upload-time = "2024-11-10T17:00:43.838Z" },
+ { url = "https://files.pythonhosted.org/packages/88/01/256824bb57ca208099c86c2fb289f888ca7732580e91ced48fa14e5903b2/pyinstaller-6.11.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:ad84abf465bcda363c1d54eafa76745d77b6a8a713778348377dc98d12a452f7", size = 710457, upload-time = "2024-11-10T17:00:47.89Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f0/98c9138f5f0ff17462f1ad6d712dcfa643b9a283d6238d464d8145bc139d/pyinstaller-6.11.1-py3-none-win32.whl", hash = "sha256:2e8365276c5131c9bef98e358fbc305e4022db8bedc9df479629d6414021956a", size = 1280261, upload-time = "2024-11-10T17:00:54.688Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/08/f43080614b3e8bce481d4dfd580e579497c7dcdaf87656d9d2ad912e5796/pyinstaller-6.11.1-py3-none-win_amd64.whl", hash = "sha256:7ac83c0dc0e04357dab98c487e74ad2adb30e7eb186b58157a8faf46f1fa796f", size = 1340482, upload-time = "2024-11-10T17:01:01.62Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/56/953c6594cb66e249563854c9cc04ac5a055c6c99d1614298feeaeaa9b87e/pyinstaller-6.11.1-py3-none-win_arm64.whl", hash = "sha256:35e6b8077d240600bb309ed68bb0b1453fd2b7ab740b66d000db7abae6244423", size = 1267519, upload-time = "2024-11-10T17:01:08.525Z" },
]
[[package]]
@@ -465,13 +523,12 @@ name = "pyinstaller-hooks-contrib"
version = "2025.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
- { name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "packaging" },
{ name = "setuptools" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/1b/dc256d42f4217db99b50d6d32dbbf841a41b9615506cde77d2345d94f4a5/pyinstaller_hooks_contrib-2025.1.tar.gz", hash = "sha256:130818f9e9a0a7f2261f1fd66054966a3a50c99d000981c5d1db11d3ad0c6ab2", size = 147043 }
+sdist = { url = "https://files.pythonhosted.org/packages/2f/1b/dc256d42f4217db99b50d6d32dbbf841a41b9615506cde77d2345d94f4a5/pyinstaller_hooks_contrib-2025.1.tar.gz", hash = "sha256:130818f9e9a0a7f2261f1fd66054966a3a50c99d000981c5d1db11d3ad0c6ab2", size = 147043, upload-time = "2025-01-31T21:51:40.131Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b7/48/833d67a585275e395f351e5787b4b7a8d462d87bca22a8c038f6ffdc2b3c/pyinstaller_hooks_contrib-2025.1-py3-none-any.whl", hash = "sha256:d3c799470cbc0bda60dcc8e6b4ab976777532b77621337f2037f558905e3a8e9", size = 346409 },
+ { url = "https://files.pythonhosted.org/packages/b7/48/833d67a585275e395f351e5787b4b7a8d462d87bca22a8c038f6ffdc2b3c/pyinstaller_hooks_contrib-2025.1-py3-none-any.whl", hash = "sha256:d3c799470cbc0bda60dcc8e6b4ab976777532b77621337f2037f558905e3a8e9", size = 346409, upload-time = "2025-01-31T21:51:37.45Z" },
]
[[package]]
@@ -486,9 +543,9 @@ dependencies = [
{ name = "pluggy" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
+sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919, upload-time = "2024-12-01T12:54:25.98Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
+ { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083, upload-time = "2024-12-01T12:54:19.735Z" },
]
[[package]]
@@ -498,27 +555,27 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
+ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "pytz"
version = "2024.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 }
+sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692, upload-time = "2024-09-11T02:24:47.91Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 },
+ { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002, upload-time = "2024-09-11T02:24:45.8Z" },
]
[[package]]
name = "pywin32-ctypes"
version = "0.2.3"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471 }
+sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 },
+ { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
]
[[package]]
@@ -531,109 +588,100 @@ dependencies = [
{ name = "idna" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
+sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
+ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
]
[[package]]
name = "setuptools"
version = "75.8.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 }
+sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222, upload-time = "2025-01-08T18:28:23.98Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 },
+ { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782, upload-time = "2025-01-08T18:28:20.912Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
+sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "soupsieve"
version = "2.6"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569, upload-time = "2024-08-13T13:39:12.166Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
+ { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186, upload-time = "2024-08-13T13:39:10.986Z" },
]
[[package]]
name = "tomli"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 },
- { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 },
- { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 },
- { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 },
- { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 },
- { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 },
- { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 },
- { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 },
- { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 },
- { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 },
- { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 },
- { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 },
- { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 },
- { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 },
- { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 },
- { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 },
- { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 },
- { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 },
- { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 },
- { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 },
- { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 },
- { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 },
- { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 },
- { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 },
- { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 },
- { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 },
- { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 },
- { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 },
- { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 },
- { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 },
- { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 },
+sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" },
+ { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" },
+ { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" },
+ { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" },
+ { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" },
+ { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" },
+ { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" },
+ { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" },
+ { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" },
+ { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" },
+ { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" },
+ { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
]
[[package]]
name = "tzdata"
version = "2024.2"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 }
+sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282, upload-time = "2024-09-23T18:56:46.89Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 },
+ { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586, upload-time = "2024-09-23T18:56:45.478Z" },
]
[[package]]
name = "urllib3"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
+ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" },
]
[[package]]
name = "xlrd"
version = "2.0.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a6/b3/19a2540d21dea5f908304375bd43f5ed7a4c28a370dc9122c565423e6b44/xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88", size = 100259 }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/a6/0c/c2a72d51fe56e08a08acc85d13013558a2d793028ae7385448a6ccdfae64/xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd", size = 96531 },
-]
-
-[[package]]
-name = "zipp"
-version = "3.21.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/b3/19a2540d21dea5f908304375bd43f5ed7a4c28a370dc9122c565423e6b44/xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88", size = 100259, upload-time = "2020-12-11T10:14:22.201Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 },
+ { url = "https://files.pythonhosted.org/packages/a6/0c/c2a72d51fe56e08a08acc85d13013558a2d793028ae7385448a6ccdfae64/xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd", size = 96531, upload-time = "2020-12-11T10:14:20.877Z" },
]