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.
Files changed (98) hide show
  1. rosterops-0.1.0/.github/workflows/ci.yml +31 -0
  2. rosterops-0.1.0/.gitignore +24 -0
  3. rosterops-0.1.0/CHANGELOG.md +60 -0
  4. rosterops-0.1.0/CONTRIBUTING.md +32 -0
  5. rosterops-0.1.0/IMPLEMENTATION_PHASES.md +978 -0
  6. rosterops-0.1.0/LICENSE +21 -0
  7. rosterops-0.1.0/PKG-INFO +129 -0
  8. rosterops-0.1.0/README.md +70 -0
  9. rosterops-0.1.0/REQUIREMENTS.md +836 -0
  10. rosterops-0.1.0/SECURITY.md +29 -0
  11. rosterops-0.1.0/benchmarks/bench_scaling.py +91 -0
  12. rosterops-0.1.0/examples/README.md +51 -0
  13. rosterops-0.1.0/examples/config/generic_pharma.yaml +143 -0
  14. rosterops-0.1.0/examples/config/scenario.yaml +72 -0
  15. rosterops-0.1.0/examples/inputs/baseline.csv +5 -0
  16. rosterops-0.1.0/examples/inputs/leaves.csv +2 -0
  17. rosterops-0.1.0/examples/inputs/promotions.csv +2 -0
  18. rosterops-0.1.0/examples/inputs/returns.csv +2 -0
  19. rosterops-0.1.0/examples/inputs/terminations.csv +2 -0
  20. rosterops-0.1.0/examples/inputs/territories.csv +6 -0
  21. rosterops-0.1.0/examples/inputs/transfers.csv +2 -0
  22. rosterops-0.1.0/pyproject.toml +99 -0
  23. rosterops-0.1.0/src/rosterops/__init__.py +24 -0
  24. rosterops-0.1.0/src/rosterops/__main__.py +10 -0
  25. rosterops-0.1.0/src/rosterops/cli.py +279 -0
  26. rosterops-0.1.0/src/rosterops/config.py +380 -0
  27. rosterops-0.1.0/src/rosterops/domain/__init__.py +1 -0
  28. rosterops-0.1.0/src/rosterops/domain/dates.py +122 -0
  29. rosterops-0.1.0/src/rosterops/domain/intervals.py +162 -0
  30. rosterops-0.1.0/src/rosterops/engine/__init__.py +20 -0
  31. rosterops-0.1.0/src/rosterops/engine/api.py +98 -0
  32. rosterops-0.1.0/src/rosterops/engine/baseline.py +181 -0
  33. rosterops-0.1.0/src/rosterops/engine/context.py +89 -0
  34. rosterops-0.1.0/src/rosterops/engine/lifecycle_rules.py +387 -0
  35. rosterops-0.1.0/src/rosterops/engine/load.py +153 -0
  36. rosterops-0.1.0/src/rosterops/engine/metrics.py +99 -0
  37. rosterops-0.1.0/src/rosterops/engine/orchestrator.py +229 -0
  38. rosterops-0.1.0/src/rosterops/engine/reconciliation.py +119 -0
  39. rosterops-0.1.0/src/rosterops/engine/reconstruction.py +517 -0
  40. rosterops-0.1.0/src/rosterops/engine/result.py +159 -0
  41. rosterops-0.1.0/src/rosterops/engine/roster.py +68 -0
  42. rosterops-0.1.0/src/rosterops/engine/snapshot_rules.py +641 -0
  43. rosterops-0.1.0/src/rosterops/engine/state.py +234 -0
  44. rosterops-0.1.0/src/rosterops/engine/vacancies.py +141 -0
  45. rosterops-0.1.0/src/rosterops/events/__init__.py +37 -0
  46. rosterops-0.1.0/src/rosterops/events/ledger.py +180 -0
  47. rosterops-0.1.0/src/rosterops/events/normalize.py +259 -0
  48. rosterops-0.1.0/src/rosterops/events/pipeline.py +79 -0
  49. rosterops-0.1.0/src/rosterops/events/reconcile.py +240 -0
  50. rosterops-0.1.0/src/rosterops/exceptions.py +24 -0
  51. rosterops-0.1.0/src/rosterops/interop.py +66 -0
  52. rosterops-0.1.0/src/rosterops/io/__init__.py +24 -0
  53. rosterops-0.1.0/src/rosterops/io/ingest.py +118 -0
  54. rosterops-0.1.0/src/rosterops/io/raw.py +68 -0
  55. rosterops-0.1.0/src/rosterops/io/readers.py +211 -0
  56. rosterops-0.1.0/src/rosterops/mapping/__init__.py +7 -0
  57. rosterops-0.1.0/src/rosterops/mapping/canonical.py +523 -0
  58. rosterops-0.1.0/src/rosterops/models.py +291 -0
  59. rosterops-0.1.0/src/rosterops/outputs/__init__.py +12 -0
  60. rosterops-0.1.0/src/rosterops/outputs/writer.py +352 -0
  61. rosterops-0.1.0/src/rosterops/py.typed +0 -0
  62. rosterops-0.1.0/src/rosterops/rules/__init__.py +53 -0
  63. rosterops-0.1.0/src/rosterops/rules/base.py +249 -0
  64. rosterops-0.1.0/src/rosterops/rules/coverage_rules.py +69 -0
  65. rosterops-0.1.0/src/rosterops/rules/employment_rules.py +120 -0
  66. rosterops-0.1.0/src/rosterops/rules/grain_rules.py +52 -0
  67. rosterops-0.1.0/src/rosterops/rules/hierarchy_rules.py +68 -0
  68. rosterops-0.1.0/src/rosterops/rules/identifier_rules.py +107 -0
  69. rosterops-0.1.0/src/rosterops/rules/input_rules.py +110 -0
  70. rosterops-0.1.0/src/rosterops/rules/leave_rules.py +141 -0
  71. rosterops-0.1.0/src/rosterops/rules/promotion_rules.py +78 -0
  72. rosterops-0.1.0/src/rosterops/rules/reconciliation_rules.py +129 -0
  73. rosterops-0.1.0/src/rosterops/rules/settings.py +66 -0
  74. rosterops-0.1.0/src/rosterops/rules/temporal_rules.py +123 -0
  75. rosterops-0.1.0/src/rosterops/rules/territory_rules.py +130 -0
  76. rosterops-0.1.0/src/rosterops/rules/transfer_rules.py +127 -0
  77. rosterops-0.1.0/src/rosterops/version.py +3 -0
  78. rosterops-0.1.0/tests/conftest.py +124 -0
  79. rosterops-0.1.0/tests/integration/test_e2e_packaging.py +96 -0
  80. rosterops-0.1.0/tests/integration/test_invalid_variants.py +415 -0
  81. rosterops-0.1.0/tests/property/test_engine_prop.py +273 -0
  82. rosterops-0.1.0/tests/property/test_intervals_prop.py +92 -0
  83. rosterops-0.1.0/tests/rules/test_spec_conformance.py +64 -0
  84. rosterops-0.1.0/tests/unit/test_cli.py +236 -0
  85. rosterops-0.1.0/tests/unit/test_config.py +276 -0
  86. rosterops-0.1.0/tests/unit/test_dates.py +107 -0
  87. rosterops-0.1.0/tests/unit/test_engine_internals.py +213 -0
  88. rosterops-0.1.0/tests/unit/test_engine_scenario.py +176 -0
  89. rosterops-0.1.0/tests/unit/test_events.py +436 -0
  90. rosterops-0.1.0/tests/unit/test_exceptions.py +15 -0
  91. rosterops-0.1.0/tests/unit/test_interop.py +85 -0
  92. rosterops-0.1.0/tests/unit/test_intervals.py +140 -0
  93. rosterops-0.1.0/tests/unit/test_io_ingest.py +153 -0
  94. rosterops-0.1.0/tests/unit/test_io_readers.py +111 -0
  95. rosterops-0.1.0/tests/unit/test_lifecycle_rules.py +288 -0
  96. rosterops-0.1.0/tests/unit/test_mapping_canonical.py +219 -0
  97. rosterops-0.1.0/tests/unit/test_outputs.py +94 -0
  98. 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.