rosterops 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rosterops-0.1.0/.github/workflows/ci.yml +31 -0
- rosterops-0.1.0/.gitignore +24 -0
- rosterops-0.1.0/CHANGELOG.md +60 -0
- rosterops-0.1.0/CONTRIBUTING.md +32 -0
- rosterops-0.1.0/IMPLEMENTATION_PHASES.md +978 -0
- rosterops-0.1.0/LICENSE +21 -0
- rosterops-0.1.0/PKG-INFO +129 -0
- rosterops-0.1.0/README.md +70 -0
- rosterops-0.1.0/REQUIREMENTS.md +836 -0
- rosterops-0.1.0/SECURITY.md +29 -0
- rosterops-0.1.0/benchmarks/bench_scaling.py +91 -0
- rosterops-0.1.0/examples/README.md +51 -0
- rosterops-0.1.0/examples/config/generic_pharma.yaml +143 -0
- rosterops-0.1.0/examples/config/scenario.yaml +72 -0
- rosterops-0.1.0/examples/inputs/baseline.csv +5 -0
- rosterops-0.1.0/examples/inputs/leaves.csv +2 -0
- rosterops-0.1.0/examples/inputs/promotions.csv +2 -0
- rosterops-0.1.0/examples/inputs/returns.csv +2 -0
- rosterops-0.1.0/examples/inputs/terminations.csv +2 -0
- rosterops-0.1.0/examples/inputs/territories.csv +6 -0
- rosterops-0.1.0/examples/inputs/transfers.csv +2 -0
- rosterops-0.1.0/pyproject.toml +99 -0
- rosterops-0.1.0/src/rosterops/__init__.py +24 -0
- rosterops-0.1.0/src/rosterops/__main__.py +10 -0
- rosterops-0.1.0/src/rosterops/cli.py +279 -0
- rosterops-0.1.0/src/rosterops/config.py +380 -0
- rosterops-0.1.0/src/rosterops/domain/__init__.py +1 -0
- rosterops-0.1.0/src/rosterops/domain/dates.py +122 -0
- rosterops-0.1.0/src/rosterops/domain/intervals.py +162 -0
- rosterops-0.1.0/src/rosterops/engine/__init__.py +20 -0
- rosterops-0.1.0/src/rosterops/engine/api.py +98 -0
- rosterops-0.1.0/src/rosterops/engine/baseline.py +181 -0
- rosterops-0.1.0/src/rosterops/engine/context.py +89 -0
- rosterops-0.1.0/src/rosterops/engine/lifecycle_rules.py +387 -0
- rosterops-0.1.0/src/rosterops/engine/load.py +153 -0
- rosterops-0.1.0/src/rosterops/engine/metrics.py +99 -0
- rosterops-0.1.0/src/rosterops/engine/orchestrator.py +229 -0
- rosterops-0.1.0/src/rosterops/engine/reconciliation.py +119 -0
- rosterops-0.1.0/src/rosterops/engine/reconstruction.py +517 -0
- rosterops-0.1.0/src/rosterops/engine/result.py +159 -0
- rosterops-0.1.0/src/rosterops/engine/roster.py +68 -0
- rosterops-0.1.0/src/rosterops/engine/snapshot_rules.py +641 -0
- rosterops-0.1.0/src/rosterops/engine/state.py +234 -0
- rosterops-0.1.0/src/rosterops/engine/vacancies.py +141 -0
- rosterops-0.1.0/src/rosterops/events/__init__.py +37 -0
- rosterops-0.1.0/src/rosterops/events/ledger.py +180 -0
- rosterops-0.1.0/src/rosterops/events/normalize.py +259 -0
- rosterops-0.1.0/src/rosterops/events/pipeline.py +79 -0
- rosterops-0.1.0/src/rosterops/events/reconcile.py +240 -0
- rosterops-0.1.0/src/rosterops/exceptions.py +24 -0
- rosterops-0.1.0/src/rosterops/interop.py +66 -0
- rosterops-0.1.0/src/rosterops/io/__init__.py +24 -0
- rosterops-0.1.0/src/rosterops/io/ingest.py +118 -0
- rosterops-0.1.0/src/rosterops/io/raw.py +68 -0
- rosterops-0.1.0/src/rosterops/io/readers.py +211 -0
- rosterops-0.1.0/src/rosterops/mapping/__init__.py +7 -0
- rosterops-0.1.0/src/rosterops/mapping/canonical.py +523 -0
- rosterops-0.1.0/src/rosterops/models.py +291 -0
- rosterops-0.1.0/src/rosterops/outputs/__init__.py +12 -0
- rosterops-0.1.0/src/rosterops/outputs/writer.py +352 -0
- rosterops-0.1.0/src/rosterops/py.typed +0 -0
- rosterops-0.1.0/src/rosterops/rules/__init__.py +53 -0
- rosterops-0.1.0/src/rosterops/rules/base.py +249 -0
- rosterops-0.1.0/src/rosterops/rules/coverage_rules.py +69 -0
- rosterops-0.1.0/src/rosterops/rules/employment_rules.py +120 -0
- rosterops-0.1.0/src/rosterops/rules/grain_rules.py +52 -0
- rosterops-0.1.0/src/rosterops/rules/hierarchy_rules.py +68 -0
- rosterops-0.1.0/src/rosterops/rules/identifier_rules.py +107 -0
- rosterops-0.1.0/src/rosterops/rules/input_rules.py +110 -0
- rosterops-0.1.0/src/rosterops/rules/leave_rules.py +141 -0
- rosterops-0.1.0/src/rosterops/rules/promotion_rules.py +78 -0
- rosterops-0.1.0/src/rosterops/rules/reconciliation_rules.py +129 -0
- rosterops-0.1.0/src/rosterops/rules/settings.py +66 -0
- rosterops-0.1.0/src/rosterops/rules/temporal_rules.py +123 -0
- rosterops-0.1.0/src/rosterops/rules/territory_rules.py +130 -0
- rosterops-0.1.0/src/rosterops/rules/transfer_rules.py +127 -0
- rosterops-0.1.0/src/rosterops/version.py +3 -0
- rosterops-0.1.0/tests/conftest.py +124 -0
- rosterops-0.1.0/tests/integration/test_e2e_packaging.py +96 -0
- rosterops-0.1.0/tests/integration/test_invalid_variants.py +415 -0
- rosterops-0.1.0/tests/property/test_engine_prop.py +273 -0
- rosterops-0.1.0/tests/property/test_intervals_prop.py +92 -0
- rosterops-0.1.0/tests/rules/test_spec_conformance.py +64 -0
- rosterops-0.1.0/tests/unit/test_cli.py +236 -0
- rosterops-0.1.0/tests/unit/test_config.py +276 -0
- rosterops-0.1.0/tests/unit/test_dates.py +107 -0
- rosterops-0.1.0/tests/unit/test_engine_internals.py +213 -0
- rosterops-0.1.0/tests/unit/test_engine_scenario.py +176 -0
- rosterops-0.1.0/tests/unit/test_events.py +436 -0
- rosterops-0.1.0/tests/unit/test_exceptions.py +15 -0
- rosterops-0.1.0/tests/unit/test_interop.py +85 -0
- rosterops-0.1.0/tests/unit/test_intervals.py +140 -0
- rosterops-0.1.0/tests/unit/test_io_ingest.py +153 -0
- rosterops-0.1.0/tests/unit/test_io_readers.py +111 -0
- rosterops-0.1.0/tests/unit/test_lifecycle_rules.py +288 -0
- rosterops-0.1.0/tests/unit/test_mapping_canonical.py +219 -0
- rosterops-0.1.0/tests/unit/test_outputs.py +94 -0
- rosterops-0.1.0/tests/unit/test_rules_base.py +225 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["master", "main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Install package with dev + optional extras
|
|
21
|
+
run: pip install -e ".[dev,excel,pandas]"
|
|
22
|
+
- name: Lint
|
|
23
|
+
run: ruff check .
|
|
24
|
+
- name: Format check
|
|
25
|
+
run: ruff format --check .
|
|
26
|
+
- name: Type check
|
|
27
|
+
run: mypy src
|
|
28
|
+
- name: Tests with coverage
|
|
29
|
+
run: pytest --cov=rosterops --cov-report=term-missing
|
|
30
|
+
- name: Build distributions
|
|
31
|
+
run: python -m build
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
|
|
12
|
+
# Tooling
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
coverage.xml
|
|
18
|
+
htmlcov/
|
|
19
|
+
.hypothesis/
|
|
20
|
+
|
|
21
|
+
# Local runs / sensitive data
|
|
22
|
+
output/
|
|
23
|
+
*.local.yaml
|
|
24
|
+
*.local.yml
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Planning baseline: `REQUIREMENTS.md` (86-rule catalogue, modes A/B, domain
|
|
13
|
+
model, effective-date semantics) and `IMPLEMENTATION_PHASES.md` tracker.
|
|
14
|
+
- Package skeleton (src layout, hatchling build, CLI entry point, py.typed).
|
|
15
|
+
- Strict versioned configuration layer (`RosterConfig`) with policy, grain,
|
|
16
|
+
input-mapping, and rule-override schemas.
|
|
17
|
+
- Canonical domain schemas and enumerations (employee, territory, assignment,
|
|
18
|
+
event, coverage; grain and severity definitions).
|
|
19
|
+
- Half-open interval primitives and effective-date helpers (termination-date
|
|
20
|
+
semantics, reconstruction-window filtering).
|
|
21
|
+
- Rule registry: 86 stable rule specifications with default severities, mode
|
|
22
|
+
applicability, and parameter declarations; deterministic finding model.
|
|
23
|
+
- `rosterops rules list` / `rosterops rules describe` CLI commands.
|
|
24
|
+
- Tooling: ruff, mypy (strict), pytest + coverage, hypothesis, CI workflow
|
|
25
|
+
matrix for Python 3.10–3.13.
|
|
26
|
+
- Input ingestion layer: `read_raw`/`detect_format` for CSV (always),
|
|
27
|
+
Parquet (always), and Excel (optional `[excel]` extra via `fastexcel`);
|
|
28
|
+
graceful `ING-001/002/003` handling with actionable install hints.
|
|
29
|
+
- Source→canonical mapping (`mapping/canonical.py`): explicit mappings,
|
|
30
|
+
aliases, configurable null tokens, per-field date formats, categorical value
|
|
31
|
+
maps/validation, list parsing, trim policy, and per-row lineage
|
|
32
|
+
(`source_file`/`source_sheet`/`source_row`). Implements `SCH-001..005` and
|
|
33
|
+
`ID-005`; no row is ever silently dropped.
|
|
34
|
+
- Leading-zero identifier preservation (string keys end-to-end).
|
|
35
|
+
- Canonical event ledger (`events/`): all eight event types normalized into a
|
|
36
|
+
single model with namespaced identity plus content fingerprints, cross-file
|
|
37
|
+
duplicate detection (`ID-007`), baseline-window cutoff (`DAT-006`), same-date
|
|
38
|
+
conflict detection with an explicit policy hook (`DAT-007`), and auditable
|
|
39
|
+
termination-date-semantics conversion (`DAT-008`). Excluded events are never
|
|
40
|
+
dropped — they retain a row with a status and finding. Duplicate movements
|
|
41
|
+
surface as `REC-006` (and `PRO-003` for promotions); conflicting transfers as
|
|
42
|
+
`TRF-006`.
|
|
43
|
+
- `Lineage` value object shared by loaded and derived records.
|
|
44
|
+
- Rule engine (`engine/`): grain enforcement (`GRN-001..003`), baseline state
|
|
45
|
+
reconstruction with half-open intervals, snapshot (Mode B) and process
|
|
46
|
+
(Mode A) rule scoping, movement metrics, vacancy derivation, event
|
|
47
|
+
reconciliation (`REC-001..004`), and a deterministic finding sort with stable
|
|
48
|
+
issue keys/ids. Post-reconstruction lifecycle rules cover transfers
|
|
49
|
+
(`TRF-005/006/007/008`), promotions (`PRO-002/003/004/005`), leave
|
|
50
|
+
(`LOA-006/007/009/010`), coverage (`COV-003/004/005`), events after
|
|
51
|
+
termination (`DAT-005`), vacancy intervals (`TER-007`), and the `LOA-008`
|
|
52
|
+
guard.
|
|
53
|
+
- Result writers (`outputs/`): the full Mode A output set and a Mode B subset
|
|
54
|
+
that omits unavailable artefacts with reasons; safety checks refuse to write
|
|
55
|
+
into input folders unless `overwrite` is set; Excel via `openpyxl`, plus a
|
|
56
|
+
console report.
|
|
57
|
+
- Public API (`RosterEngine.process` / `.validate`) and a wired CLI
|
|
58
|
+
(`rosterops process`, `rosterops validate`) with documented exit codes.
|
|
59
|
+
- Optional pandas interop (`[pandas]` extra): `to_polars`/`to_pandas` without
|
|
60
|
+
mutating caller frames (decision D11).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Contributing to rosterops
|
|
2
|
+
|
|
3
|
+
## Development setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m venv .venv
|
|
7
|
+
.venv\Scripts\activate # Windows
|
|
8
|
+
pip install -e ".[dev]"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quality gates (run before submitting changes)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ruff check .
|
|
15
|
+
ruff format --check .
|
|
16
|
+
mypy src
|
|
17
|
+
pytest # unit + rules + property + integration
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
CI runs the same gates on Python 3.10–3.13.
|
|
21
|
+
|
|
22
|
+
## Guidelines
|
|
23
|
+
|
|
24
|
+
- Requirements live in `REQUIREMENTS.md`; progress in `IMPLEMENTATION_PHASES.md`.
|
|
25
|
+
A behavior change updates both in the same change.
|
|
26
|
+
- Every rule needs a stable ID, at least one positive and one negative test,
|
|
27
|
+
and documentation that matches the code. Never advertise an unimplemented rule.
|
|
28
|
+
- Domain decisions (date semantics, grain, policy defaults) are configuration,
|
|
29
|
+
not hard-coded facts. When ambiguity appears, pick a configurable default,
|
|
30
|
+
document it, and log it in the phases tracker backlog.
|
|
31
|
+
- Use synthetic data only in tests and examples. Never commit real roster data.
|
|
32
|
+
- Findings must carry source lineage (file, sheet, row, column) wherever known.
|