From e20d9684cd84913a7d19abec8e1d47766596ba1c Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Sun, 8 Mar 2026 17:23:14 -0300 Subject: [PATCH 1/9] feat: complete epic-01 packaging & CI modernization - Modernize pyproject.toml with split dep groups, mypy/ruff config - Restructure CI into 4 parallel jobs (lint, typecheck, test, docs) - Migrate docs deployment to official GitHub Pages actions - Create tag-triggered release workflow with version validation - Add pre-commit hooks (ruff + mypy manual stage) Co-Authored-By: Claude Opus 4.6 --- .claude/.plan-executing | 1 + .claude/agent-memory/code-reviewer/MEMORY.md | 59 +++++ .github/workflows/docs.yml | 38 ++-- .github/workflows/main.yml | 56 +++-- .github/workflows/publish.yml | 40 ---- .github/workflows/release.yml | 47 ++++ .pre-commit-config.yaml | 17 ++ .../.implementation-state.json | 209 ++++++++++++++++++ plans/infra-docs-overhaul/00-master-plan.md | 118 ++++++++++ plans/infra-docs-overhaul/README.md | 66 ++++++ .../00-epic-overview.md | 38 ++++ .../ticket-001-modernize-pyproject-toml.md | 144 ++++++++++++ .../ticket-002-restructure-ci-workflow.md | 111 ++++++++++ .../ticket-003-migrate-docs-deployment.md | 124 +++++++++++ .../ticket-004-create-release-workflow.md | 139 ++++++++++++ .../ticket-005-add-pre-commit-hooks.md | 125 +++++++++++ .../00-epic-overview.md | 31 +++ ...ticket-006-migrate-sphinx-theme-to-furo.md | 126 +++++++++++ ...cket-007-update-sphinx-gallery-examples.md | 135 +++++++++++ .../00-epic-overview.md | 36 +++ .../ticket-008-create-architecture-page.md | 32 +++ .../ticket-009-create-faq-page.md | 31 +++ .../ticket-010-create-performance-guide.md | 31 +++ ...t-011-improve-api-reference-autosummary.md | 34 +++ .../ticket-012-update-index-toctree.md | 31 +++ .../00-epic-overview.md | 35 +++ .../ticket-013-expand-readme.md | 32 +++ .../ticket-014-create-contributing.md | 32 +++ .../ticket-015-reformat-changelog.md | 32 +++ .../ticket-016-update-installation-docs.md | 31 +++ .../learnings/epic-01-summary.md | 86 +++++++ pyproject.toml | 32 ++- 32 files changed, 2025 insertions(+), 74 deletions(-) create mode 100644 .claude/.plan-executing create mode 100644 .claude/agent-memory/code-reviewer/MEMORY.md delete mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 .pre-commit-config.yaml create mode 100644 plans/infra-docs-overhaul/.implementation-state.json create mode 100644 plans/infra-docs-overhaul/00-master-plan.md create mode 100644 plans/infra-docs-overhaul/README.md create mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md create mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md create mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md create mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md create mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md create mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md create mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md create mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md create mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md create mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md create mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md create mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md create mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md create mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md create mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-012-update-index-toctree.md create mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/00-epic-overview.md create mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md create mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md create mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md create mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md create mode 100644 plans/infra-docs-overhaul/learnings/epic-01-summary.md diff --git a/.claude/.plan-executing b/.claude/.plan-executing new file mode 100644 index 0000000..584c847 --- /dev/null +++ b/.claude/.plan-executing @@ -0,0 +1 @@ +active diff --git a/.claude/agent-memory/code-reviewer/MEMORY.md b/.claude/agent-memory/code-reviewer/MEMORY.md new file mode 100644 index 0000000..655fbbd --- /dev/null +++ b/.claude/agent-memory/code-reviewer/MEMORY.md @@ -0,0 +1,59 @@ +# Code Reviewer Memory — idecomp project + +## Project Conventions + +- Stack: Python, pandas, numpy, cfinterface framework +- Lint: ruff (default config, F401 not enabled by default) +- Type check: mypy strict on idecomp.decomp._ and idecomp.libs._ +- Test framework: pytest + mock_open patterns for round-trip tests +- Python >= 3.10 (lowercase generics like `list[str]` are fine) +- No `from __future__ import annotations` — not used anywhere in codebase + +## Confirmed Patterns + +### Round-trip test mock_open extraction + +cfinterface TextualRepository calls `open(path, "w", encoding=...)` then `.close()` directly +(no context manager overhead in mock_calls). So mock_calls structure is: + +- [0] call(path, mode, encoding=...) <- open +- [1..n-2] call().write(...) <- writes +- [n-1] call().close() + +`range(1, len(chamadas) - 1)` correctly extracts just the write calls for SectionFile/BlockFile. +`range(2, len(chamadas) - 1)` is used for RegisterFile (dadger, dadgnl) — likely a header/encoding +write at index 1 that should be skipped. + +### pd.concat empty-list guard pattern + +The safe pattern (used in relato.py, custos.py, relgnl.py): + +```python +dfs = [...] +if dfs: + return pd.concat(dfs, ignore_index=True) +return None +``` + +The UNSAFE pattern (found in mapcut modelos after refactor): + +```python +dfs = [...] +return pd.concat(dfs).reset_index(drop=True) # raises ValueError when dfs is [] +``` + +pd.concat([]) raises `ValueError: No objects to concatenate` in pandas >= 2.x. + +### PEP 562 lazy imports (decomp/**init**.py) + +`globals()[name] = value` caching in `__getattr__` is the standard PEP 562 idiom. +`__dir__` returning `__all__` is minimal but documented and acceptable. + +## Common False Positives to Avoid + +- `type: ignore[override]` on cfinterface Block/Section read/write methods: intentional, + cfinterface base signature returns bool but subclasses return None. +- `type: ignore[assignment]` in dadger.py: attribute types from cfinterface registers + are not known to mypy; suppressing is correct. +- Redundant mypy overrides (e.g., `idecomp.decomp.modelos.blocos.*` after `idecomp.decomp.modelos.*`): + harmless, not a bug. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 71c7233..ffc5415 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,9 +4,17 @@ on: branches: - main workflow_dispatch: - + +permissions: + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + jobs: - docs: + build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -16,18 +24,22 @@ jobs: run: uv python install - name: Install the project run: | - uv sync --all-extras --dev - - name: Runs tests - run: | - uv run pytest ./tests + uv sync --extra docs - name: Sphinx build run: | uv run sphinx-build -M html docs/source docs/build - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 with: - publish_branch: gh-pages - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs/build/html - force_orphan: true \ No newline at end of file + path: docs/build/html + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4075c51..722e087 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,34 @@ on: - main jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + - name: Set up Python 3.12 + run: uv python install 3.12 + - name: Install the project + run: uv sync --extra lint + - name: Ruff check + run: uv run ruff check ./idecomp + - name: Ruff format check + run: uv run ruff format --check ./idecomp + + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + - name: Set up Python 3.12 + run: uv python install 3.12 + - name: Install the project + run: uv sync --extra lint + - name: Static type check + run: uv run mypy ./idecomp + test: runs-on: ubuntu-latest strategy: @@ -21,11 +49,9 @@ jobs: - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} - name: Install the project - run: | - uv sync --all-extras --dev - - name: Runs tests - run: | - uv run pytest --cov-report=xml --cov=idecomp ./tests + run: uv sync --extra test + - name: Run tests + run: uv run pytest --cov-report=xml --cov=idecomp ./tests - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -35,12 +61,16 @@ jobs: name: codecov-idecomp fail_ci_if_error: true verbose: true - - name: Static type check - run: | - uv run mypy ./idecomp - - name: Linter code check - run: | - uv run ruff check ./idecomp + + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + - name: Set up Python 3.12 + run: uv python install 3.12 + - name: Install the project + run: uv sync --extra docs - name: Sphinx build - run: | - uv run sphinx-build -M html docs/source docs/build \ No newline at end of file + run: uv run sphinx-build -M html docs/source docs/build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 1851234..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: deploy - - -on: - release: - types: [created] - -jobs: - build-and-publish: - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/idecomp - permissions: - id-token: write - steps: - - uses: actions/checkout@v4 - - name: Install uv - uses: astral-sh/setup-uv@v3 - - name: Set up Python - run: uv python install 3.11 - - name: Install the project - run: | - uv sync --all-extras --dev - - name: Runs tests - run: | - uv run pytest ./tests - - name: Static typing check - run: | - uv run mypy ./idecomp - - name: PEP8 check - run: | - uv run ruff check ./idecomp - - name: Builds package - if: startsWith(github.ref, 'refs/tags') - run: | - uv build - - name: PyPI publish - if: startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..89e1d01 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,47 @@ +name: release + +on: + push: + tags: + - "v*" + +jobs: + build-and-publish: + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/idecomp + permissions: + id-token: write + contents: write + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + - name: Set up Python + run: uv python install 3.12 + - name: Install the project + run: uv sync --extra test --extra lint + - name: Validate version + run: | + PKG_VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' idecomp/__init__.py) + TAG_VERSION=${GITHUB_REF#refs/tags/v} + if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then + echo "Version mismatch: __init__.py=$PKG_VERSION tag=$TAG_VERSION" + exit 1 + fi + - name: Run tests + run: uv run pytest ./tests + - name: Type check + run: uv run mypy ./idecomp + - name: Lint check + run: uv run ruff check ./idecomp + - name: Build package + run: uv build + - name: PyPI publish + uses: pypa/gh-action-pypi-publish@release/v1 + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create ${{ github.ref_name }} --generate-notes diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9d3e8d0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.6 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + + - repo: local + hooks: + - id: mypy + name: mypy + entry: uv run mypy ./idecomp + language: system + types: [python] + pass_filenames: false + stages: [manual] diff --git a/plans/infra-docs-overhaul/.implementation-state.json b/plans/infra-docs-overhaul/.implementation-state.json new file mode 100644 index 0000000..a1de29b --- /dev/null +++ b/plans/infra-docs-overhaul/.implementation-state.json @@ -0,0 +1,209 @@ +{ + "plan": "infra-docs-overhaul", + "last_updated": "2026-03-08T21:00:00Z", + "progressive": true, + "tickets": { + "ticket-001-modernize-pyproject-toml.md": { + "status": "completed", + "detail_level": "detailed", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "python-task-automation-developer" + }, + "ticket-002-restructure-ci-workflow.md": { + "status": "completed", + "detail_level": "detailed", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "python-task-automation-developer" + }, + "ticket-003-migrate-docs-deployment.md": { + "status": "completed", + "detail_level": "detailed", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "python-task-automation-developer" + }, + "ticket-004-create-release-workflow.md": { + "status": "completed", + "detail_level": "detailed", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "python-task-automation-developer" + }, + "ticket-005-add-pre-commit-hooks.md": { + "status": "completed", + "detail_level": "detailed", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "python-task-automation-developer" + }, + "ticket-006-migrate-sphinx-theme-to-furo.md": { + "status": "pending", + "detail_level": "detailed", + "scores": { + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + } + }, + "ticket-007-update-sphinx-gallery-examples.md": { + "status": "pending", + "detail_level": "detailed", + "scores": { + "readiness": 0.94, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 0.8, + "dependency_clarity": 1.0, + "atomicity": 0.8 + } + } + }, + "ticket-008-create-architecture-page.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-009-create-faq-page.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-010-create-performance-guide.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-011-improve-api-reference-autosummary.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-012-update-index-toctree.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-013-expand-readme.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-014-create-contributing.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-015-reformat-changelog.md": { + "status": "pending", + "detail_level": "outline" + }, + "ticket-016-update-installation-docs.md": { + "status": "pending", + "detail_level": "outline" + } + }, + "epics": { + "epic-01-packaging-ci-modernization": { + "phase": "completed", + "learning_extracted": true + }, + "epic-02-sphinx-modernization": { + "phase": "executing", + "learning_extracted": false + }, + "epic-03-documentation-content": { + "phase": "outline", + "learning_extracted": false + }, + "epic-04-repository-polish": { + "phase": "outline", + "learning_extracted": false + } + } +} diff --git a/plans/infra-docs-overhaul/00-master-plan.md b/plans/infra-docs-overhaul/00-master-plan.md new file mode 100644 index 0000000..aa85765 --- /dev/null +++ b/plans/infra-docs-overhaul/00-master-plan.md @@ -0,0 +1,118 @@ +# Master Plan: Infrastructure & Documentation Overhaul (idecomp) + +## Executive Summary + +This plan modernizes the idecomp Python package infrastructure, CI/CD pipelines, documentation theme, content, and repository polish. It replicates the successful overhaul completed for the sibling inewave package, adapted for idecomp's DECOMP-specific structure (the `idecomp/decomp/` and `idecomp/libs/` modules, DECOMP file formats, and existing `py.typed` marker). + +The plan is organized into 4 epics with 16 tickets, following the same proven structure from the inewave overhaul. + +## Goals & Non-Goals + +### Goals + +- Modernize `pyproject.toml` with proper metadata, dependency groups, and tool configuration +- Split the monolithic CI workflow into parallel, focused jobs (lint, typecheck, test matrix, docs) +- Migrate docs deployment from deprecated `peaceiris/actions-gh-pages@v3` to official GitHub Pages actions +- Create a tag-triggered release workflow with version validation (regex, not exec) +- Add pre-commit hooks for ruff and mypy +- Migrate Sphinx theme from sphinx-rtd-theme to Furo with dark mode +- Expand documentation with architecture, FAQ, and performance pages (all in Brazilian Portuguese) +- Improve API reference with autosummary +- Polish repository with expanded README, CONTRIBUTING.md, reformatted CHANGELOG + +### Non-Goals + +- Changing the idecomp public API or data models +- Adding new DECOMP file support +- Migrating away from cfinterface +- Changing the test suite logic (only CI pipeline changes) +- Translating any content to English + +## Architecture Overview + +### Current State + +- **Build**: Hatchling, version in `idecomp/__init__.py`, `py.typed` present +- **pyproject.toml**: Flat dev deps, minimal ruff config, no mypy config, sparse classifiers, description is just "idecomp" +- **CI**: Single `main.yml` job running lint/typecheck/test/docs sequentially per matrix entry; separate `docs.yml` using deprecated peaceiris action; `publish.yml` triggered on GitHub release events +- **Docs**: sphinx-rtd-theme, pt_BR, extensions include autosummary/autodoc/intersphinx/viewcode/sphinx-gallery/numpydoc; sections for apresentacao, geral (instalacao/tutorial/contribuicao), referencia (decomp + libs) +- **Repo**: Minimal README (36 lines), no CONTRIBUTING.md, no .pre-commit-config.yaml, no mypy config, CHANGELOG in ad-hoc format + +### Target State + +- **pyproject.toml**: Full Portuguese description, Python 3.10/3.11/3.12 classifiers, `Typing :: Typed`, split dep groups (test/lint/docs/dev), mypy and ruff config sections +- **CI**: 4 parallel jobs in main.yml (lint, typecheck, test matrix, docs); docs.yml using official `upload-pages-artifact@v3` + `deploy-pages@v4`; release.yml with tag trigger and version validation +- **Docs**: Furo theme with dark mode (monokai), updated sphinx-gallery examples, new pages (arquitetura, faq, desempenho), improved autosummary, updated index.rst toctree +- **Repo**: Expanded README with badges, CONTRIBUTING.md, Keep a Changelog format, updated installation docs, .pre-commit-config.yaml + +### Key Design Decisions + +1. **Regex for version extraction** in release workflow (never `exec()` with relative imports) +2. **mypy hook at `stages: [manual]`** due to cfinterface compatibility issues +3. **All documentation in Brazilian Portuguese** (pt_BR) +4. **`uv run` prefix** for all tool invocations in CI and docs +5. **Furo theme** replaces sphinx-rtd-theme (dark mode with monokai pygments) +6. **Keep a Changelog** format for CHANGELOG.md with Portuguese category names + +## Technical Approach + +### Tech Stack + +- Python 3.10+ with Hatchling build system +- GitHub Actions for CI/CD +- Sphinx with Furo theme for documentation +- ruff for linting/formatting, mypy for type checking +- pre-commit for local hooks + +### Component/Module Breakdown + +| Component | Current | Target | +| ----------------------- | ----------------------------- | --------------------------------------------- | +| pyproject.toml | Flat deps, sparse metadata | Split groups, full metadata, tool configs | +| main.yml | 1 sequential job | 4 parallel jobs | +| docs.yml | peaceiris/actions-gh-pages@v3 | upload-pages-artifact@v3 + deploy-pages@v4 | +| publish.yml | Release trigger | Tag trigger + version validation + GH release | +| .pre-commit-config.yaml | N/A | ruff + mypy hooks | +| docs/source/conf.py | sphinx-rtd-theme | Furo | +| docs content | 3 pages | 3 + 3 new guide pages | +| README.md | 36 lines | Full with badges | +| CONTRIBUTING.md | N/A | Full contributor guide | +| CHANGELOG.md | Ad-hoc format | Keep a Changelog | + +### Data Flow + +No runtime data flow changes. This plan affects only build/CI/docs/repo infrastructure. + +### Testing Strategy + +- Verify CI workflows with dry-run analysis (YAML structure validation) +- Verify Sphinx builds successfully with Furo theme +- Verify pre-commit hooks run correctly +- No changes to the existing test suite + +## Phases & Milestones + +| Epic | Name | Tickets | Estimated Duration | Milestone | +| ---- | ------------------------------- | ------- | ------------------ | ------------------------------------------------------- | +| 1 | Packaging & CI Modernization | 5 | 1-2 weeks | CI pipeline fully parallel, pre-commit hooks active | +| 2 | Sphinx Modernization | 2 | 3-5 days | Furo theme live, gallery working | +| 3 | Documentation Content Expansion | 5 | 2-3 weeks | 3 new guide pages, improved API ref, updated toctree | +| 4 | Repository Polish | 4 | 1-2 weeks | Full README, CONTRIBUTING, CHANGELOG, installation docs | + +## Risk Analysis + +| Risk | Likelihood | Impact | Mitigation | +| ----------------------------------------------- | ---------- | ------ | ------------------------------------------------------------------------------ | +| cfinterface import errors in sphinx-gallery | Medium | Medium | Known from inewave; test gallery build early in Epic 2 | +| mypy strict + cfinterface conflicts | Medium | Low | Known fix: manual stage for pre-commit hook, `warn_return_any: false` override | +| ruff auto-format changes many files | High | Low | Run ruff format once, commit separately before other changes | +| GitHub Pages action migration breaks deployment | Low | High | Test in PR before merging; keep old workflow as fallback | + +## Success Metrics + +- All 4 CI jobs pass independently on PR +- Sphinx builds with Furo theme without warnings +- Pre-commit hooks catch formatting/linting issues locally +- Documentation site has architecture, FAQ, and performance pages +- README displays all badges correctly +- Release workflow creates GitHub release and publishes to PyPI on tag push diff --git a/plans/infra-docs-overhaul/README.md b/plans/infra-docs-overhaul/README.md new file mode 100644 index 0000000..da7fc50 --- /dev/null +++ b/plans/infra-docs-overhaul/README.md @@ -0,0 +1,66 @@ +# Infrastructure & Documentation Overhaul (idecomp) + +Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentation theme/content, and repository polish. Replicates the successful overhaul from the inewave sibling project, adapted for idecomp's DECOMP-specific structure. + +## Tech Stack + +- Python 3.10+ / Hatchling build system +- GitHub Actions CI/CD +- Sphinx + Furo documentation theme +- ruff (linting/formatting) + mypy (type checking) +- pre-commit hooks + +## Epics + +| Epic | Name | Tickets | Detail Level | Phase | +| ---- | ------------------------------- | ------- | ------------ | --------- | +| 1 | Packaging & CI Modernization | 5 | Detailed | executing | +| 2 | Sphinx Modernization | 2 | Detailed | executing | +| 3 | Documentation Content Expansion | 5 | Outline | outline | +| 4 | Repository Polish | 4 | Outline | outline | + +## Progress + +| Ticket | Title | Epic | Status | Detail Level | Readiness | Quality | Badge | +| ---------- | -------------------------------------------------------- | ------- | --------- | ------------ | --------- | ------- | --------- | +| ticket-001 | Modernize pyproject.toml Metadata and Dependency Groups | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-002 | Restructure CI Workflow into Parallel Jobs | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-003 | Migrate Docs Deployment to Official GitHub Pages Actions | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-004 | Create Tag-Triggered Release Workflow | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-005 | Add Pre-commit Hooks Configuration | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-006 | Migrate Sphinx Theme to Furo | epic-02 | pending | Detailed | 1.00 | -- | -- | +| ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | epic-02 | pending | Detailed | 0.94 | -- | -- | +| ticket-008 | Create Architecture Documentation Page | epic-03 | pending | Outline | -- | -- | -- | +| ticket-009 | Create FAQ Documentation Page | epic-03 | pending | Outline | -- | -- | -- | +| ticket-010 | Create Performance Guide Page | epic-03 | pending | Outline | -- | -- | -- | +| ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | pending | Outline | -- | -- | -- | +| ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | pending | Outline | -- | -- | -- | +| ticket-013 | Expand README with Badges and Sections | epic-04 | pending | Outline | -- | -- | -- | +| ticket-014 | Create CONTRIBUTING.md | epic-04 | pending | Outline | -- | -- | -- | +| ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | pending | Outline | -- | -- | -- | +| ticket-016 | Update Installation Documentation | epic-04 | pending | Outline | -- | -- | -- | + +## Dependency Graph + +``` +ticket-001 (pyproject.toml) + |---> ticket-002 (CI restructure) ---> ticket-013 (README badges) + |---> ticket-004 (release workflow) + |---> ticket-005 (pre-commit) ---> ticket-014 (CONTRIBUTING) + |---> ticket-006 (Furo theme) + |---> ticket-007 (gallery examples) + |---> ticket-008 (architecture page) --+ + |---> ticket-009 (FAQ page) -----------+--> ticket-012 (index toctree) + |---> ticket-010 (performance page) ---+ + |---> ticket-011 (API autosummary) + +ticket-003 (docs deployment) [independent] +ticket-015 (changelog) [independent] +ticket-016 (installation docs) [independent] +``` + +## Notes + +- This is a **progressive plan**: Epics 1-2 have fully detailed tickets; Epics 3-4 have outline tickets that will be refined with learnings from earlier epics +- All documentation content must be written in **Brazilian Portuguese** (pt_BR) +- Key learnings from the inewave overhaul are embedded in the detailed tickets (regex version extraction, mypy manual stage, cfinterface workarounds) diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md new file mode 100644 index 0000000..3e74385 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md @@ -0,0 +1,38 @@ +# Epic 1: Packaging & CI Modernization + +## Goal + +Modernize the idecomp build metadata, CI/CD pipelines, and developer tooling to match the standards established in the inewave sibling project. After this epic, the CI will run 4 parallel jobs (lint, typecheck, test matrix, docs), the release workflow will be tag-triggered with version validation, and pre-commit hooks will enforce code quality locally. + +## Scope + +- Modernize `pyproject.toml` metadata, dependency groups, and tool configuration +- Restructure `main.yml` into 4 parallel CI jobs +- Migrate `docs.yml` to official GitHub Pages deployment actions +- Create tag-triggered `release.yml` with version validation +- Add `.pre-commit-config.yaml` with ruff and mypy hooks + +## Tickets + +| Order | Ticket | Title | Points | +| ----- | ---------- | -------------------------------------------------------- | ------ | +| 1 | ticket-001 | Modernize pyproject.toml Metadata and Dependency Groups | 3 | +| 2 | ticket-002 | Restructure CI Workflow into Parallel Jobs | 3 | +| 3 | ticket-003 | Migrate Docs Deployment to Official GitHub Pages Actions | 2 | +| 4 | ticket-004 | Create Tag-Triggered Release Workflow | 3 | +| 5 | ticket-005 | Add Pre-commit Hooks Configuration | 2 | + +## Dependencies + +- ticket-002 depends on ticket-001 (needs dependency groups defined) +- ticket-003 is independent +- ticket-004 depends on ticket-001 (needs dependency groups) +- ticket-005 depends on ticket-001 (needs ruff/mypy config in pyproject.toml) + +## Completion Criteria + +- `uv sync --extra test` installs only test deps; `uv sync --extra lint` installs only lint deps +- `main.yml` runs 4 independent jobs that pass on PR +- `docs.yml` deploys using `actions/upload-pages-artifact@v3` and `actions/deploy-pages@v4` +- `release.yml` triggers on `v*` tags, validates version with regex, creates GitHub release +- `pre-commit run --all-files` executes ruff check + ruff format; `pre-commit run --hook-stage manual --all-files` runs mypy diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md new file mode 100644 index 0000000..9d42ef7 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md @@ -0,0 +1,144 @@ +# ticket-001 Modernize pyproject.toml Metadata and Dependency Groups + +## Context + +### Background + +The idecomp `pyproject.toml` has sparse metadata (description is just "idecomp", only Python 3.10 classifier), flat dev dependencies in a single group, and no tool configuration for mypy. This ticket brings it to parity with the inewave sibling project's modernized `pyproject.toml`. + +### Relation to Epic + +This is the foundational ticket of Epic 1. All other CI and tooling tickets depend on the dependency groups and tool configuration defined here. + +### Current State + +File: `/home/rogerio/git/idecomp/pyproject.toml` + +- `description = "idecomp"` (should be descriptive, in Portuguese) +- Classifiers: only `Python :: 3.10`, missing 3.11/3.12 and `Typing :: Typed` +- Single `[project.optional-dependencies] dev` group with all deps mixed together: pytest, pytest-cov, ruff, mypy, sphinx-rtd-theme, sphinx-gallery, sphinx, numpydoc, plotly, matplotlib +- `[tool.ruff]` has only `line-length = 80` +- No `[tool.mypy]` section +- No `[tool.ruff.lint]` section +- `py.typed` marker already exists at `/home/rogerio/git/idecomp/idecomp/py.typed` +- Wheel include already covers `idecomp/` (which includes `py.typed`) + +## Specification + +### Requirements + +1. Update `description` to `"Pacote para manipulacao dos arquivos do DECOMP"` (matching the README's Portuguese description) +2. Add classifiers: `Programming Language :: Python :: 3.11`, `Programming Language :: Python :: 3.12`, `Typing :: Typed` +3. Split `[project.optional-dependencies]` into 4 groups: + - `test = ["pytest", "pytest-cov"]` + - `lint = ["ruff", "mypy"]` + - `docs = ["sphinx", "furo", "sphinx-gallery", "numpydoc", "plotly", "matplotlib"]` + - `dev = ["idecomp[test,lint,docs]"]` (self-referencing alias) +4. Note: `furo` replaces `sphinx-rtd-theme` in the docs group (theme migration happens in ticket-006, but the dependency is set here) +5. Add `[tool.mypy]` section with strict settings and cfinterface override: + + ```toml + [tool.mypy] + python_version = "3.10" + warn_return_any = false + warn_unused_configs = true + disallow_untyped_defs = true + disallow_incomplete_defs = true + check_untyped_defs = true + no_implicit_optional = true + warn_redundant_casts = true + warn_unused_ignores = true + strict = true + + [[tool.mypy.overrides]] + module = "cfinterface.*" + ignore_missing_imports = true + warn_return_any = false + ``` + +6. Expand `[tool.ruff.lint]` with select rules: + ```toml + [tool.ruff.lint] + select = ["E", "F", "I", "UP"] + ``` +7. Verify `py.typed` is present and included in wheel build (no changes needed, just verify) + +### Inputs/Props + +- Current file: `/home/rogerio/git/idecomp/pyproject.toml` (57 lines) + +### Outputs/Behavior + +- Updated `pyproject.toml` with all changes above +- `uv sync --extra test` installs only pytest + pytest-cov +- `uv sync --extra lint` installs only ruff + mypy +- `uv sync --extra docs` installs only sphinx + furo + sphinx-gallery + numpydoc + plotly + matplotlib +- `uv sync --extra dev` installs all of the above + +### Error Handling + +- If the self-referencing `dev` extra causes resolution issues, fall back to listing all deps explicitly in the dev group +- The cfinterface pin `>=1.8,<=1.8.3` must remain unchanged + +## Acceptance Criteria + +- [ ] Given the updated `pyproject.toml`, when running `uv sync --extra test`, then only `pytest` and `pytest-cov` are installed as extras (verify with `uv pip list | grep -i pytest`) +- [ ] Given the updated `pyproject.toml`, when inspecting the `[project]` section, then `description` equals `"Pacote para manipulacao dos arquivos do DECOMP"` and classifiers include `Programming Language :: Python :: 3.11`, `Programming Language :: Python :: 3.12`, and `Typing :: Typed` +- [ ] Given the updated `pyproject.toml`, when inspecting `[tool.mypy]`, then `strict = true` is set and `[[tool.mypy.overrides]]` for `cfinterface.*` has `ignore_missing_imports = true` and `warn_return_any = false` +- [ ] Given the updated `pyproject.toml`, when inspecting `[tool.ruff.lint]`, then `select = ["E", "F", "I", "UP"]` is present +- [ ] Given the file `/home/rogerio/git/idecomp/idecomp/py.typed`, when inspecting the wheel build config `[tool.hatch.build.targets.wheel]`, then the include pattern `"idecomp/"` covers the `py.typed` marker file + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/pyproject.toml` +2. Update the `description` field in `[project]` +3. Add missing classifiers to the `classifiers` list +4. Replace the single `dev` group in `[project.optional-dependencies]` with 4 groups: `test`, `lint`, `docs`, `dev` +5. Add `[tool.mypy]` section after the existing `[tool.ruff]` section +6. Add `[tool.ruff.lint]` section after `[tool.ruff]` +7. Verify `idecomp/py.typed` exists and the wheel include covers it (read-only check) + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/pyproject.toml` (the only file modified) + +### Patterns to Follow + +- Match the inewave pyproject.toml structure for consistency across sibling projects +- Use self-referencing extras syntax: `"idecomp[test,lint,docs]"` + +### Pitfalls to Avoid + +- Do NOT remove the `cfinterface>=1.8,<=1.8.3` pin from `[project] dependencies` +- Do NOT add `sphinx-rtd-theme` to the docs group (it is being replaced by `furo`) +- Do NOT use `exec()` anywhere for version extraction (the Hatchling `path` config handles this) +- The `warn_return_any = false` must appear both at the top-level `[tool.mypy]` AND in the `[[tool.mypy.overrides]]` for cfinterface because `strict = true` re-enables it + +## Testing Requirements + +### Unit Tests + +- No code changes; no unit tests needed + +### Integration Tests + +- Run `uv sync --extra test` and verify pytest is available +- Run `uv sync --extra lint` and verify ruff and mypy are available +- Run `uv sync --extra docs` and verify sphinx and furo are available +- Run `uv sync --extra dev` and verify all tools are available + +### E2E Tests + +- Not applicable + +## Dependencies + +- **Blocked By**: None (first ticket in the plan) +- **Blocks**: ticket-002-restructure-ci-workflow.md, ticket-004-create-release-workflow.md, ticket-005-add-pre-commit-hooks.md, ticket-006-migrate-sphinx-theme-to-furo.md + +## Effort Estimate + +**Points**: 3 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md new file mode 100644 index 0000000..e44fc61 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md @@ -0,0 +1,111 @@ +# ticket-002 Restructure CI Workflow into Parallel Jobs + +## Context + +### Background + +The current `main.yml` workflow runs a single job that sequentially executes pytest, mypy, ruff, and sphinx-build for each Python version in the matrix. This wastes CI time because a linting failure only surfaces after tests complete. The inewave overhaul split this into 4 parallel jobs, and idecomp should follow the same pattern. + +### Relation to Epic + +Second ticket in Epic 1. Depends on ticket-001 for the dependency group split (each job installs only its required extras). + +### Current State + +File: `/home/rogerio/git/idecomp/.github/workflows/main.yml` + +- Workflow name: `tests` +- Triggers: push/PR to `main` +- Single `test` job with matrix `python-version: ["3.10", "3.11", "3.12"]` +- Steps: checkout, install uv, install python, `uv sync --all-extras --dev`, pytest with coverage, codecov upload, mypy, ruff check, sphinx-build +- All 7 steps run sequentially in each matrix entry + +## Specification + +### Requirements + +1. Replace the single `test` job with 4 independent jobs: + - **lint**: Runs `ruff check ./idecomp` and `ruff format --check ./idecomp`. Single Python version (3.12). Installs `--extra lint`. + - **typecheck**: Runs `uv run mypy ./idecomp`. Single Python version (3.12). Installs `--extra lint`. + - **test**: Runs `uv run pytest --cov-report=xml --cov=idecomp ./tests` with codecov upload. Matrix: 3.10, 3.11, 3.12. Installs `--extra test`. + - **docs**: Runs `uv run sphinx-build -M html docs/source docs/build`. Single Python version (3.12). Installs `--extra docs`. +2. Keep the workflow name as `tests` (badge URLs reference this name) +3. Keep triggers as push/PR to `main` +4. Each job: checkout, install uv, install python, `uv sync --extra `, run tool +5. The `test` job keeps the codecov upload step with the existing token reference + +### Inputs/Props + +- Current file: `/home/rogerio/git/idecomp/.github/workflows/main.yml` (46 lines) + +### Outputs/Behavior + +- 4 jobs appear in the GitHub Actions UI, running in parallel +- Each job fails independently without blocking others +- Total CI wall-clock time is reduced to the longest single job + +### Error Handling + +- Each job fails independently; a lint failure does not prevent test results from appearing +- Codecov token is only referenced in the test job + +## Acceptance Criteria + +- [ ] Given the updated `main.yml`, when parsing the YAML, then exactly 4 top-level keys exist under `jobs`: `lint`, `typecheck`, `test`, `docs` +- [ ] Given the `lint` job definition, when inspecting its steps, then it runs `uv sync --extra lint` (not `--all-extras`) and executes both `uv run ruff check ./idecomp` and `uv run ruff format --check ./idecomp` +- [ ] Given the `typecheck` job definition, when inspecting its steps, then it runs `uv sync --extra lint` and executes `uv run mypy ./idecomp` +- [ ] Given the `test` job definition, when inspecting its matrix, then `python-version` includes `["3.10", "3.11", "3.12"]` and the job runs `uv sync --extra test` and includes the codecov upload step +- [ ] Given the `docs` job definition, when inspecting its steps, then it runs `uv sync --extra docs` and executes `uv run sphinx-build -M html docs/source docs/build` + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/.github/workflows/main.yml` +2. Remove the existing `test` job entirely +3. Add 4 new jobs: `lint`, `typecheck`, `test`, `docs` +4. For each job, use the common preamble: `actions/checkout@v4`, `astral-sh/setup-uv@v3`, `uv python install `, `uv sync --extra ` +5. The `test` job gets the matrix strategy and codecov step from the old job +6. The `lint` and `typecheck` jobs use Python 3.12 (no matrix) +7. The `docs` job uses Python 3.12 (no matrix) + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/.github/workflows/main.yml` + +### Patterns to Follow + +- Match the inewave `main.yml` 4-job structure +- Use `uv sync --extra ` instead of `--all-extras --dev` for each job + +### Pitfalls to Avoid + +- Do NOT rename the workflow from `tests` (the README badge URL references `actions/workflows/main.yml/badge.svg`) +- Do NOT remove the codecov token secret reference; it must stay in the `test` job +- Do NOT add job dependencies between the 4 jobs; they must run in parallel +- The docs job needs `--extra docs` which pulls in sphinx, furo, sphinx-gallery, etc. -- it does NOT need test or lint deps + +## Testing Requirements + +### Unit Tests + +- Not applicable (YAML configuration) + +### Integration Tests + +- Validate the YAML structure is valid (no syntax errors) +- Verify each job references the correct extras group + +### E2E Tests + +- Push a PR and verify 4 separate jobs appear in the GitHub Actions UI + +## Dependencies + +- **Blocked By**: ticket-001-modernize-pyproject-toml.md +- **Blocks**: ticket-013-expand-readme.md (needs final workflow name for badge URL) + +## Effort Estimate + +**Points**: 3 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md new file mode 100644 index 0000000..3fa978c --- /dev/null +++ b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md @@ -0,0 +1,124 @@ +# ticket-003 Migrate Docs Deployment to Official GitHub Pages Actions + +## Context + +### Background + +The current `docs.yml` workflow uses `peaceiris/actions-gh-pages@v3`, which is deprecated and no longer maintained. GitHub now provides official actions for Pages deployment: `actions/upload-pages-artifact@v3` and `actions/deploy-pages@v4`. The inewave overhaul successfully migrated to these official actions. + +### Relation to Epic + +Independent ticket within Epic 1. Does not depend on the pyproject.toml changes (it only changes the deployment mechanism, not what gets installed). + +### Current State + +File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml` + +- Triggers: push to `main`, workflow_dispatch +- Single `docs` job: checkout, install uv, install python, `uv sync --all-extras --dev`, pytest, sphinx-build, deploy with `peaceiris/actions-gh-pages@v3` +- Deploys to `gh-pages` branch with `force_orphan: true` +- Uses `${{ secrets.GITHUB_TOKEN }}` + +## Specification + +### Requirements + +1. Replace `peaceiris/actions-gh-pages@v3` deployment with official GitHub Pages actions +2. Add top-level `permissions` block: `pages: write`, `id-token: write` +3. Add `concurrency` group to prevent concurrent deployments: `group: "pages"`, `cancel-in-progress: false` +4. Split into 2 jobs: + - **build**: checkout, install uv, install python, `uv sync --extra docs`, sphinx-build, `actions/upload-pages-artifact@v3` pointing to `docs/build/html` + - **deploy**: depends on build, uses `actions/deploy-pages@v4`, environment `github-pages` +5. Remove the pytest step from the docs workflow (tests run in `main.yml`) +6. Remove the `--all-extras --dev` install; use `--extra docs` instead +7. Keep triggers: push to `main`, workflow_dispatch + +### Inputs/Props + +- Current file: `/home/rogerio/git/idecomp/.github/workflows/docs.yml` (33 lines) + +### Outputs/Behavior + +- Documentation deploys to GitHub Pages using the official deployment mechanism +- Deployment uses OIDC token (id-token: write) instead of GITHUB_TOKEN for Pages +- Concurrent pushes to main do not cause race conditions in deployment + +### Error Handling + +- If sphinx-build fails, the upload-pages-artifact step is skipped and deploy job is skipped +- The concurrency group ensures only one deployment runs at a time + +## Acceptance Criteria + +- [ ] Given the updated `docs.yml`, when parsing the YAML, then `peaceiris/actions-gh-pages` does not appear anywhere in the file +- [ ] Given the updated `docs.yml`, when inspecting the top-level keys, then `permissions` contains `pages: write` and `id-token: write`, and `concurrency` has `group: "pages"` with `cancel-in-progress: false` +- [ ] Given the `build` job, when inspecting its steps, then it runs `uv sync --extra docs` (not `--all-extras`) and uses `actions/upload-pages-artifact@v3` with `path: docs/build/html` +- [ ] Given the `deploy` job, when inspecting its configuration, then it has `needs: build`, uses `actions/deploy-pages@v4`, and sets `environment: name: github-pages` +- [ ] Given the updated `docs.yml`, when searching for `pytest`, then no pytest step exists in any job + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/.github/workflows/docs.yml` +2. Add top-level `permissions` and `concurrency` blocks after the `on` trigger +3. Rename the existing `docs` job to `build` and modify its steps: + - Keep: checkout, install uv, install python + - Change: `uv sync --all-extras --dev` to `uv sync --extra docs` + - Remove: pytest step + - Keep: sphinx-build step + - Replace: peaceiris action with `actions/upload-pages-artifact@v3` with `path: docs/build/html` +4. Add a new `deploy` job: + ```yaml + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + ``` + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/.github/workflows/docs.yml` + +### Patterns to Follow + +- Match the inewave `docs.yml` structure with build + deploy jobs +- Use the official GitHub Pages actions pattern from GitHub's documentation + +### Pitfalls to Avoid + +- Do NOT keep the pytest step; tests belong in `main.yml` only +- Do NOT forget the `id-token: write` permission; it is required for the official Pages deployment +- Do NOT set `cancel-in-progress: true`; this could cancel an active deployment mid-way +- The `environment: name: github-pages` must match the GitHub Pages environment configured in the repo settings + +## Testing Requirements + +### Unit Tests + +- Not applicable (YAML configuration) + +### Integration Tests + +- Validate YAML syntax +- Verify no references to `peaceiris` remain + +### E2E Tests + +- After merging to main, verify docs deploy successfully to the GitHub Pages URL + +## Dependencies + +- **Blocked By**: None (independent within Epic 1) +- **Blocks**: None + +## Effort Estimate + +**Points**: 2 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md new file mode 100644 index 0000000..474a1c0 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md @@ -0,0 +1,139 @@ +# ticket-004 Create Tag-Triggered Release Workflow + +## Context + +### Background + +The current `publish.yml` triggers on GitHub release creation events. The inewave overhaul moved to a tag-triggered workflow that validates the tag version against `__version__`, creates a GitHub release automatically, and publishes to PyPI. idecomp should adopt the same pattern. + +### Relation to Epic + +Fourth ticket in Epic 1. Depends on ticket-001 for dependency groups (the release workflow installs `--extra test` and `--extra lint` for validation steps). + +### Current State + +File: `/home/rogerio/git/idecomp/.github/workflows/publish.yml` + +- Name: `deploy` +- Trigger: `release: types: [created]` +- Single job `build-and-publish`: checkout, install uv, install python 3.11, `uv sync --all-extras --dev`, pytest, mypy, ruff check, uv build, pypa/gh-action-pypi-publish +- Uses trusted publishing (`id-token: write`) +- Environment: `pypi` with URL `https://pypi.org/p/idecomp` + +## Specification + +### Requirements + +1. Rename the file from `publish.yml` to `release.yml` +2. Rename the workflow from `deploy` to `release` +3. Change trigger from `release: types: [created]` to `push: tags: ["v*"]` +4. Add a version validation step that: + - Extracts version from `idecomp/__init__.py` using `grep` + regex (NOT `exec()` or `python -c "import idecomp"`) + - Extracts tag version from `GITHUB_REF` (strips `refs/tags/v` prefix) + - Compares them and fails if they do not match +5. Keep the test, mypy, and ruff validation steps but use `--extra test` and `--extra lint` instead of `--all-extras` +6. After successful build and publish, create a GitHub release using `gh release create $TAG --generate-notes` +7. Keep the trusted publishing setup (id-token: write, pypi environment) +8. Update Python version to 3.12 + +### Inputs/Props + +- Current file: `/home/rogerio/git/idecomp/.github/workflows/publish.yml` (41 lines) +- Version source: `/home/rogerio/git/idecomp/idecomp/__init__.py` line `__version__ = "1.8.2"` + +### Outputs/Behavior + +- Pushing a tag `v1.8.3` triggers the workflow +- Version validation fails if tag `v1.8.3` does not match `__version__ = "1.8.3"` in `__init__.py` +- On success: tests pass, package is built, published to PyPI, and a GitHub release is created + +### Error Handling + +- Version mismatch between tag and `__init__.py` fails the workflow early (before build/publish) +- Test/lint failures prevent package publication + +## Acceptance Criteria + +- [ ] Given the repository, when listing files in `.github/workflows/`, then `release.yml` exists and `publish.yml` does not exist +- [ ] Given the `release.yml`, when inspecting the `on` trigger, then it is `push: tags: ["v*"]` and no `release` trigger exists +- [ ] Given the version validation step, when inspecting the shell commands, then it uses `grep -oP '__version__\s*=\s*"\K[^"]+'` (or equivalent regex) on `idecomp/__init__.py` and does NOT use `exec()`, `eval()`, or `python -c "import idecomp"` +- [ ] Given the `release.yml`, when inspecting install steps, then it uses `uv sync --extra test --extra lint` (not `--all-extras --dev`) +- [ ] Given the workflow, when inspecting the final steps, then after `pypa/gh-action-pypi-publish`, there is a step running `gh release create` with the tag and `--generate-notes` + +## Implementation Guide + +### Suggested Approach + +1. Rename `/home/rogerio/git/idecomp/.github/workflows/publish.yml` to `release.yml` (use `git mv`) +2. Update the workflow `name` to `release` +3. Replace the `on` trigger: + ```yaml + on: + push: + tags: + - "v*" + ``` +4. Add a version validation step early in the job: + ```yaml + - name: Validate version + run: | + PKG_VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' idecomp/__init__.py) + TAG_VERSION=${GITHUB_REF#refs/tags/v} + if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then + echo "Version mismatch: __init__.py=$PKG_VERSION tag=$TAG_VERSION" + exit 1 + fi + ``` +5. Change install step to `uv sync --extra test --extra lint` +6. Update Python to 3.12: `uv python install 3.12` +7. Add GitHub release creation step after PyPI publish: + ```yaml + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create ${{ github.ref_name }} --generate-notes + ``` +8. Add `contents: write` to permissions (needed for `gh release create`) + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/.github/workflows/publish.yml` (rename to `release.yml` and rewrite) + +### Patterns to Follow + +- Match the inewave `release.yml` structure +- Use grep + regex for version extraction (never exec/eval) +- Trusted publishing with `pypa/gh-action-pypi-publish@release/v1` + +### Pitfalls to Avoid + +- Do NOT use `exec(open('__init__.py').read())` -- it fails because `__init__.py` has `from . import decomp` (relative import) +- Do NOT use `python -c "from idecomp import __version__"` -- it requires the package to be importable with all deps +- Do NOT forget `contents: write` permission for `gh release create` +- The `startsWith(github.ref, 'refs/tags')` guards on build/publish steps can be removed since the trigger already ensures this + +## Testing Requirements + +### Unit Tests + +- Not applicable (YAML configuration) + +### Integration Tests + +- Validate YAML syntax +- Verify the grep regex correctly extracts version from `idecomp/__init__.py` + +### E2E Tests + +- Create a test tag on a branch to verify the workflow triggers (can be deleted after) + +## Dependencies + +- **Blocked By**: ticket-001-modernize-pyproject-toml.md +- **Blocks**: None + +## Effort Estimate + +**Points**: 3 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md new file mode 100644 index 0000000..be573fc --- /dev/null +++ b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md @@ -0,0 +1,125 @@ +# ticket-005 Add Pre-commit Hooks Configuration + +## Context + +### Background + +idecomp has no `.pre-commit-config.yaml`. Developers must manually run ruff and mypy before committing. The inewave overhaul added pre-commit hooks for ruff (check + format) and mypy (manual stage due to cfinterface issues), and idecomp should adopt the same configuration. + +### Relation to Epic + +Fifth and final ticket of Epic 1. Depends on ticket-001 for ruff and mypy configuration in `pyproject.toml`. + +### Current State + +- No `.pre-commit-config.yaml` exists in the repository +- ruff and mypy are listed as dev dependencies (will be in `lint` group after ticket-001) +- mypy has known issues with cfinterface (missing type stubs, `warn_return_any` conflicts) + +## Specification + +### Requirements + +1. Create `.pre-commit-config.yaml` at repository root with: + - **ruff-pre-commit** hooks (from `https://github.com/astral-sh/ruff-pre-commit`): + - `ruff` hook (linting/check) + - `ruff-format` hook (formatting) + - **mypy** hook as a local hook: + - `language: system` (uses the project's installed mypy, not a separate venv) + - `entry: uv run mypy ./idecomp` + - `pass_filenames: false` (runs on entire package) + - `stages: [manual]` (not run on every commit due to cfinterface import issues; invoked explicitly with `pre-commit run --hook-stage manual`) +2. Use a recent ruff-pre-commit rev (e.g., `v0.8.6` or latest stable) + +### Inputs/Props + +- No existing file to modify; creating new file at `/home/rogerio/git/idecomp/.pre-commit-config.yaml` + +### Outputs/Behavior + +- `pre-commit run --all-files` executes ruff check and ruff format on all files +- `pre-commit run --hook-stage manual --all-files` additionally runs mypy +- ruff hooks use the pre-commit framework's own ruff installation (fast, isolated) +- mypy hook uses the system/project-installed mypy via `uv run` + +### Error Handling + +- If ruff finds formatting issues, the hook auto-fixes them and fails the commit (user re-stages and re-commits) +- If mypy fails due to cfinterface issues, it only affects manual runs, not regular commits + +## Acceptance Criteria + +- [ ] Given the repository root, when listing files, then `.pre-commit-config.yaml` exists at `/home/rogerio/git/idecomp/.pre-commit-config.yaml` +- [ ] Given the `.pre-commit-config.yaml`, when inspecting the ruff-pre-commit repo entry, then it contains hooks with ids `ruff` and `ruff-format` +- [ ] Given the `.pre-commit-config.yaml`, when inspecting the local hooks, then there is a hook with `id: mypy`, `language: system`, `entry: uv run mypy ./idecomp`, `pass_filenames: false`, and `stages: [manual]` +- [ ] Given a clean checkout with pre-commit installed, when running `pre-commit run --all-files`, then the ruff and ruff-format hooks execute (mypy does not execute because it is manual stage) + +## Implementation Guide + +### Suggested Approach + +1. Create `/home/rogerio/git/idecomp/.pre-commit-config.yaml` with the following structure: + + ```yaml + repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.6 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + + - repo: local + hooks: + - id: mypy + name: mypy + entry: uv run mypy ./idecomp + language: system + types: [python] + pass_filenames: false + stages: [manual] + ``` + +2. Verify the file is valid YAML + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/.pre-commit-config.yaml` (new file) + +### Patterns to Follow + +- Match the inewave `.pre-commit-config.yaml` structure exactly +- Use `language: system` for mypy to avoid installing it in an isolated venv (it needs access to project deps) +- Use `stages: [manual]` to prevent mypy from running on every commit + +### Pitfalls to Avoid + +- Do NOT set mypy stages to `[pre-commit]` or omit stages -- cfinterface issues will cause every commit to fail +- Do NOT use `language: python` for mypy -- it installs mypy in an isolated venv without project deps +- Do NOT forget `pass_filenames: false` for mypy -- it should type-check the whole package, not individual files +- Do NOT forget `args: [--fix]` on the ruff hook -- without it, ruff only reports but does not fix issues + +## Testing Requirements + +### Unit Tests + +- Not applicable (configuration file) + +### Integration Tests + +- Validate YAML syntax of the new file +- Run `pre-commit run --all-files` and verify ruff hooks execute + +### E2E Tests + +- Not applicable + +## Dependencies + +- **Blocked By**: ticket-001-modernize-pyproject-toml.md +- **Blocks**: ticket-014-create-contributing.md (CONTRIBUTING references pre-commit setup) + +## Effort Estimate + +**Points**: 2 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md new file mode 100644 index 0000000..1c73c4c --- /dev/null +++ b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md @@ -0,0 +1,31 @@ +# Epic 2: Sphinx Modernization + +## Goal + +Replace the sphinx-rtd-theme with Furo for a modern documentation appearance with dark mode support, and ensure all sphinx-gallery examples build correctly with the new theme. + +## Scope + +- Migrate Sphinx theme from sphinx-rtd-theme to Furo +- Configure dark mode with monokai pygments style +- Update conf.py extensions and theme options +- Review and fix sphinx-gallery example scripts for compatibility + +## Tickets + +| Order | Ticket | Title | Points | +| ----- | ---------- | ----------------------------------------------------- | ------ | +| 1 | ticket-006 | Migrate Sphinx Theme to Furo | 3 | +| 2 | ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | 2 | + +## Dependencies + +- ticket-006 depends on ticket-001 (needs docs dependency group with furo instead of sphinx-rtd-theme) +- ticket-007 depends on ticket-006 (needs Furo theme configured) + +## Completion Criteria + +- `uv run sphinx-build -M html docs/source docs/build` succeeds with Furo theme +- Documentation site renders with dark mode toggle +- All 4 example scripts in `/examples/` build successfully in the gallery +- No sphinx-rtd-theme references remain in the codebase diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md new file mode 100644 index 0000000..51bb6fd --- /dev/null +++ b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md @@ -0,0 +1,126 @@ +# ticket-006 Migrate Sphinx Theme to Furo + +## Context + +### Background + +idecomp's documentation uses `sphinx-rtd-theme`, which lacks dark mode and has a dated appearance. The inewave overhaul migrated to Furo, a modern Sphinx theme with built-in dark/light mode toggle and clean typography. idecomp should adopt the same theme for consistency across sibling projects. + +### Relation to Epic + +First ticket of Epic 2. Depends on ticket-001 which replaces `sphinx-rtd-theme` with `furo` in the docs dependency group. Blocks ticket-007 (gallery examples must be verified after theme change). + +### Current State + +File: `/home/rogerio/git/idecomp/docs/source/conf.py` (129 lines) + +- `html_theme = "sphinx_rtd_theme"` +- `extensions` list includes `"sphinx_rtd_theme"` +- `html_theme_options` configured for RTD: `logo_only`, `collapse_navigation`, `sticky_navigation`, `navigation_depth`, `includehidden`, `titles_only` +- `pygments_style = "sphinx"` +- `html_logo = "_static/logo_idecomp_svg.svg"` +- Existing extensions: autosummary, autodoc, intersphinx, mathjax, viewcode, githubpages, sphinx_gallery, numpydoc + +## Specification + +### Requirements + +1. Replace `html_theme = "sphinx_rtd_theme"` with `html_theme = "furo"` +2. Remove `"sphinx_rtd_theme"` from the `extensions` list (Furo is not loaded as an extension) +3. Replace `html_theme_options` with Furo-specific options: + ```python + html_theme_options = { + "light_css_variables": { + "color-brand-primary": "#2962ff", + "color-brand-content": "#2962ff", + }, + "dark_css_variables": { + "color-brand-primary": "#5c8aff", + "color-brand-content": "#5c8aff", + }, + "sidebar_hide_name": True, + } + ``` +4. Change `pygments_style` to `"friendly"` and add `pygments_dark_style = "monokai"` for dark mode code blocks +5. Keep `html_logo` pointing to the existing SVG logo +6. Keep all other extensions unchanged (autosummary, autodoc, intersphinx, mathjax, viewcode, githubpages, sphinx_gallery, numpydoc) +7. Remove the `from typing import List` import if no longer needed (check if `exclude_patterns: List[str]` can use `list[str]` since Python >= 3.10) + +### Inputs/Props + +- File: `/home/rogerio/git/idecomp/docs/source/conf.py` + +### Outputs/Behavior + +- `uv run sphinx-build -M html docs/source docs/build` produces documentation with Furo theme +- The site has a dark/light mode toggle button in the header +- Code blocks use friendly pygments in light mode and monokai in dark mode +- The idecomp SVG logo appears in the sidebar + +### Error Handling + +- If Furo is not installed, sphinx-build fails with a clear error about missing theme +- If the logo path is wrong, Furo shows a warning but builds successfully + +## Acceptance Criteria + +- [ ] Given the updated `conf.py`, when searching for `sphinx_rtd_theme`, then the string does not appear anywhere in the file (not in `extensions`, not in `html_theme`, not in imports) +- [ ] Given the updated `conf.py`, when inspecting `html_theme`, then it equals `"furo"` +- [ ] Given the updated `conf.py`, when inspecting `pygments_style` and `pygments_dark_style`, then they equal `"friendly"` and `"monokai"` respectively +- [ ] Given the updated `conf.py`, when inspecting `html_theme_options`, then it contains `"dark_css_variables"` and `"sidebar_hide_name"` keys (Furo-specific options, not RTD options) +- [ ] Given a virtual environment with the docs extras installed, when running `uv run sphinx-build -M html docs/source docs/build`, then the build completes without errors and `docs/build/html/index.html` contains `furo` in its HTML source + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/docs/source/conf.py` +2. Remove `"sphinx_rtd_theme"` from the `extensions` list (line 49) +3. Change `html_theme = "sphinx_rtd_theme"` to `html_theme = "furo"` (line 89) +4. Replace the entire `html_theme_options` dict with Furo options +5. Change `pygments_style = "sphinx"` to `pygments_style = "friendly"` (line 81) +6. Add `pygments_dark_style = "monokai"` after the pygments_style line +7. Optionally update `exclude_patterns: List[str] = []` to `exclude_patterns: list[str] = []` and remove the `from typing import List` import +8. Run `uv run sphinx-build -M html docs/source docs/build` to verify + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/docs/source/conf.py` + +### Patterns to Follow + +- Match the inewave Furo configuration for consistency +- Keep all existing extensions except `sphinx_rtd_theme` +- Furo does not need to be listed in extensions; it is configured purely via `html_theme` + +### Pitfalls to Avoid + +- Do NOT remove `sphinx.ext.githubpages` from extensions (it creates the `.nojekyll` file needed for GitHub Pages) +- Do NOT remove `html_logo` -- Furo supports logos via the same `html_logo` config +- Do NOT add Furo to extensions -- it is a theme, not an extension; adding it causes a warning +- The `html_theme_options` keys are completely different between RTD and Furo; do not try to adapt the old keys + +## Testing Requirements + +### Unit Tests + +- Not applicable (configuration file) + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify it exits with code 0 +- Verify `docs/build/html/index.html` exists and references Furo CSS + +### E2E Tests + +- Open the built docs locally and verify the dark mode toggle works + +## Dependencies + +- **Blocked By**: ticket-001-modernize-pyproject-toml.md (furo must be in the docs dependency group) +- **Blocks**: ticket-007-update-sphinx-gallery-examples.md + +## Effort Estimate + +**Points**: 3 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md new file mode 100644 index 0000000..37a7810 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md @@ -0,0 +1,135 @@ +# ticket-007 Update Sphinx-Gallery Examples for Furo Compatibility + +## Context + +### Background + +idecomp has 4 example scripts in `/examples/` that are rendered by sphinx-gallery into the documentation. After migrating to the Furo theme (ticket-006), these examples must be verified to build correctly. The inewave overhaul revealed that sphinx-gallery can fail due to cfinterface import errors during example execution, and example scripts may use deprecated API patterns. + +### Relation to Epic + +Second and final ticket of Epic 2. Depends on ticket-006 (Furo must be configured before verifying gallery builds). + +### Current State + +Directory: `/home/rogerio/git/idecomp/examples/` + +- `plot_dadger.py` - Example reading/plotting dadger data +- `plot_dec_oper_sist.py` - Example reading/plotting dec_oper_sist data +- `plot_edit_dadger.py` - Example editing dadger data +- `plot_relato.py` - Example reading/plotting relato data +- `README.rst` - Gallery index page +- `decomp/` - Subdirectory (likely contains mock data for examples) + +Sphinx-gallery config in `conf.py`: + +```python +sphinx_gallery_conf = { + "examples_dirs": "../../examples", + "gallery_dirs": "examples", + "backreferences_dir": "gen_modules/generated", +} +``` + +## Specification + +### Requirements + +1. Review each of the 4 example scripts for: + - Deprecated API patterns (e.g., `le_arquivo`/`escreve_arquivo` which were deprecated in v1.0.0) + - Import errors that would cause sphinx-gallery to fail during build + - Plotly renderer compatibility with sphinx-gallery (the `pio.renderers.default = "sphinx_gallery"` is already set in conf.py) +2. Run `uv run sphinx-build -M html docs/source docs/build` and verify the gallery builds without errors +3. If any example script fails: + - Fix import paths or API usage + - If cfinterface import errors prevent execution, add `sphinx_gallery_conf["abort_on_example_error"] = False` to `conf.py` as a fallback +4. Verify the gallery renders correctly with Furo theme (thumbnail grid, example pages) +5. Update `README.rst` in the examples directory if its content references sphinx-rtd-theme or outdated information + +### Inputs/Props + +- Example scripts: `/home/rogerio/git/idecomp/examples/plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py` +- Gallery config: `/home/rogerio/git/idecomp/docs/source/conf.py` (sphinx_gallery_conf section) +- Example data: `/home/rogerio/git/idecomp/examples/decomp/` + +### Outputs/Behavior + +- All 4 example scripts execute during sphinx-build without errors (or gracefully handle import failures) +- The gallery page at `examples/index.html` shows thumbnails for all examples +- Individual example pages render correctly with Furo theme + +### Error Handling + +- If cfinterface import errors prevent example execution, set `abort_on_example_error = False` to allow partial gallery builds +- If plotly rendering fails in sphinx-gallery, add `plot_gallery = False` for affected examples or switch to matplotlib-only plots + +## Acceptance Criteria + +- [ ] Given the example scripts in `/home/rogerio/git/idecomp/examples/`, when inspecting each script, then no deprecated API patterns (`le_arquivo`, `escreve_arquivo`) are used +- [ ] Given a virtual environment with docs extras, when running `uv run sphinx-build -M html docs/source docs/build`, then the sphinx-gallery extension produces output in `docs/build/html/examples/` without errors (exit code 0) +- [ ] Given the built documentation, when inspecting `docs/build/html/examples/index.html`, then it contains thumbnail entries for all 4 example scripts +- [ ] Given the `examples/README.rst`, when inspecting its content, then it does not reference `sphinx-rtd-theme` or any removed/outdated concepts + +## Implementation Guide + +### Suggested Approach + +1. Read each example script in `/home/rogerio/git/idecomp/examples/`: + - `plot_dadger.py` + - `plot_dec_oper_sist.py` + - `plot_edit_dadger.py` + - `plot_relato.py` +2. Check for deprecated API usage (`le_arquivo`, `escreve_arquivo`) and fix if found +3. Check that imports resolve correctly (the examples likely import from `idecomp.decomp`) +4. Run the full sphinx-build: `uv run sphinx-build -M html docs/source docs/build` +5. If gallery errors occur: + - Check error messages for cfinterface import issues + - If needed, add `"abort_on_example_error": False` to `sphinx_gallery_conf` in `conf.py` +6. Inspect the generated gallery HTML to verify thumbnails and example pages render +7. Review and update `examples/README.rst` if needed + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/examples/plot_dadger.py` (if deprecated APIs found) +- `/home/rogerio/git/idecomp/examples/plot_dec_oper_sist.py` (if deprecated APIs found) +- `/home/rogerio/git/idecomp/examples/plot_edit_dadger.py` (if deprecated APIs found) +- `/home/rogerio/git/idecomp/examples/plot_relato.py` (if deprecated APIs found) +- `/home/rogerio/git/idecomp/docs/source/conf.py` (only if `abort_on_example_error` needed) + +### Patterns to Follow + +- Preserve existing example functionality; only fix deprecated patterns +- Use the current idecomp API (class-based file reading, not deprecated `le_arquivo`/`escreve_arquivo`) +- Follow the plotly sphinx-gallery renderer pattern already configured in `conf.py` + +### Pitfalls to Avoid + +- Do NOT rewrite examples entirely; make minimal changes to fix compatibility +- Do NOT remove the `pio.renderers.default = "sphinx_gallery"` line from `conf.py`; it is required for plotly charts in gallery +- Do NOT change `sphinx_gallery_conf["examples_dirs"]` or `"gallery_dirs"` paths +- cfinterface import errors at example execution time are a known issue; use `abort_on_example_error = False` rather than trying to fix cfinterface + +## Testing Requirements + +### Unit Tests + +- Not applicable + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0 +- Verify `docs/build/html/examples/index.html` exists and is non-empty + +### E2E Tests + +- Open the built gallery page in a browser and verify thumbnails and example pages render + +## Dependencies + +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md +- **Blocks**: None (Epic 3 depends on Epic 2 being complete, but not specifically on this ticket) + +## Effort Estimate + +**Points**: 2 +**Confidence**: Medium (cfinterface import issues may require investigation) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md b/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md new file mode 100644 index 0000000..f470775 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md @@ -0,0 +1,36 @@ +# Epic 3: Documentation Content Expansion + +## Goal + +Expand the idecomp documentation with three new guide pages (architecture, FAQ, performance), improve the API reference with autosummary blocks, and update the index.rst toctree. All content in Brazilian Portuguese. + +## Scope + +- Create `arquitetura.rst` page describing idecomp's package structure, cfinterface framework, and data flow +- Create `faq.rst` page with 15+ frequently asked questions about DECOMP file handling +- Create `desempenho.rst` page with performance tips, import times, batch optimization +- Add autosummary blocks to module index RSTs for decomp/ and libs/ +- Update `index.rst` toctree with new "Guias" section + +## Tickets + +| Order | Ticket | Title | Points | +| ----- | ---------- | --------------------------------------------- | ------ | +| 1 | ticket-008 | Create Architecture Documentation Page | 3 | +| 2 | ticket-009 | Create FAQ Documentation Page | 3 | +| 3 | ticket-010 | Create Performance Guide Page | 3 | +| 4 | ticket-011 | Improve API Reference with Autosummary Blocks | 2 | +| 5 | ticket-012 | Update index.rst Toctree with Guias Section | 1 | + +## Dependencies + +- ticket-008 through ticket-011 are independent of each other +- ticket-012 depends on ticket-008, ticket-009, ticket-010 (needs the new pages to exist) +- All tickets depend on Epic 2 completion (Furo theme must be in place) + +## Completion Criteria + +- Three new .rst files exist in `docs/source/guias/` +- Sphinx build succeeds with no warnings for the new pages +- API reference pages show autosummary tables with class/method listings +- index.rst toctree includes the "Guias" section diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md new file mode 100644 index 0000000..a8ae230 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md @@ -0,0 +1,32 @@ +# ticket-008 Create Architecture Documentation Page + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Create a new `arquitetura.rst` page in the documentation that describes idecomp's package structure, the cfinterface framework integration, the decomp/ and libs/ module organization, lazy import patterns, and the data flow from DECOMP files to pandas DataFrames. All content in Brazilian Portuguese. This page helps new users and contributors understand how idecomp is architected before diving into the API reference. + +## Anticipated Scope + +- **Files likely to be modified**: + - `docs/source/guias/arquitetura.rst` (new file) + - `docs/source/index.rst` (adding to toctree, but done in ticket-012) +- **Key decisions needed**: + - Whether to create a `docs/source/guias/` directory or place the file in `docs/source/geral/` + - Whether to include UML-style diagrams or keep it text-only + - How deeply to describe cfinterface internals vs. linking to cfinterface docs +- **Open questions**: + - Does the `idecomp/decomp/__init__.py` eager import pattern (not lazy) need to be documented as-is, or is a migration to lazy imports planned? + - Should the architecture page include the `idecomp/config.py` module's role? + - What level of detail for the `idecomp/decomp/modelos/` subdirectory? + +## Dependencies + +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (Furo theme must be active for consistent docs build) +- **Blocks**: ticket-012-update-index-toctree.md + +## Effort Estimate + +**Points**: 3 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md new file mode 100644 index 0000000..7377a01 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md @@ -0,0 +1,31 @@ +# ticket-009 Create FAQ Documentation Page + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Create a new `faq.rst` page with at least 15 frequently asked questions about using idecomp to handle DECOMP files. The FAQ covers installation issues, common reading/writing patterns, DataFrame manipulation, error handling, and compatibility with different DECOMP versions. All content in Brazilian Portuguese, organized into 5 thematic sections. + +## Anticipated Scope + +- **Files likely to be modified**: + - `docs/source/guias/faq.rst` (new file) +- **Key decisions needed**: + - The 5 section categories (e.g., Instalacao, Leitura de Arquivos, Escrita de Arquivos, DataFrames, Erros Comuns) + - Which specific DECOMP file formats to highlight in FAQ examples + - Whether to reference specific class names or keep questions generic +- **Open questions**: + - Are there known user pain points from GitHub Issues that should be prioritized? + - Should the FAQ include questions about cfinterface directly or redirect to cfinterface docs? + - Should the FAQ reference the `tests/mocks/` directory structure for users who want example files? + +## Dependencies + +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md +- **Blocks**: ticket-012-update-index-toctree.md + +## Effort Estimate + +**Points**: 3 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md new file mode 100644 index 0000000..4f418fb --- /dev/null +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md @@ -0,0 +1,31 @@ +# ticket-010 Create Performance Guide Page + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Create a new `desempenho.rst` page documenting performance characteristics and optimization strategies for idecomp. Covers import times, file read/write times for common DECOMP files, batch processing patterns, memory usage considerations, and benchmarking guidance. All content in Brazilian Portuguese. + +## Anticipated Scope + +- **Files likely to be modified**: + - `docs/source/guias/desempenho.rst` (new file) +- **Key decisions needed**: + - Whether to include actual benchmark numbers or describe how to run benchmarks + - Which DECOMP files to profile (dadger, relato, dec_oper_sist are likely candidates) + - Whether to mention the eager import pattern in `idecomp/decomp/__init__.py` as a performance consideration +- **Open questions**: + - Does idecomp have a `benchmarks/` directory? (Not observed in the repo root; may need to be created or the guide refers to ad-hoc profiling) + - Should the guide include comparison with raw file parsing (without idecomp)? + - Should lazy import migration be recommended as a performance improvement? + +## Dependencies + +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md +- **Blocks**: ticket-012-update-index-toctree.md + +## Effort Estimate + +**Points**: 3 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md new file mode 100644 index 0000000..ab63c58 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md @@ -0,0 +1,34 @@ +# ticket-011 Improve API Reference with Autosummary Blocks + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Enhance the API reference pages in `docs/source/referencia/decomp/index.rst` and `docs/source/referencia/libs/index.rst` with autosummary directive blocks that generate method/attribute tables for each documented class. Currently these pages are plain toctree listings; autosummary adds navigable tables showing the public interface at a glance. + +## Anticipated Scope + +- **Files likely to be modified**: + - `docs/source/referencia/decomp/index.rst` (add autosummary blocks) + - `docs/source/referencia/libs/index.rst` (add autosummary blocks) + - Individual `.rst` files in `docs/source/referencia/decomp/arquivos/` (may need autosummary directives per class) + - `docs/source/conf.py` (may need autosummary template customization) +- **Key decisions needed**: + - Whether to use `.. autosummary::` at the module index level or at each individual class page + - Whether to create custom autosummary templates in `_templates/` + - The canonical public class list for decomp/ (the 34 classes in `idecomp/decomp/__init__.py`) +- **Open questions**: + - Does the existing `autosummary_generate = True` in `conf.py` already generate stubs? + - Should autosummary show methods and attributes, or just classes? + - Are there autosummary templates from inewave that can be reused? + +## Dependencies + +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md +- **Blocks**: None + +## Effort Estimate + +**Points**: 2 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-012-update-index-toctree.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-012-update-index-toctree.md new file mode 100644 index 0000000..b23838c --- /dev/null +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-012-update-index-toctree.md @@ -0,0 +1,31 @@ +# ticket-012 Update index.rst Toctree with Guias Section + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Update `docs/source/index.rst` to add a new "Guias" toctree section between the "Geral" and "Referencia" sections, linking to the architecture (`arquitetura.rst`), performance (`desempenho.rst`), and FAQ (`faq.rst`) pages created in tickets 008-010. This makes the new guide pages discoverable in the documentation sidebar. + +## Anticipated Scope + +- **Files likely to be modified**: + - `docs/source/index.rst` (add new toctree section) + - `docs/source/guias/` directory may need to be created if not done by earlier tickets +- **Key decisions needed**: + - The exact placement of the "Guias" section in index.rst (between Geral and Referencia) + - The order of pages within the Guias section (architecture first, then performance, then FAQ) + - Whether to set a different maxdepth for the Guias section +- **Open questions**: + - Should the "Geral" section's contribuicao.rst link be moved to the Guias section, or kept in Geral? + - Should the Guias section caption be "Guias" or "Guias e Tutoriais"? + +## Dependencies + +- **Blocked By**: ticket-008-create-architecture-page.md, ticket-009-create-faq-page.md, ticket-010-create-performance-guide.md +- **Blocks**: None + +## Effort Estimate + +**Points**: 1 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/00-epic-overview.md b/plans/infra-docs-overhaul/epic-04-repository-polish/00-epic-overview.md new file mode 100644 index 0000000..ca20dc1 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/00-epic-overview.md @@ -0,0 +1,35 @@ +# Epic 4: Repository Polish + +## Goal + +Polish the repository's public-facing files: expand the README with badges and sections, create a CONTRIBUTING.md, reformat the CHANGELOG to Keep a Changelog standard, and update installation documentation. + +## Scope + +- Expand README.md with badges (CI, codecov, PyPI, license, docs), structured sections in Portuguese +- Create CONTRIBUTING.md with environment setup, quality tools, testing, code conventions, PR workflow +- Reformat CHANGELOG.md to Keep a Changelog format with Portuguese categories +- Update installation documentation for Python >= 3.10, pip primary + uv secondary + +## Tickets + +| Order | Ticket | Title | Points | +| ----- | ---------- | ----------------------------------------------- | ------ | +| 1 | ticket-013 | Expand README with Badges and Sections | 2 | +| 2 | ticket-014 | Create CONTRIBUTING.md | 3 | +| 3 | ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | 2 | +| 4 | ticket-016 | Update Installation Documentation | 2 | + +## Dependencies + +- ticket-013 depends on ticket-002 (needs final CI workflow name for badge URL) +- ticket-014 depends on ticket-001 and ticket-005 (references dep groups and pre-commit) +- ticket-015 is independent +- ticket-016 is independent + +## Completion Criteria + +- README.md displays CI, codecov, PyPI version, license, and docs badges +- CONTRIBUTING.md covers full contributor workflow with uv-based setup +- CHANGELOG.md follows Keep a Changelog format with Portuguese categories +- Installation docs reference Python >= 3.10 and show both pip and uv commands diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md new file mode 100644 index 0000000..381ca81 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md @@ -0,0 +1,32 @@ +# ticket-013 Expand README with Badges and Sections + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Expand the minimal README.md (currently 36 lines) with a comprehensive set of badges (CI, codecov, PyPI version, license, docs) and structured sections in Brazilian Portuguese covering features, installation, quick start, documentation links, and contributing. The README serves as the primary landing page for the GitHub repository. + +## Anticipated Scope + +- **Files likely to be modified**: + - `/home/rogerio/git/idecomp/README.md` (rewrite/expand) +- **Key decisions needed**: + - Badge URL format for CI (depends on final workflow name from ticket-002: `actions/workflows/main.yml/badge.svg`) + - Which PyPI badge service to use (shields.io vs. pypi-version badge) + - Whether to include a "Projetos Relacionados" section linking to inewave, cfinterface + - Section ordering: badges, description, features, installation, quick start, docs, contributing, license +- **Open questions**: + - Should the README include a code example snippet (e.g., reading a dadger file)? + - Should it mention uv as an alternative to pip for installation? + - Should the Python version badge say >=3.10 to match the actual requirement (README currently says >=3.8)? + +## Dependencies + +- **Blocked By**: ticket-002-restructure-ci-workflow.md (needs final workflow name for badge URL) +- **Blocks**: None + +## Effort Estimate + +**Points**: 2 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md new file mode 100644 index 0000000..b1dcf81 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md @@ -0,0 +1,32 @@ +# ticket-014 Create CONTRIBUTING.md + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Create a standalone CONTRIBUTING.md file at the repository root documenting the full contributor workflow: environment setup with uv, dependency installation by group, pre-commit hook usage, testing with pytest, type checking with mypy, code formatting with ruff, code conventions (PEP8, static typing, DataFrame patterns), and PR workflow. Content extracted and updated from the existing `docs/source/geral/contribuicao.rst` which currently references deprecated tools (pylama, black, pip/requirements.txt). + +## Anticipated Scope + +- **Files likely to be modified**: + - `/home/rogerio/git/idecomp/CONTRIBUTING.md` (new file) + - `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` (update to reference CONTRIBUTING.md or keep both in sync) +- **Key decisions needed**: + - Whether to keep `contribuicao.rst` as a full page or convert it to a redirect/summary pointing to CONTRIBUTING.md + - Which content from `contribuicao.rst` stays in docs vs. moves to CONTRIBUTING.md (cfinterface framework description is docs-appropriate; dev setup is CONTRIBUTING-appropriate) + - Whether to include the cfinterface framework explanation or keep it in the architecture page (ticket-008) +- **Open questions**: + - Should CONTRIBUTING.md reference pre-commit hook stages (normal vs. manual for mypy)? + - Should it include a section on running sphinx-build locally? + - Should the PR workflow section describe branch naming conventions? + +## Dependencies + +- **Blocked By**: ticket-001-modernize-pyproject-toml.md, ticket-005-add-pre-commit-hooks.md +- **Blocks**: None + +## Effort Estimate + +**Points**: 3 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md new file mode 100644 index 0000000..b0fcdf5 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md @@ -0,0 +1,32 @@ +# ticket-015 Reformat CHANGELOG to Keep a Changelog Standard + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Reformat the existing CHANGELOG.md (88 lines, 15 versions from v1.0.0 to v1.8.2) from its current ad-hoc format (heading per version, bullet points) to the Keep a Changelog standard with Portuguese category names (Adicionado, Corrigido, Modificado, Removido). Preserve all existing content; only restructure it into the standard format. + +## Anticipated Scope + +- **Files likely to be modified**: + - `/home/rogerio/git/idecomp/CHANGELOG.md` (reformat in place) +- **Key decisions needed**: + - Portuguese category names: Adicionado, Corrigido, Modificado, Removido, Depreciado, Seguranca + - Whether to add dates to version headers (current format has no dates) + - Whether to add a `[Unreleased]` section at the top + - How to categorize entries that are ambiguous (e.g., "Atualiza processamento" -- is that Modificado or Corrigido?) +- **Open questions**: + - Are release dates available from git tags or GitHub releases? + - Should the header link to a GitHub compare URL between versions? + - Should the "Primeira major release" note for v1.0.0 be kept as-is or expanded? + +## Dependencies + +- **Blocked By**: None (independent) +- **Blocks**: None + +## Effort Estimate + +**Points**: 2 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md new file mode 100644 index 0000000..6b590e5 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md @@ -0,0 +1,31 @@ +# ticket-016 Update Installation Documentation + +> **[OUTLINE]** This ticket requires refinement before execution. +> It will be refined with learnings from earlier epics. + +## Objective + +Update the installation documentation page (`docs/source/geral/instalacao.rst`) to reflect the current Python >= 3.10 requirement (currently says >= 3.8), add uv as a secondary installation method alongside pip, and modernize the commands to use current best practices. All content in Brazilian Portuguese. + +## Anticipated Scope + +- **Files likely to be modified**: + - `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` (update content) +- **Key decisions needed**: + - Whether to present pip as primary and uv as secondary, or give them equal weight + - Whether to add a section about installing from source with uv (development setup) + - Whether to remove the `pip install --upgrade pip` preamble (modernize to just `pip install idecomp`) +- **Open questions**: + - Should the page mention conda/mamba as installation alternatives? + - Should it include a section on verifying the installation (`python -c "import idecomp; print(idecomp.__version__)"`) + - Should the development installation section reference `uv sync --extra dev` instead of `pip install git+...`? + +## Dependencies + +- **Blocked By**: None (independent) +- **Blocks**: None + +## Effort Estimate + +**Points**: 2 +**Confidence**: Low (will be re-estimated during refinement) diff --git a/plans/infra-docs-overhaul/learnings/epic-01-summary.md b/plans/infra-docs-overhaul/learnings/epic-01-summary.md new file mode 100644 index 0000000..ff6c197 --- /dev/null +++ b/plans/infra-docs-overhaul/learnings/epic-01-summary.md @@ -0,0 +1,86 @@ +# Epic 01 Learnings: Packaging & CI Modernization + +**Epic**: epic-01-packaging-ci-modernization +**Date**: 2026-03-08 +**Tickets**: ticket-001 through ticket-005 +**Files changed**: `pyproject.toml`, `.github/workflows/main.yml`, `.github/workflows/docs.yml`, `.github/workflows/release.yml` (renamed from `publish.yml`), `.pre-commit-config.yaml` (new) + +--- + +## Patterns Established + +- **Dependency group split (4 extras)**: `pyproject.toml` uses `test = [pytest, pytest-cov]`, `lint = [ruff, mypy]`, `docs = [sphinx, furo, ...]`, `dev = ["idecomp[test,lint,docs]"]` as a self-referencing alias. Each CI job installs only its required extra via `uv sync --extra `. See `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. + +- **4-parallel-job CI pattern**: `main.yml` runs `lint`, `typecheck`, `test`, `docs` as fully independent jobs. `lint` and `typecheck` both install `--extra lint`. `test` matrix covers Python 3.10/3.11/3.12 with codecov upload. `docs` installs `--extra docs` and runs sphinx-build. No `needs:` dependencies between any of the 4 jobs. See `/home/rogerio/git/idecomp/.github/workflows/main.yml`. + +- **Official GitHub Pages 2-job deploy pattern**: `docs.yml` uses a `build` job (sphinx-build + `actions/upload-pages-artifact@v3`) followed by a `deploy` job (`needs: build`, `actions/deploy-pages@v4`, `environment: github-pages`). Top-level `permissions: pages: write, id-token: write` and `concurrency: group: "pages", cancel-in-progress: false` are mandatory for this pattern. See `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. + +- **Tag-triggered release with regex version validation**: `release.yml` triggers on `push: tags: ["v*"]`. Version is extracted with `grep -oP '__version__\s*=\s*"\K[^"]+'` on `idecomp/__init__.py`, not via `exec()` or Python import. GitHub release is created after PyPI publish via `gh release create ${{ github.ref_name }} --generate-notes`. See `/home/rogerio/git/idecomp/.github/workflows/release.yml`. + +- **mypy pre-commit hook at `stages: [manual]`**: The `.pre-commit-config.yaml` registers mypy as `language: system`, `entry: uv run mypy ./idecomp`, `pass_filenames: false`, `stages: [manual]`. This prevents cfinterface import errors from blocking every commit while keeping mypy available for deliberate invocation via `pre-commit run --hook-stage manual --all-files`. See `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. + +- **ruff hook with `args: [--fix]`**: The ruff pre-commit hook includes `args: [--fix]` so it auto-corrects fixable issues rather than just reporting them. Without this flag, the hook only reports and the commit still requires manual re-run. See `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. + +--- + +## Architectural Decisions + +- **`strict = true` replaces explicit mypy flags**: The ticket specified many redundant mypy flags (`disallow_untyped_defs`, `no_implicit_optional`, etc.) explicitly alongside `strict = true`. The implementation correctly omits the redundant flags because `strict = true` already enables all of them. Only the overrides that conflict with strict mode (`warn_return_any = false`) were made explicit. **Rejected alternative**: listing all flags verbosely for documentation purposes (rejected because `strict = true` is self-documenting and the redundancy creates maintenance risk). + +- **`uv python install` without version in docs.yml**: The `build` job in `docs.yml` uses `uv python install` with no explicit version, relying on uv to select from `.python-version` or `pyproject.toml`'s `requires-python`. This differs from `main.yml` which explicitly pins `3.12`. **Accepted because**: docs builds are not version-sensitive; using the project default is appropriate and avoids a hard pin that diverges from the matrix. + +- **`publish.yml` renamed to `release.yml` via git mv**: The file rename was done preserving git history through `git mv`, not a delete-and-create. This preserves the audit trail for the old trigger model. Future maintainers should verify `release.yml` exists and `publish.yml` does not before adding any references. + +- **Workflow name kept as `tests`**: `main.yml` retains `name: tests` even though it now runs lint, typecheck, test, and docs. This was a deliberate constraint: the README badge URL references `actions/workflows/main.yml/badge.svg` and renaming the workflow would visually change the badge label but not break it. Badge references must be audited in ticket-013 before changing. + +--- + +## Files & Structures Created + +- `/home/rogerio/git/idecomp/pyproject.toml` — Modernized with split dependency groups, full classifiers, `[tool.mypy]` with strict settings and cfinterface override, `[tool.ruff.lint]` with E/F/I/UP rule sets. +- `/home/rogerio/git/idecomp/.github/workflows/main.yml` — 4-parallel-job workflow replacing the former single sequential job. +- `/home/rogerio/git/idecomp/.github/workflows/docs.yml` — 2-job official Pages deployment replacing `peaceiris/actions-gh-pages@v3`. +- `/home/rogerio/git/idecomp/.github/workflows/release.yml` — Tag-triggered release workflow (renamed from `publish.yml`). +- `/home/rogerio/git/idecomp/.pre-commit-config.yaml` — New file with ruff (auto-fix) and mypy (manual stage) hooks. + +--- + +## Conventions Adopted + +- **All tool invocations in CI use `uv run `**: Never call `python -m pytest` or bare `ruff` in workflow steps; always prefix with `uv run`. This ensures the project's virtual environment is active and the correct tool version is used. + +- **CI installs only the minimal required extras per job**: `lint` job uses `--extra lint`, `test` job uses `--extra test`, `docs` job uses `--extra docs`. Never use `--all-extras` or `--dev` in CI — this would increase install time and mask missing dependency declarations. + +- **`cfinterface` pin `>=1.8,<=1.8.3` is immutable**: This constraint must not be removed or widened in any future ticket. It is at `/home/rogerio/git/idecomp/pyproject.toml` line 9 and exists because newer versions of cfinterface introduced breaking changes. + +- **`warn_return_any = false` must appear in both `[tool.mypy]` and `[[tool.mypy.overrides]]` for cfinterface**: `strict = true` re-enables `warn_return_any`. The top-level override relaxes it for all code; the per-module override is belt-and-suspenders for cfinterface specifically. + +- **Concurrency group `"pages"` with `cancel-in-progress: false`**: When migrating any future docs deployment workflow, always preserve this exact concurrency configuration. `cancel-in-progress: true` would abort a running deployment mid-way and leave Pages in an inconsistent state. + +- **Version extraction in release workflows uses grep regex, never exec/import**: `grep -oP '__version__\s*=\s*"\K[^"]+'` is the correct pattern. Python import (`from idecomp import __version__`) fails in fresh CI environments because relative imports in `__init__.py` require the package to be properly installed. `exec(open('__init__.py').read())` fails for the same reason. + +--- + +## Surprises & Deviations + +- **mypy strict flag verbosity**: Tickets specified all individual strict flags explicitly in the spec section for documentation clarity, but the final `pyproject.toml` correctly omits them as redundant under `strict = true`. This was the right call. Future ticket authors should note that specifying redundant flags in a spec is an anti-pattern — the spec should match what should be written, not serve as documentation for what `strict` implies. + +- **`docs.yml` Python version unspecified**: The ticket did not specify which Python version the docs `build` job should use (unlike `main.yml` which explicitly pins 3.12 for non-matrix jobs). The implementation used `uv python install` without a version, which is valid. Future tickets touching `docs.yml` should note this difference from `main.yml` and decide whether to align them. + +- **`release.yml` does not run `ruff format --check`**: The validation steps in `release.yml` include `ruff check` but not `ruff format --check`. This is consistent with the ticket spec (which only listed `ruff check` in the validation steps), but differs from `main.yml`'s `lint` job which checks both. This is an intentional asymmetry: format violations are caught in normal CI before a release is tagged. + +- **`publish.yml` deleted**: The git status shows the file was renamed (`RM .github/workflows/publish.yml -> .github/workflows/release.yml`). Any documentation in epics 3 or 4 that references `publish.yml` must be updated to `release.yml`. + +--- + +## Recommendations for Future Epics + +- **Epic 2 (Sphinx Modernization)**: The `docs` extra in `pyproject.toml` already includes `furo` (replacing `sphinx-rtd-theme`). The `docs.yml` workflow already builds with `--extra docs`. Epic 2 only needs to change `docs/source/conf.py` — no workflow or dependency changes required. Verify `furo` is resolvable before starting ticket-006. + +- **Epic 3 (Documentation Content)**: All new Sphinx pages must build successfully under the `docs` job in `main.yml` and the `build` job in `docs.yml`. Test sphinx-build locally with `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build` before writing tickets. If cfinterface causes sphinx-gallery import errors, the mitigation pattern from inewave (skipping problematic examples) should be documented in the ticket. + +- **Epic 4 (Repository Polish)**: CONTRIBUTING.md should document the pre-commit hook setup: `pip install pre-commit && pre-commit install` for regular hooks, and `pre-commit run --hook-stage manual --all-files` for mypy. The `stages: [manual]` design decision should be explained. Reference `/home/rogerio/git/idecomp/.pre-commit-config.yaml` directly in the contributing guide. + +- **Badge URL stability**: `main.yml` workflow name is `tests`. If ticket-013 updates the README, it should reference `[![Tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](...)`. Do not assume the workflow display name will match — it is `tests`, not `CI` or `main`. + +- **Release workflow trigger**: Any ticket that involves bumping `__version__` in `idecomp/__init__.py` must also consider that pushing a `v*` tag will trigger `release.yml` and attempt PyPI publication. Coordinate version bumps and tag pushes explicitly. The version validation step in `release.yml` will catch mismatches but the workflow will still be triggered. diff --git a/pyproject.toml b/pyproject.toml index d7696ef..a511226 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ authors = [ {name = "Rogerio Alves", email = "rogerioalves.ee@gmail.com"}, {name = "Mariana Noel", email = "marianasimoesnoel@gmail.com"}, ] -description = "idecomp" +description = "Pacote para manipulacao dos arquivos do DECOMP" readme = "README.md" license = {file = "LICENSE.md"} classifiers = [ @@ -23,22 +23,24 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Typing :: Typed", "Operating System :: OS Independent", ] [project.optional-dependencies] -dev = [ - "pytest", - "pytest-cov", - "ruff", - "mypy", - "sphinx-rtd-theme", - "sphinx-gallery", +test = ["pytest", "pytest-cov"] +lint = ["ruff", "mypy"] +docs = [ "sphinx", + "furo", + "sphinx-gallery", "numpydoc", "plotly", "matplotlib", ] +dev = ["idecomp[test,lint,docs]"] [project.urls] Documentation = "https://rjmalves.github.io/idecomp/" @@ -54,3 +56,17 @@ include = [ [tool.ruff] line-length = 80 + +[tool.ruff.lint] +select = ["E", "F", "I", "UP"] + +[tool.mypy] +python_version = "3.10" +warn_return_any = false +warn_unused_configs = true +strict = true + +[[tool.mypy.overrides]] +module = "cfinterface.*" +ignore_missing_imports = true +warn_return_any = false From 6d18e82fb9b71044c8798ab94582cc77209c23cf Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Sun, 8 Mar 2026 17:35:14 -0300 Subject: [PATCH 2/9] feat: complete epic-02 Sphinx modernization - Migrate Sphinx theme from sphinx-rtd-theme to Furo with dark mode - Verify sphinx-gallery examples compatible with Furo (no changes needed) - Simplify conf.py configuration Co-Authored-By: Claude Opus 4.6 --- docs/source/conf.py | 78 +++--------------- docs/source/sg_execution_times.rst | 46 +++++++++++ .../.implementation-state.json | 34 ++++++-- plans/infra-docs-overhaul/README.md | 36 ++++----- .../epic-02-sphinx-modernization/learnings.md | 80 +++++++++++++++++++ .../learnings/epic-02-summary.md | 69 ++++++++++++++++ 6 files changed, 253 insertions(+), 90 deletions(-) create mode 100644 docs/source/sg_execution_times.rst create mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md create mode 100644 plans/infra-docs-overhaul/learnings/epic-02-summary.md diff --git a/docs/source/conf.py b/docs/source/conf.py index 7d6d7ee..0bbdf0c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,19 +1,6 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# import os import sys from datetime import date -from typing import List import plotly.io as pio pio.renderers.default = "sphinx_gallery" @@ -21,9 +8,6 @@ sys.path.insert(0, os.path.abspath("../../")) from idecomp import __version__ # noqa: E402 - -# -- Project information ----------------------------------------------------- - project = "idecomp" copyright = f"{date.today().year}, Rogerio Alves" author = "Rogerio Alves" @@ -32,11 +16,6 @@ release = __version__ today = date.today().strftime("%d/%m/%Y") -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ "sphinx.ext.autosummary", "sphinx.ext.autodoc", @@ -46,65 +25,37 @@ "sphinx.ext.githubpages", "sphinx_gallery.gen_gallery", "numpydoc", - "sphinx_rtd_theme", ] - -# generate autosummary pages autosummary_generate = True - -# Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. language = "pt_BR" - -# The suffix of source filenames. source_suffix = ".rst" - -# The encoding of source files. source_encoding = "utf-8" - -# The master toctree document. master_doc = "index" - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns: List[str] = [] +exclude_patterns: list[str] = [] add_module_names = False -pygments_style = "sphinx" +pygments_style = "friendly" +pygments_dark_style = "monokai" modindex_common_prefix = ["idecomp."] -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "sphinx_rtd_theme" +html_theme = "furo" html_theme_options = { - "logo_only": True, - # Toc options - "collapse_navigation": False, - "sticky_navigation": True, - "navigation_depth": 4, - "includehidden": True, - "titles_only": False, + "light_css_variables": { + "color-brand-primary": "#2962ff", + "color-brand-content": "#2962ff", + }, + "dark_css_variables": { + "color-brand-primary": "#5c8aff", + "color-brand-content": "#5c8aff", + }, + "sidebar_hide_name": True, } -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] html_logo = "_static/logo_idecomp_svg.svg" -# The reST default role (used for this markup: `text`) to use for all -# documents. default_role = "obj" numpydoc_show_class_members = False @@ -118,11 +69,8 @@ "cfinterface": ("https://rjmalves.github.io/cfinterface/", None), } -# https://github.com/sphinx-gallery/sphinx-gallery sphinx_gallery_conf = { - # path to your examples scripts "examples_dirs": "../../examples", - # path where to save gallery generated examples "gallery_dirs": "examples", "backreferences_dir": "gen_modules/generated", } diff --git a/docs/source/sg_execution_times.rst b/docs/source/sg_execution_times.rst new file mode 100644 index 0000000..11317b1 --- /dev/null +++ b/docs/source/sg_execution_times.rst @@ -0,0 +1,46 @@ + +:orphan: + +.. _sphx_glr_sg_execution_times: + + +Computation times +================= +**00:00.568** total execution time for 4 files **from all galleries**: + +.. container:: + + .. raw:: html + + + + + + + + .. list-table:: + :header-rows: 1 + :class: table table-striped sg-datatable + + * - Example + - Time + - Mem (MB) + * - :ref:`sphx_glr_examples_plot_relato.py` (``../../examples/plot_relato.py``) + - 00:00.415 + - 0.0 + * - :ref:`sphx_glr_examples_plot_dec_oper_sist.py` (``../../examples/plot_dec_oper_sist.py``) + - 00:00.067 + - 0.0 + * - :ref:`sphx_glr_examples_plot_dadger.py` (``../../examples/plot_dadger.py``) + - 00:00.046 + - 0.0 + * - :ref:`sphx_glr_examples_plot_edit_dadger.py` (``../../examples/plot_edit_dadger.py``) + - 00:00.040 + - 0.0 diff --git a/plans/infra-docs-overhaul/.implementation-state.json b/plans/infra-docs-overhaul/.implementation-state.json index a1de29b..107b7af 100644 --- a/plans/infra-docs-overhaul/.implementation-state.json +++ b/plans/infra-docs-overhaul/.implementation-state.json @@ -1,6 +1,6 @@ { "plan": "infra-docs-overhaul", - "last_updated": "2026-03-08T21:00:00Z", + "last_updated": "2026-03-08T20:35:09Z", "progressive": true, "tickets": { "ticket-001-modernize-pyproject-toml.md": { @@ -124,9 +124,17 @@ "agent": "python-task-automation-developer" }, "ticket-006-migrate-sphinx-theme-to-furo.md": { - "status": "pending", + "status": "completed", "detail_level": "detailed", "scores": { + "quality": 0.85, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 0.5, + "type_safety": 0.5, + "test_delta": 1.0 + }, "readiness": 1.0, "readiness_breakdown": { "structure": 1.0, @@ -135,12 +143,22 @@ "dependency_clarity": 1.0, "atomicity": 1.0 } - } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-007-update-sphinx-gallery-examples.md": { - "status": "pending", + "status": "completed", "detail_level": "detailed", "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, "readiness": 0.94, "readiness_breakdown": { "structure": 1.0, @@ -149,7 +167,9 @@ "dependency_clarity": 1.0, "atomicity": 0.8 } - } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-008-create-architecture-page.md": { "status": "pending", @@ -194,8 +214,8 @@ "learning_extracted": true }, "epic-02-sphinx-modernization": { - "phase": "executing", - "learning_extracted": false + "phase": "completed", + "learning_extracted": true }, "epic-03-documentation-content": { "phase": "outline", diff --git a/plans/infra-docs-overhaul/README.md b/plans/infra-docs-overhaul/README.md index da7fc50..d78e53c 100644 --- a/plans/infra-docs-overhaul/README.md +++ b/plans/infra-docs-overhaul/README.md @@ -21,24 +21,24 @@ Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentat ## Progress -| Ticket | Title | Epic | Status | Detail Level | Readiness | Quality | Badge | -| ---------- | -------------------------------------------------------- | ------- | --------- | ------------ | --------- | ------- | --------- | -| ticket-001 | Modernize pyproject.toml Metadata and Dependency Groups | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-002 | Restructure CI Workflow into Parallel Jobs | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-003 | Migrate Docs Deployment to Official GitHub Pages Actions | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-004 | Create Tag-Triggered Release Workflow | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-005 | Add Pre-commit Hooks Configuration | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-006 | Migrate Sphinx Theme to Furo | epic-02 | pending | Detailed | 1.00 | -- | -- | -| ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | epic-02 | pending | Detailed | 0.94 | -- | -- | -| ticket-008 | Create Architecture Documentation Page | epic-03 | pending | Outline | -- | -- | -- | -| ticket-009 | Create FAQ Documentation Page | epic-03 | pending | Outline | -- | -- | -- | -| ticket-010 | Create Performance Guide Page | epic-03 | pending | Outline | -- | -- | -- | -| ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | pending | Outline | -- | -- | -- | -| ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | pending | Outline | -- | -- | -- | -| ticket-013 | Expand README with Badges and Sections | epic-04 | pending | Outline | -- | -- | -- | -| ticket-014 | Create CONTRIBUTING.md | epic-04 | pending | Outline | -- | -- | -- | -| ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | pending | Outline | -- | -- | -- | -| ticket-016 | Update Installation Documentation | epic-04 | pending | Outline | -- | -- | -- | +| Ticket | Title | Epic | Status | Detail Level | Readiness | Quality | Badge | +| ---------- | -------------------------------------------------------- | ------- | --------- | ------------ | --------- | ------- | ---------- | +| ticket-001 | Modernize pyproject.toml Metadata and Dependency Groups | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-002 | Restructure CI Workflow into Parallel Jobs | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-003 | Migrate Docs Deployment to Official GitHub Pages Actions | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-004 | Create Tag-Triggered Release Workflow | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-005 | Add Pre-commit Hooks Configuration | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | +| ticket-006 | Migrate Sphinx Theme to Furo | epic-02 | completed | Detailed | 1.00 | 0.85 | ACCEPTABLE | +| ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | epic-02 | completed | Detailed | 0.94 | 1.00 | EXCELLENT | +| ticket-008 | Create Architecture Documentation Page | epic-03 | pending | Outline | -- | -- | -- | +| ticket-009 | Create FAQ Documentation Page | epic-03 | pending | Outline | -- | -- | -- | +| ticket-010 | Create Performance Guide Page | epic-03 | pending | Outline | -- | -- | -- | +| ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | pending | Outline | -- | -- | -- | +| ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | pending | Outline | -- | -- | -- | +| ticket-013 | Expand README with Badges and Sections | epic-04 | pending | Outline | -- | -- | -- | +| ticket-014 | Create CONTRIBUTING.md | epic-04 | pending | Outline | -- | -- | -- | +| ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | pending | Outline | -- | -- | -- | +| ticket-016 | Update Installation Documentation | epic-04 | pending | Outline | -- | -- | -- | ## Dependency Graph diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md new file mode 100644 index 0000000..836f2b1 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md @@ -0,0 +1,80 @@ +# Epic 02 Learnings: Sphinx Modernization + +**Epic**: epic-02-sphinx-modernization +**Date**: 2026-03-08 +**Tickets**: ticket-006, ticket-007 +**Files changed**: `docs/source/conf.py` only (ticket-006); no files changed (ticket-007) + +--- + +## Patterns Established + +- **Furo theme is configured exclusively via `html_theme`, not `extensions`**: Unlike `sphinx-rtd-theme`, Furo must NOT appear in the `extensions` list. Adding it there causes a warning. The only required change is `html_theme = "furo"` plus the `html_theme_options` dict. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 43-54. + +- **Dual pygments style pattern for dark/light mode**: Furo requires two separate pygments settings: `pygments_style = "friendly"` for light mode and `pygments_dark_style = "monokai"` for dark mode. A single `pygments_style` setting (as with RTD) leaves dark mode code blocks unstyled. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 39-40. + +- **`sidebar_hide_name` with SVG logo**: When an `html_logo` is set, `"sidebar_hide_name": True` in `html_theme_options` prevents the project name from appearing redundantly next to the logo in the sidebar. Without it, both the logo and the text name display together. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 53. + +- **Brand color split into `light_css_variables` and `dark_css_variables`**: Furo uses CSS variable overrides to apply brand colors per mode. The pattern uses `"color-brand-primary"` and `"color-brand-content"` keys — primary for interactive elements, content for text/links. The light and dark values use different blue shades (`#2962ff` vs `#5c8aff`) to maintain contrast ratios in each mode. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 44-53. + +- **`from typing import List` replaced by built-in `list[str]`**: The original `conf.py` used `from typing import List` for the `exclude_patterns` type annotation. Since idecomp targets Python >= 3.10, this was updated to the built-in `list[str]` syntax and the import removed. See `/home/rogerio/git/idecomp/docs/source/conf.py` line 36. + +- **Example scripts use current class-based API (`ClassName.read()`) with no deprecated patterns**: All 4 example scripts in `/home/rogerio/git/idecomp/examples/` (`plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`) already use the modern API. No `le_arquivo` or `escreve_arquivo` calls were found. The gallery was already clean before ticket-007 ran. + +- **`pio.renderers.default = "sphinx_gallery"` is pre-existing and theme-agnostic**: The plotly renderer configuration at the top of `conf.py` (lines 4-6) predates the Furo migration and requires no changes when switching themes. It is a sphinx-gallery-specific setting, not an RTD-specific one. + +--- + +## Architectural Decisions + +- **conf.py is the single point of control for theme, pygments, and gallery**: The entire epic's scope was one file. No other files (pyproject.toml, CI workflows, example scripts) required modification. This confirms the epic-01 assessment that `furo` was already in the `docs` dependency group, and the theme switch is purely a `conf.py` concern. + +- **`abort_on_example_error = False` was NOT needed**: The ticket spec anticipated potential cfinterface import errors during gallery execution and listed this as a fallback. In practice, all 4 examples executed without error — the examples use idecomp's own API (not cfinterface directly), and the mock data files in `/home/rogerio/git/idecomp/examples/decomp/` are sufficient for clean execution. The fallback option was researched but not applied. + +- **RTD `html_theme_options` keys are wholly incompatible with Furo**: The original conf.py had RTD-specific keys (`logo_only`, `collapse_navigation`, `sticky_navigation`, `navigation_depth`, `includehidden`, `titles_only`). These were completely replaced, not adapted. The two themes have no overlapping option keys. Any future theme migration must treat `html_theme_options` as a clean replacement, not a patch. + +- **`sphinx.ext.githubpages` retained**: This extension creates the `.nojekyll` file required for GitHub Pages to serve the docs correctly. It is not RTD-specific. Removing it when switching themes would silently break the Pages deployment. It stays in `extensions` regardless of theme. + +--- + +## Files & Structures Created + +- `/home/rogerio/git/idecomp/docs/source/conf.py` — Updated in place: Furo theme, dual pygments styles, Furo `html_theme_options`, `sphinx_rtd_theme` removed from extensions, `from typing import List` replaced with built-in `list[str]`. No other files were modified in this epic. + +--- + +## Conventions Adopted + +- **Theme options must be a full replacement, never a patch**: When switching Sphinx themes, the `html_theme_options` dict must be completely rewritten for the new theme. Keeping any keys from the previous theme's dict causes Sphinx to emit warnings about unknown options (RTD keys are silently ignored by Furo, but this creates noise in build logs). + +- **Example mock data lives in `examples/decomp/`**: The 3 mock data files (`dadger.rv0`, `dec_oper_sist.csv`, `relato.rv0`) live at `/home/rogerio/git/idecomp/examples/decomp/`. Example scripts reference them via relative path `"./decomp/"`. This directory must be preserved and the files kept in sync with the format expected by the examples. Any new example script must place its mock data here. + +- **sphinx-gallery conf keys are stable and must not be changed**: The `sphinx_gallery_conf` dict at `/home/rogerio/git/idecomp/docs/source/conf.py` lines 72-76 uses `examples_dirs`, `gallery_dirs`, and `backreferences_dir` paths that are consistent with the project's directory layout. These paths must not be altered in future tickets — the gallery output goes to `docs/build/html/examples/` and `docs/source/gen_modules/generated/`. + +- **Brand colors from inewave sibling project are used verbatim**: The Furo CSS variable values (`#2962ff` / `#5c8aff`) match the inewave project's Furo configuration. This is an explicit cross-project consistency choice. Future theming work should consult the inewave conf.py before introducing any color changes. + +--- + +## Surprises & Deviations + +- **ticket-007 required zero code changes**: The ticket was allocated 2 points with medium confidence, anticipating potential API deprecation fixes or cfinterface fallback configuration. In practice, all 4 example scripts were already using the current API, the mock data was complete, and the gallery built cleanly without `abort_on_example_error`. The verification step was still valuable — it confirmed the gallery is healthy — but the ticket was a pure audit, not an implementation. + +- **conf.py was already partially modernized before the plan**: The original `conf.py` (as of commit `f84091f`) had the full scaffold: autosummary, intersphinx, sphinx-gallery, numpydoc, plotly renderer, SVG logo. The only gaps were the RTD theme, `sphinx_rtd_theme` in extensions, the legacy `pygments_style = "sphinx"`, no dark pygments style, and `from typing import List`. Epic 2 was a targeted diff, not a rewrite. + +- **Git history shows conf.py was not changed in the epic-01 commit**: The epic-01 completion commit (`e20d968`) included `pyproject.toml` and CI workflow changes but did not touch `conf.py`. The furo dependency was added to `pyproject.toml` in that commit, but the theme activation in `conf.py` was deferred to epic-02 as designed. This confirms the correct sequencing. + +- **No `html_logo` change needed**: The ticket spec noted the logo path must be preserved. The original `html_logo = "_static/logo_idecomp_svg.svg"` was already set before this epic and Furo supports the same `html_logo` config key as RTD. No path changes were needed. + +--- + +## Recommendations for Future Epics + +- **Epic 3 (Documentation Content)**: New RST pages added to `docs/source/` will render with the Furo theme. Use `.. code-block:: python` with explicit language hints — Furo's monokai dark style is optimized for syntax-highlighted blocks. Avoid using `::` (anonymous code blocks) since pygments will not apply syntax coloring. Build locally with `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build` before finalizing any RST content. + +- **Epic 3 (autosummary)**: The `autosummary_generate = True` setting at `/home/rogerio/git/idecomp/docs/source/conf.py` line 30 auto-generates stub pages from `.. autosummary::` directives. When ticket-011 improves the API reference, ensure that `_templates/autosummary/` contains the correct templates. The `numpydoc_show_class_members = False` setting (line 61) suppresses member tables in class pages — adjust this if the improved autosummary should show members inline. + +- **Epic 3 (intersphinx)**: The intersphinx mapping at `/home/rogerio/git/idecomp/docs/source/conf.py` lines 62-70 includes `cfinterface` pointing to `https://rjmalves.github.io/cfinterface/`. Any new documentation page that cross-references cfinterface types can use `:class:`cfinterface.SomeClass`` and it will resolve. Do not add cfinterface types to the API reference directly. + +- **Epic 4 (CONTRIBUTING.md)**: When documenting the local docs build process, the command is `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build`. The built output at `docs/build/html/index.html` can be opened directly. The gallery examples take significant time to execute on first build but are cached in `docs/source/examples/` on subsequent runs. + +- **No future sphinx-rtd-theme work needed**: The string `sphinx_rtd_theme` no longer appears anywhere in `docs/source/conf.py`. Any future search for this string returning results would indicate a regression. The pyproject.toml `docs` extra group (added in epic-01) lists `furo` as the theme package — do not re-add `sphinx-rtd-theme`. diff --git a/plans/infra-docs-overhaul/learnings/epic-02-summary.md b/plans/infra-docs-overhaul/learnings/epic-02-summary.md new file mode 100644 index 0000000..4ad2b0e --- /dev/null +++ b/plans/infra-docs-overhaul/learnings/epic-02-summary.md @@ -0,0 +1,69 @@ +# Accumulated Learnings: Epics 01-02 + +**Plan**: infra-docs-overhaul +**Last updated**: 2026-03-08 +**Covers**: epic-01-packaging-ci-modernization, epic-02-sphinx-modernization + +--- + +## Dependency & Packaging Conventions + +- `pyproject.toml` uses 4 extras: `test`, `lint`, `docs`, `dev`; `docs` includes `furo` (not `sphinx-rtd-theme`). File: `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. +- `cfinterface` pin `>=1.8,<=1.8.3` is immutable — do not widen or remove. File: `/home/rogerio/git/idecomp/pyproject.toml` line 9. +- CI installs only the minimal required extra per job via `uv sync --extra `. Never use `--all-extras` in CI. +- All tool invocations in CI use `uv run ` — never bare `ruff`, `pytest`, or `sphinx-build`. + +## CI Workflow Patterns + +- `main.yml` runs 4 independent parallel jobs: `lint`, `typecheck`, `test`, `docs`. No `needs:` dependencies between them. File: `/home/rogerio/git/idecomp/.github/workflows/main.yml`. +- `docs.yml` uses the official 2-job Pages pattern: `build` job (sphinx-build + `upload-pages-artifact@v3`) then `deploy` job (`deploy-pages@v4`). Concurrency group `"pages"` with `cancel-in-progress: false` is mandatory. File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. +- `release.yml` triggers on `push: tags: ["v*"]`. Version extracted via `grep -oP` from `idecomp/__init__.py` — never via Python import or `exec()`. File: `/home/rogerio/git/idecomp/.github/workflows/release.yml`. +- `main.yml` workflow `name:` is `tests` — badge URLs reference this. Do not rename. +- `publish.yml` was renamed to `release.yml` via `git mv`. Any docs referencing `publish.yml` must be updated. + +## Pre-commit Hook Conventions + +- mypy hook uses `stages: [manual]` to prevent cfinterface import errors from blocking commits. Run with `pre-commit run --hook-stage manual --all-files`. File: `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. +- ruff hook uses `args: [--fix]` for auto-correction. Without this flag it only reports and requires manual re-run. + +## mypy Configuration + +- `strict = true` replaces all explicit strict flags — do not repeat `disallow_untyped_defs` etc. alongside it. +- `warn_return_any = false` must appear in `[tool.mypy]` to override what `strict = true` enables. File: `/home/rogerio/git/idecomp/pyproject.toml`. + +## Sphinx / Furo Theme Conventions + +- `html_theme = "furo"` is the only required theme setting; Furo must NOT appear in `extensions`. Adding it to extensions causes a warning. File: `/home/rogerio/git/idecomp/docs/source/conf.py` line 43. +- Dual pygments settings are required for Furo: `pygments_style = "friendly"` (light) and `pygments_dark_style = "monokai"` (dark). A single setting leaves dark mode unstyled. File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 39-40. +- `html_theme_options` must be a complete replacement when switching themes — no RTD keys are reusable with Furo. +- Brand colors match inewave sibling project verbatim: `#2962ff` (light) / `#5c8aff` (dark). Do not change without consulting inewave `conf.py`. +- `"sidebar_hide_name": True` prevents duplicate project name display when `html_logo` is set. +- `sphinx.ext.githubpages` must remain in `extensions` regardless of theme — it creates the `.nojekyll` file required by GitHub Pages. +- `pio.renderers.default = "sphinx_gallery"` at the top of `conf.py` is required for plotly charts in the gallery — do not remove. + +## Documentation Build Command + +- Local build: `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build` +- Built output: `docs/build/html/index.html` +- Gallery examples are cached after first build in `docs/source/examples/` — subsequent builds are faster. + +## Sphinx Gallery Structure + +- 4 example scripts in `/home/rogerio/git/idecomp/examples/`: `plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`. +- Mock data lives at `/home/rogerio/git/idecomp/examples/decomp/` (`dadger.rv0`, `dec_oper_sist.csv`, `relato.rv0`). New examples must add their mock data here. +- `sphinx_gallery_conf` paths (`examples_dirs`, `gallery_dirs`, `backreferences_dir`) must not be changed. File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 72-76. +- `abort_on_example_error = False` was anticipated as a cfinterface fallback but was NOT needed — all 4 examples run cleanly with the existing mock data. +- All examples use the current class-based API (`ClassName.read()`). No deprecated `le_arquivo`/`escreve_arquivo` patterns exist. + +## Documentation Content Conventions (for Epic 3) + +- Use explicit `.. code-block:: python` in RST pages — Furo's monokai dark style only applies to syntax-highlighted blocks, not anonymous `::` blocks. +- `autosummary_generate = True` auto-generates stub pages. Templates at `docs/source/_templates/autosummary/`. File: `/home/rogerio/git/idecomp/docs/source/conf.py` line 30. +- `numpydoc_show_class_members = False` suppresses member tables in class pages — adjust if ticket-011 should show members inline. +- intersphinx includes cfinterface at `https://rjmalves.github.io/cfinterface/` — cross-references to cfinterface types will resolve without explicit declarations. + +## Release & Version Conventions (for Epic 4) + +- Pushing a `v*` tag triggers `release.yml` and attempts PyPI publication. Coordinate version bumps and tag pushes explicitly. +- CONTRIBUTING.md must document: `pip install pre-commit && pre-commit install` for hooks, and `pre-commit run --hook-stage manual --all-files` for mypy. Explain the `stages: [manual]` design decision. +- Badge URL: `[![Tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](...)` — references `main.yml` by filename, not display name. From f0683bbe34c1cd47a3a85afb31d8db382dc66534 Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 18:12:09 -0300 Subject: [PATCH 3/9] feat: complete epic-03 documentation content expansion - Create architecture documentation page (guias/arquitetura.rst) - Create FAQ documentation page with 16 Q&A entries (guias/faq.rst) - Create performance guide page (guias/desempenho.rst) - Add autosummary blocks to API reference pages (41 decomp + 2 libs classes) - Update index.rst toctree with new Guias section Co-Authored-By: Claude Opus 4.6 --- docs/source/guias/arquitetura.rst | 178 +++++++++ docs/source/guias/desempenho.rst | 252 ++++++++++++ docs/source/guias/faq.rst | 374 ++++++++++++++++++ docs/source/index.rst | 14 +- docs/source/referencia/decomp/index.rst | 45 +++ docs/source/referencia/libs/index.rst | 6 + .../.implementation-state.json | 126 +++++- plans/infra-docs-overhaul/README.md | 17 +- .../ticket-008-create-architecture-page.md | 123 +++++- .../ticket-009-create-faq-page.md | 133 ++++++- .../ticket-010-create-performance-guide.md | 140 ++++++- ...t-011-improve-api-reference-autosummary.md | 133 ++++++- .../ticket-012-update-index-toctree.md | 116 +++++- .../learnings/epic-03-summary.md | 89 +++++ 14 files changed, 1631 insertions(+), 115 deletions(-) create mode 100644 docs/source/guias/arquitetura.rst create mode 100644 docs/source/guias/desempenho.rst create mode 100644 docs/source/guias/faq.rst create mode 100644 plans/infra-docs-overhaul/learnings/epic-03-summary.md diff --git a/docs/source/guias/arquitetura.rst b/docs/source/guias/arquitetura.rst new file mode 100644 index 0000000..3beacbb --- /dev/null +++ b/docs/source/guias/arquitetura.rst @@ -0,0 +1,178 @@ +Arquitetura do idecomp +====================== + +Visao Geral +----------- + +O *idecomp* é um pacote Python que oferece uma interface de alto nível para leitura e +escrita dos arquivos do modelo `DECOMP `_, desenvolvido pelo CEPEL e +utilizado para o planejamento da operação de curto prazo do Sistema Interligado Nacional (SIN). +Cada arquivo suportado pelo pacote é representado por uma classe Python dedicada, que encapsula +a lógica de leitura, escrita e acesso aos dados. + +A base do idecomp é o framework `cfinterface `_, +que fornece as classes-base responsáveis pelo mecanismo de parse de arquivos de texto estruturado. +O idecomp atua como uma camada de domínio sobre esse framework: as classes de arquivo do idecomp +herdam de classes-base do cfinterface e definem quais modelos de registros ou blocos compõem +cada arquivo do DECOMP. + +Dessa forma, o desenvolvedor que utiliza o idecomp não precisa conhecer os detalhes do protocolo +de parse — basta instanciar a classe do arquivo desejado, chamar o método ``read`` para carregar +os dados do disco e acessar as propriedades que retornam os dados em estruturas Python convenientes, +como ``pd.DataFrame`` do `pandas `_. + +Estrutura de Pacotes +-------------------- + +O idecomp organiza seu código em dois subpacotes principais: + +``idecomp/decomp/`` + Contém as 41 classes de arquivo do DECOMP, uma por arquivo suportado. Exemplos: + ``Dadger``, ``Relato``, ``Vazoes``, ``DecOperSist``, ``Hidr``, entre outros. + Todas as classes são expostas diretamente em ``idecomp.decomp`` via importação no + ``__init__.py`` do subpacote. + +``idecomp/decomp/modelos/`` + Contém os modelos de registros e blocos utilizados pelas classes de arquivo acima. + São 45 módulos de modelos, organizados em subdiretórios: + + ``idecomp/decomp/modelos/blocos/`` + Definições de blocos reutilizáveis entre diferentes arquivos, como + ``VersaoModelo``, que identifica a versão do DECOMP a partir de um cabeçalho. + + ``idecomp/decomp/modelos/arquivoscsv/`` + Adaptador para arquivos de saída no formato CSV, base para arquivos como + ``DecOperSist``, ``DecOperUsih``, ``DecOperRee``, entre outros. + +``idecomp/libs/`` + Contém 2 módulos auxiliares para arquivos do tipo LIBS utilizados em conjunto + com o DECOMP: ``UsinasHidreletricas`` (``usinas_hidreletricas.py``) e + ``Restricoes`` (``restricoes.py``), com seus respectivos modelos em + ``idecomp/libs/modelos/``. + +Framework cfinterface +--------------------- + +O cfinterface define três classes-base para arquivos de texto estruturado. O idecomp +utiliza todas as três, dependendo do formato do arquivo DECOMP em questão: + +:class:`cfinterface.files.registerfile.RegisterFile` + Base para arquivos orientados a registros, em que cada linha do arquivo corresponde + a um registro identificado por uma palavra-chave no início da linha. O arquivo + ``dadger.rvX`` é o principal exemplo: cada seção de dados (usinas, restrições, + configurações de estudo) é representada por um tipo de registro próprio. + + .. code-block:: python + + from idecomp.decomp import Dadger + + arq = Dadger.read("./dadger.rv0") + + # Acessa o registro TE (título do estudo) + titulo = arq.te + print(titulo) + + # Acessa a tabela de usinas hidrelétricas como DataFrame + df_usinas = arq.uh + print(df_usinas.head()) + + # Modifica um dado e salva o arquivo de entrada atualizado + arq.te = "Novo titulo do estudo" + arq.write("./dadger.rv0") + +:class:`cfinterface.files.blockfile.BlockFile` + Base para arquivos orientados a blocos, em que o conteúdo é dividido em seções + delimitadas por cabeçalhos textuais. O arquivo ``relato.rvX`` é o principal + exemplo: cada bloco de resultados (convergência, balanço energético, CMO) é + identificado por um padrão textual de abertura e fechamento. + + .. code-block:: python + + from idecomp.decomp import Relato + + arq = Relato.read("./relato.rv0") + + # Acessa o bloco de convergência como DataFrame + df_conv = arq.convergencia + print(df_conv.head(10)) + +``ArquivoCSV`` (``idecomp.decomp.modelos.arquivoscsv.arquivocsv``) + Adaptador interno do idecomp construído sobre o cfinterface para arquivos de saída + no formato CSV gerados pelo DECOMP. Classes como ``DecOperSist``, ``DecOperUsih`` + e ``DecOperRee`` herdam de ``ArquivoCSV`` e implementam apenas a especificação + das colunas e versões suportadas. + + .. code-block:: python + + from idecomp.decomp import DecOperSist + + arq = DecOperSist.read("./dec_oper_sist.csv") + + # Acessa a tabela de operação por submercado + df = arq.tabela + print(df.head()) + +Fluxo de Dados +-------------- + +O fluxo de dados no idecomp segue sempre o mesmo padrão, independentemente do tipo +de arquivo (registro, bloco ou CSV): + +1. **Leitura do arquivo bruto**: o método de classe ``ClassName.read(caminho)`` abre o + arquivo no disco e aciona o mecanismo de parse do cfinterface. +2. **Construção do objeto Python**: o cfinterface percorre o arquivo linha a linha, + identificando registros ou blocos e instanciando os modelos correspondentes. +3. **Acesso via propriedades**: o objeto retornado expõe os dados através de propriedades + Python, que retornam ``pd.DataFrame`` para dados tabulares ou valores escalares + tipados para configurações simples. +4. **Escrita opcional**: para arquivos de entrada, o método ``instancia.write(caminho)`` + serializa o estado atual do objeto de volta para o formato de texto do DECOMP. + +.. code-block:: python + + from idecomp.decomp import Vazoes + + # 1. Leitura: arquivo bruto -> objeto Python + arq_vazoes = Vazoes.read("./vazoes.rv0") + + # 2. Acesso: propriedade retorna pd.DataFrame + df = arq_vazoes.previsoes + print(df.shape) + + # 3. Modificação e escrita (somente para arquivos de entrada) + df.iloc[:, 1:] *= 1.1 + arq_vazoes.previsoes = df + arq_vazoes.write("./vazoes.rv0") + +Arquivos de saída do DECOMP (como ``relato.rvX``, ``dec_oper_sist.csv``) não +implementam o método ``write``, pois são gerados exclusivamente pelo próprio modelo. +Tentativas de chamar ``write`` em instâncias desses arquivos resultam em erro. + +Modelos e Registros +------------------- + +Cada classe de arquivo em ``idecomp/decomp/`` delega o parse dos dados para classes +de modelo definidas em ``idecomp/decomp/modelos/``. Essa separação mantém as classes +de arquivo focadas na interface pública (propriedades e métodos) enquanto os modelos +cuidam dos detalhes de formatação e posicionamento de campos. + +Para arquivos baseados em ``RegisterFile`` (como ``Dadger``), os modelos são +subclasses de ``Register`` do cfinterface. Cada subclasse de ``Register`` define: + +- O código (palavra-chave) que identifica a linha no arquivo. +- Os campos (posição, tipo e nome) que compõem o registro. + +Para arquivos baseados em ``BlockFile`` (como ``Relato``), os modelos são subclasses +de ``Block`` do cfinterface. Cada subclasse de ``Block`` define: + +- O padrão textual que delimita o início do bloco. +- A lógica de leitura das linhas internas ao bloco. + +Os modelos em ``idecomp/decomp/modelos/blocos/`` são blocos reutilizáveis compartilhados +entre múltiplos arquivos. Por exemplo, o bloco ``VersaoModelo`` aparece em todos os +arquivos CSV do DECOMP e é responsável por identificar a versão do modelo a partir +do cabeçalho, permitindo que o idecomp selecione a estrutura de colunas correta para +cada versão do DECOMP. + +Para aprofundamento nos mecanismos internos de ``Register``, ``Block`` e ``RegisterFile``, +consulte a `documentação do cfinterface `_. diff --git a/docs/source/guias/desempenho.rst b/docs/source/guias/desempenho.rst new file mode 100644 index 0000000..4704208 --- /dev/null +++ b/docs/source/guias/desempenho.rst @@ -0,0 +1,252 @@ +Guia de Desempenho +================== + +Este guia apresenta as características de desempenho do *idecomp* e estratégias +práticas para otimizar fluxos de trabalho que processam grandes volumes de arquivos +do DECOMP. As recomendações aqui descritas são especialmente relevantes para análises +em lote, como a consolidação de resultados de múltiplos cenários ou revisões. + +Importacao +---------- + +Ao executar ``import idecomp`` ou ``from idecomp.decomp import AlgumaClasse``, o +interpretador Python carrega o módulo ``idecomp/__init__.py``, que importa +imediatamente o subpacote ``idecomp.decomp``. O ``__init__.py`` desse subpacote, +por sua vez, importa todas as **41 classes de arquivo** do DECOMP de uma vez. Esse +comportamento de importação antecipada (*eager import*) garante que a interface +pública do pacote esteja sempre disponível de forma consistente, mas implica um +custo de inicialização proporcional ao número de módulos carregados. + +Para a maioria dos scripts interativos e notebooks, esse custo é imperceptível. +Entretanto, em aplicações que chamam scripts Python repetidamente — por exemplo, +pipelines de automação que invocam o interpretador a cada execução — o tempo de +inicialização do processo pode ser relevante. Nesses casos, é possível reduzir o +escopo da importação apontando diretamente para o módulo do arquivo desejado, em +vez de importar pelo namespace agregado do subpacote. + +.. code-block:: python + + # Importa pelo namespace agregado: carrega todos os 41 modulos + from idecomp.decomp import Dadger + + # Importa diretamente do modulo: carrega apenas o modulo necessario + from idecomp.decomp.dadger import Dadger + +A segunda forma acessa ``idecomp/decomp/dadger.py`` diretamente, sem acionar a +importação em cadeia de todos os demais módulos do subpacote. O resultado funcional +é idêntico — a classe ``Dadger`` obtida é a mesma — mas o tempo de importação é +menor quando apenas um ou poucos arquivos precisam ser processados. + +Para medir o impacto no seu ambiente, utilize o módulo ``time`` do Python antes de +decidir qual estratégia adotar: + +.. code-block:: python + + import time + + inicio = time.perf_counter() + from idecomp.decomp.dadger import Dadger + fim = time.perf_counter() + + print(f"Tempo de importacao: {(fim - inicio) * 1000:.2f} ms") + +Leitura e Escrita de Arquivos +------------------------------ + +O padrão básico de leitura de um arquivo é sempre o mesmo: instanciar a classe +correspondente chamando o método de classe ``read`` com o caminho do arquivo no +disco. O objeto retornado mantém todos os dados do arquivo em memória e expõe as +informações por meio de propriedades que retornam ``pd.DataFrame`` ou valores +escalares tipados. + +.. code-block:: python + + from idecomp.decomp import DecOperSist + + # Leitura de um unico arquivo + arq = DecOperSist.read("./resultados/dec_oper_sist.csv") + df = arq.tabela + print(df.shape) + +Em análises de resultados do DECOMP, é comum processar o mesmo arquivo de saída +proveniente de múltiplos cenários ou revisões armazenados em diretórios separados. +O padrão recomendado para esse caso é iterar sobre os caminhos, ler cada arquivo, +extrair o DataFrame desejado e acumulá-los em uma lista para concatenação ao final: + +.. code-block:: python + + from pathlib import Path + import pandas as pd + from idecomp.decomp.dec_oper_sist import DecOperSist + + dfs = [] + for caminho in Path("./resultados").glob("*/dec_oper_sist.csv"): + arq = DecOperSist.read(str(caminho)) + dfs.append(arq.tabela) + + df_total = pd.concat(dfs, ignore_index=True) + print(f"Total de linhas consolidadas: {len(df_total)}") + +Concatenar ao final, em vez de concatenar incrementalmente dentro do loop, evita a +criação de cópias intermediárias do DataFrame a cada iteração — um padrão de +desempenho amplamente recomendado pelo próprio pandas. Quando o número de arquivos +for muito grande, considere adicionar ao DataFrame uma coluna de identificação +(nome do diretório ou do cenário) antes de acrescentá-lo à lista, para facilitar +a rastreabilidade dos dados na análise subsequente. + +Para medir o tempo total de um processamento em lote, envolva o loop com o módulo +``time``: + +.. code-block:: python + + import time + from pathlib import Path + import pandas as pd + from idecomp.decomp.dec_oper_sist import DecOperSist + + caminhos = list(Path("./resultados").glob("*/dec_oper_sist.csv")) + inicio = time.perf_counter() + + dfs = [] + for caminho in caminhos: + arq = DecOperSist.read(str(caminho)) + dfs.append(arq.tabela) + + df_total = pd.concat(dfs, ignore_index=True) + fim = time.perf_counter() + + print(f"{len(caminhos)} arquivos processados em {fim - inicio:.2f}s") + +Uso de Memoria +-------------- + +Cada objeto de arquivo do *idecomp* mantém em memória a representação Python +completa do arquivo lido — registros, blocos ou colunas CSV, dependendo do tipo. +Para arquivos de saída CSV com muitas linhas (como ``dec_oper_sist.csv`` com colunas +de estágio, cenário e patamar), o objeto pode ocupar memória significativa quando +múltiplos arquivos são carregados simultaneamente. + +O padrão recomendado para operações em lote é **ler, extrair e descartar**: após +obter o DataFrame desejado de um objeto de arquivo, remova a referência ao objeto +com ``del`` para que o coletor de lixo do Python possa liberar a memória associada. + +.. code-block:: python + + from idecomp.decomp.dec_oper_sist import DecOperSist + + arq = DecOperSist.read("./dec_oper_sist.csv") + df = arq.tabela # extrai o DataFrame necessario + del arq # libera o objeto do arquivo da memoria + + # A partir daqui, apenas df permanece em memoria + print(df.head()) + +Em loops que processam dezenas ou centenas de arquivos, combine esse padrão com +uma chamada explícita ao coletor de lixo ao final de cada iteração quando o +consumo de memória for crítico: + +.. code-block:: python + + import gc + from pathlib import Path + import pandas as pd + from idecomp.decomp.dec_oper_sist import DecOperSist + + dfs = [] + for caminho in Path("./resultados").glob("*/dec_oper_sist.csv"): + arq = DecOperSist.read(str(caminho)) + df_parcial = arq.tabela + del arq # descarta o objeto antes de passar para o proximo arquivo + gc.collect() # forca a coleta de lixo (recomendado para lotes muito grandes) + dfs.append(df_parcial) + + df_total = pd.concat(dfs, ignore_index=True) + +Note que ``gc.collect()`` adiciona um pequeno overhead por chamada. Avalie se o +ganho de memória justifica esse custo no seu caso de uso, medindo o consumo de +memória com ferramentas como ``tracemalloc`` (biblioteca padrão) ou ``memory_profiler`` +(pacote externo). + +Dicas de Otimizacao +-------------------- + +As dicas a seguir sintetizam as práticas mais eficientes para uso do *idecomp* em +fluxos de trabalho de maior escala. + +**1. Prefira importações diretas de módulo quando o tempo de inicialização importa.** +Use ``from idecomp.decomp.nome_modulo import NomeClasse`` em vez de +``from idecomp.decomp import NomeClasse`` para evitar o carregamento antecipado de +todos os 41 módulos. Aplique essa estratégia em scripts invocados repetidamente por +pipelines de automação. + +**2. Use geradores para processar arquivos em lote sem acumular todos na memória.** +Se o objetivo é calcular estatísticas ou filtrar linhas, um gerador evita carregar +todos os DataFrames simultaneamente: + +.. code-block:: python + + from pathlib import Path + import pandas as pd + from idecomp.decomp.dec_oper_sist import DecOperSist + + def gerar_tabelas(padrao): + for caminho in Path("./resultados").glob(padrao): + arq = DecOperSist.read(str(caminho)) + yield arq.tabela + del arq + + # Concatena sob demanda, sem manter todos os objetos vivos ao mesmo tempo + df_total = pd.concat(gerar_tabelas("*/dec_oper_sist.csv"), ignore_index=True) + +**3. Filtre os DataFrames imediatamente após a extração.** +Quanto antes as linhas irrelevantes forem descartadas, menor será o consumo de +memória ao longo do processamento. Aplique filtros baseados em colunas como +``estagio``, ``cenario`` ou ``patamar`` logo após acessar a propriedade do arquivo: + +.. code-block:: python + + from idecomp.decomp.dec_oper_sist import DecOperSist + + arq = DecOperSist.read("./dec_oper_sist.csv") + df = arq.tabela + + # Filtra apenas o primeiro estagio antes de qualquer outra operacao + df_filtrado = df[df["estagio"] == 1].copy() + del arq, df + +**4. Libere objetos de arquivo com ``del`` após extrair os dados necessários.** +Objetos de arquivo do *idecomp* não são referências leves — eles armazenam a +representação estruturada completa do arquivo. Descartá-los explicitamente com +``del`` após a extração do DataFrame é especialmente importante em loops com muitas +iterações. + +**5. Utilize ``gc.collect()`` em lotes muito grandes.** +Para processamentos com centenas de arquivos de grande volume, chame ``gc.collect()`` +periodicamente (por exemplo, a cada 50 arquivos) para garantir que objetos +descartados sejam de fato liberados antes que o Python reutilize a memória. Avalie +o impacto com ``tracemalloc`` antes e depois de adotar essa prática: + +.. code-block:: python + + import gc + import tracemalloc + from pathlib import Path + import pandas as pd + from idecomp.decomp.dec_oper_sist import DecOperSist + + tracemalloc.start() + + dfs = [] + caminhos = list(Path("./resultados").glob("*/dec_oper_sist.csv")) + for i, caminho in enumerate(caminhos): + arq = DecOperSist.read(str(caminho)) + dfs.append(arq.tabela) + del arq + if (i + 1) % 50 == 0: + gc.collect() + + df_total = pd.concat(dfs, ignore_index=True) + + memoria_atual, memoria_pico = tracemalloc.get_traced_memory() + tracemalloc.stop() + print(f"Pico de memoria: {memoria_pico / 1024 / 1024:.1f} MB") diff --git a/docs/source/guias/faq.rst b/docs/source/guias/faq.rst new file mode 100644 index 0000000..d9ab723 --- /dev/null +++ b/docs/source/guias/faq.rst @@ -0,0 +1,374 @@ +Perguntas Frequentes (FAQ) +========================== + +Esta página reúne as dúvidas mais comuns sobre o uso do *idecomp*. As respostas estão +organizadas por tema para facilitar a navegação. + +Instalacao +---------- + +Como instalo o idecomp? +^^^^^^^^^^^^^^^^^^^^^^^ + +O *idecomp* está disponível no PyPI e pode ser instalado com o ``pip``: + +.. code-block:: python + + pip install idecomp + +Para instalar uma versão específica: + +.. code-block:: python + + pip install idecomp==1.8.2 + +Recomenda-se utilizar um ambiente virtual (``venv`` ou ``conda``) para evitar conflitos +de dependências. + +Qual versão do Python é compatível com o idecomp? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +O *idecomp* requer **Python 3.8 ou superior**. As versões 3.10, 3.11 e 3.12 são as +mais testadas e recomendadas para uso em produção. + +Estou com problemas de conflito de versão com o cfinterface. O que fazer? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +O *idecomp* depende da biblioteca `cfinterface `_, +que é responsável pelo mecanismo de parse dos arquivos. A versão compatível é fixada em +``<=1.8.3``. Caso você tenha uma versão incompatível instalada, force a reinstalação com +a restrição correta: + +.. code-block:: python + + pip install "cfinterface<=1.8.3" --force-reinstall + +Se o conflito persistir, crie um ambiente virtual limpo e instale o *idecomp* a partir +do zero: + +.. code-block:: python + + python -m venv .venv + source .venv/bin/activate # Linux/macOS + # .venv\Scripts\activate # Windows + pip install idecomp + +Leitura de Arquivos +------------------- + +Como leio um arquivo do DECOMP com o idecomp? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Utilize o método de classe ``read`` da classe correspondente ao arquivo desejado, +passando o caminho para o arquivo no disco: + +.. code-block:: python + + from idecomp.decomp import Dadger + + arq = Dadger.read("./dadger.rv0") + print(arq) + +O mesmo padrão se aplica a todos os arquivos suportados. Por exemplo, para ler o +arquivo de relato: + +.. code-block:: python + + from idecomp.decomp import Relato + + arq = Relato.read("./relato.rv0") + +Quais arquivos do DECOMP são suportados? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +O *idecomp* suporta **41 arquivos** do modelo DECOMP. Todos estão disponíveis no +módulo ``idecomp.decomp``. Os mais utilizados são: + +- ``Dadger`` — arquivo principal de dados gerais do estudo (``dadger.rvX``) +- ``Relato`` — arquivo de relatório de resultados (``relato.rvX``) +- ``Vazoes`` — arquivo de vazões previstas e geradas (``vazoes.rvX``) +- ``Hidr`` — cadastro de usinas hidrelétricas (``hidr.dat``) +- ``Caso`` — arquivo de identificação do caso (``caso.dat``) +- ``DecOperSist`` — operação por submercado em formato CSV (``dec_oper_sist.csv``) +- ``DecOperUsih`` — operação por usina hidrelétrica (``dec_oper_usih.csv``) +- ``DecOperRee`` — operação por reservatório equivalente (``dec_oper_ree.csv``) + +Para ver a lista completa, inspecione o módulo: + +.. code-block:: python + + import idecomp.decomp as decomp + print(dir(decomp)) + +O arquivo não foi encontrado. Como tratar o erro? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Quando o caminho fornecido não existe, o Python levanta ``FileNotFoundError``. Trate +o erro explicitamente para fornecer mensagens mais informativas ao usuário: + +.. code-block:: python + + from idecomp.decomp import Relato + + caminho = "./relato.rv0" + try: + arq = Relato.read(caminho) + except FileNotFoundError: + print(f"Arquivo não encontrado: {caminho}") + print("Verifique se o caminho está correto e se o DECOMP foi executado.") + +Como o idecomp lida com a codificação dos arquivos? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +O *idecomp* utiliza codificação **UTF-8** por padrão para leitura e escrita. Arquivos +gerados por versões antigas do DECOMP podem estar em Latin-1 (ISO-8859-1). Nesse caso, +pode ocorrer ``UnicodeDecodeError`` durante a leitura. + +Se encontrar esse erro, converta o arquivo para UTF-8 antes de processá-lo: + +.. code-block:: python + + # Converte de Latin-1 para UTF-8 + with open("relato.rv0", "r", encoding="latin-1") as f: + conteudo = f.read() + + with open("relato_utf8.rv0", "w", encoding="utf-8") as f: + f.write(conteudo) + + from idecomp.decomp import Relato + arq = Relato.read("./relato_utf8.rv0") + +Escrita de Arquivos +------------------- + +Como escrevo um arquivo de entrada modificado? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Para arquivos de entrada (como ``Dadger``, ``Vazoes``, ``Hidr``), utilize o método +``write`` na instância do objeto após realizar as modificações desejadas: + +.. code-block:: python + + from idecomp.decomp import Vazoes + + arq = Vazoes.read("./vazoes.rv0") + + # Eleva todas as previsões em 10% + df = arq.previsoes + df.iloc[:, 1:] *= 1.1 + arq.previsoes = df + + # Salva o arquivo modificado + arq.write("./vazoes_modificado.rv0") + +Por que não consigo chamar ``write`` em arquivos de saída? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Arquivos de saída do DECOMP — como ``Relato``, ``DecOperSist``, ``DecOperUsih`` e +outros arquivos no formato CSV — são gerados exclusivamente pelo próprio modelo e +**não implementam o método ``write``**. Esses arquivos são somente leitura no contexto +do *idecomp*. + +Tentar chamar ``write`` em uma instância de arquivo de saída levanta ``AttributeError``: + +.. code-block:: python + + from idecomp.decomp import Relato + + arq = Relato.read("./relato.rv0") + # Isso levanta AttributeError: 'Relato' object has no attribute 'write' + # arq.write("./relato.rv0") + +Para modificar resultados, trabalhe diretamente com os DataFrames retornados pelas +propriedades e salve-os em outros formatos (CSV, Parquet, Excel, etc.): + +.. code-block:: python + + from idecomp.decomp import DecOperSist + + arq = DecOperSist.read("./dec_oper_sist.csv") + df = arq.tabela + df.to_csv("./resultado_filtrado.csv", index=False) + +Como lidar com erros de permissão ao escrever? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Se o diretório de destino não existir ou não tiver permissão de escrita, o Python +levantará ``FileNotFoundError`` ou ``PermissionError``. Garanta que o diretório +existe antes de escrever: + +.. code-block:: python + + import os + from idecomp.decomp import Dadger + + arq = Dadger.read("./dadger.rv0") + + diretorio_saida = "./saida/estudo_modificado" + os.makedirs(diretorio_saida, exist_ok=True) + + arq.write(os.path.join(diretorio_saida, "dadger.rv0")) + +DataFrames e Dados +------------------ + +Como acesso os dados de um arquivo como DataFrame? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As propriedades das classes do *idecomp* retornam dados tabulares diretamente como +``pd.DataFrame`` do `pandas `_. Basta acessar a propriedade +correspondente ao dado desejado: + +.. code-block:: python + + from idecomp.decomp import Relato + + arq = Relato.read("./relato.rv0") + + # Retorna pd.DataFrame com as iterações de convergência + df_convergencia = arq.convergencia + print(df_convergencia.dtypes) + print(df_convergencia.head(5)) + +Para descobrir quais propriedades estão disponíveis em uma classe, use ``help()`` ou +inspecione os atributos do objeto: + +.. code-block:: python + + from idecomp.decomp import DecOperSist + + arq = DecOperSist.read("./dec_oper_sist.csv") + # Lista propriedades e métodos públicos + props = [attr for attr in dir(arq) if not attr.startswith("_")] + print(props) + +Como filtro e modifico um DataFrame retornado por uma propriedade? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use as operações padrão do pandas sobre o DataFrame retornado. Para modificar os +dados e persistir a alteração no objeto, atribua o DataFrame modificado de volta +à mesma propriedade: + +.. code-block:: python + + from idecomp.decomp import Vazoes + + arq = Vazoes.read("./vazoes.rv0") + + # Acessa o DataFrame de previsões + df = arq.previsoes + + # Filtra apenas os primeiros 5 estágios + df_filtrado = df[df["estagio"] <= 5].copy() + print(df_filtrado) + + # Modifica todos os valores e atualiza o objeto + df.iloc[:, 1:] *= 1.1 + arq.previsoes = df + arq.write("./vazoes_modificado.rv0") + +Como as propriedades se relacionam com os blocos e registros internos do DECOMP? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Cada propriedade de uma classe de arquivo corresponde a um **registro** (em arquivos +baseados em ``RegisterFile``) ou a um **bloco** (em arquivos baseados em +``BlockFile``). Os modelos internos ficam em ``idecomp/decomp/modelos/``. + +Por exemplo, no ``Dadger`` (``RegisterFile``), a propriedade ``uh`` corresponde ao +registro ``UH`` do ``dadger.rvX``, que define as usinas hidrelétricas do estudo: + +.. code-block:: python + + from idecomp.decomp import Dadger + + arq = Dadger.read("./dadger.rv0") + + # Propriedade 'uh' mapeia para o registro UH do dadger.rvX + df_usinas = arq.uh + print(df_usinas.columns.tolist()) + +No ``Relato`` (``BlockFile``), a propriedade ``convergencia`` mapeia para o bloco +de convergência do ``relato.rvX``, delimitado por um padrão textual específico no +arquivo de saída. + +Para entender a correspondência entre propriedades e registros/blocos de um arquivo +específico, consulte a :doc:`../referencia/decomp/index` ou a documentação do cfinterface. + +Erros Comuns +------------ + +Recebi um erro de parse do cfinterface. O que significa? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Erros de parse geralmente indicam que o arquivo do DECOMP está **malformado** ou em +uma versão não reconhecida. O cfinterface tenta fazer o parse linha a linha e levanta +exceções quando encontra conteúdo inesperado. + +Verifique os pontos mais comuns: + +1. O arquivo está completo e não foi truncado durante a execução do DECOMP. +2. A extensão e o nome do arquivo correspondem ao tipo esperado (``dadger.rv0``, + ``relato.rv0``, ``dec_oper_sist.csv``, etc.). +3. O arquivo não foi editado manualmente de forma incorreta. + +.. code-block:: python + + from idecomp.decomp import Dadger + + # Se o arquivo estiver malformado, use try/except para capturar o erro + try: + arq = Dadger.read("./dadger.rv0") + except Exception as e: + print(f"Erro ao ler o arquivo: {type(e).__name__}: {e}") + print("Verifique se o arquivo dadger.rv0 está íntegro.") + +Como lidar com versões diferentes do DECOMP? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Alguns arquivos de saída CSV do DECOMP possuem estruturas de colunas diferentes +conforme a versão do modelo. O *idecomp* utiliza o dicionário ``VERSIONS`` do +cfinterface para lidar com isso automaticamente: ao ler o arquivo, o *idecomp* +inspeciona o cabeçalho e seleciona a estrutura de colunas correta. + +Caso o arquivo tenha sido gerado por uma versão do DECOMP não reconhecida, o *idecomp* +pode não conseguir fazer o parse corretamente. Verifique a versão do DECOMP que +gerou o arquivo: + +.. code-block:: python + + from idecomp.decomp import DecOperSist + + arq = DecOperSist.read("./dec_oper_sist.csv") + + # Verifica a versão detectada no cabeçalho + if hasattr(arq, "versao"): + print(f"Versão detectada: {arq.versao}") + +Se a versão não for suportada, considere atualizar o *idecomp* para a versão mais +recente, que pode incluir suporte a versões mais novas do DECOMP. + +Uma propriedade está retornando ``None``. Por que? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Quando uma propriedade retorna ``None``, o registro ou bloco correspondente **não +foi encontrado no arquivo**. Isso pode ocorrer quando: + +1. O arquivo não contém aquele bloco de dados (por exemplo, o estudo não modelou + a feature correspondente). +2. O arquivo está em uma versão mais antiga do DECOMP que não gerava aquele campo. +3. O arquivo está correto, mas o dado esperado não foi gerado nesta execução. + +Sempre verifique se a propriedade é ``None`` antes de operar sobre ela: + +.. code-block:: python + + from idecomp.decomp import Relato + + arq = Relato.read("./relato.rv0") + + df_cmo = arq.cmo_medio_subsistema + if df_cmo is None: + print("Dados de CMO não encontrados no arquivo relato.rv0.") + print("Verifique se o estudo incluiu o cálculo de CMO.") + else: + print(df_cmo.head()) diff --git a/docs/source/index.rst b/docs/source/index.rst index 8752326..63bb7e8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,13 +9,13 @@ Interface de Programação para o DECOMP **Data:** |today| .. toctree:: - :caption: Apresentação + :caption: Apresentação :maxdepth: 3 apresentacao/apresentacao.rst .. toctree:: - :caption: Geral + :caption: Geral :maxdepth: 3 geral/instalacao @@ -23,6 +23,14 @@ Interface de Programação para o DECOMP examples/index.rst geral/contribuicao +.. toctree:: + :caption: Guias + :maxdepth: 2 + + guias/arquitetura + guias/desempenho + guias/faq + .. toctree:: :caption: Referência :maxdepth: 2 @@ -30,4 +38,4 @@ Interface de Programação para o DECOMP referencia/decomp/index.rst referencia/libs/index.rst -:ref:`genindex` \ No newline at end of file +:ref:`genindex` diff --git a/docs/source/referencia/decomp/index.rst b/docs/source/referencia/decomp/index.rst index 7592e39..0e6c8a7 100644 --- a/docs/source/referencia/decomp/index.rst +++ b/docs/source/referencia/decomp/index.rst @@ -3,6 +3,51 @@ DECOMP ======= +.. autosummary:: + :nosignatures: + + idecomp.decomp.arquivos.Arquivos + idecomp.decomp.avl_cortesfpha_dec.AvlCortesFpha + idecomp.decomp.avl_turb_max.AvlTurbMax + idecomp.decomp.caso.Caso + idecomp.decomp.cortdeco.Cortdeco + idecomp.decomp.custos.Custos + idecomp.decomp.dadger.Dadger + idecomp.decomp.dadgnl.Dadgnl + idecomp.decomp.decomptim.Decomptim + idecomp.decomp.dec_avl_evap.DecAvlEvap + idecomp.decomp.dec_cortes_evap.DecCortesEvap + idecomp.decomp.dec_desvfpha.DecDesvFpha + idecomp.decomp.dec_eco_cotajus.DecEcoCotajus + idecomp.decomp.dec_eco_discr.DecEcoDiscr + idecomp.decomp.dec_eco_evap.DecEcoEvap + idecomp.decomp.dec_eco_qlat.DecEcoQlat + idecomp.decomp.dec_estatevap.DecEstatEvap + idecomp.decomp.dec_estatfpha.DecEstatFpha + idecomp.decomp.dec_fcf_cortes.DecFcfCortes + idecomp.decomp.dec_oper_evap.DecOperEvap + idecomp.decomp.dec_oper_gnl.DecOperGnl + idecomp.decomp.dec_oper_interc.DecOperInterc + idecomp.decomp.dec_oper_ree.DecOperRee + idecomp.decomp.dec_oper_rhesoft.DecOperRheSoft + idecomp.decomp.dec_oper_sist.DecOperSist + idecomp.decomp.dec_oper_usie.DecOperUsie + idecomp.decomp.dec_oper_usih.DecOperUsih + idecomp.decomp.dec_oper_usit.DecOperUsit + idecomp.decomp.eco_fpha.EcoFpha + idecomp.decomp.fcfnw.Fcfnw + idecomp.decomp.hidr.Hidr + idecomp.decomp.inviabunic.InviabUnic + idecomp.decomp.mapcut.Mapcut + idecomp.decomp.oper_desvio_fpha.OperDesvioFpha + idecomp.decomp.oper_disp_usih.OperDispUsih + idecomp.decomp.oper_disp_usih_ree.OperDispUsihRee + idecomp.decomp.oper_disp_usih_subm.OperDispUsihSubm + idecomp.decomp.postos.Postos + idecomp.decomp.relato.Relato + idecomp.decomp.relgnl.Relgnl + idecomp.decomp.vazoes.Vazoes + .. toctree:: :maxdepth: 2 diff --git a/docs/source/referencia/libs/index.rst b/docs/source/referencia/libs/index.rst index 00652a8..9f0cd06 100644 --- a/docs/source/referencia/libs/index.rst +++ b/docs/source/referencia/libs/index.rst @@ -3,6 +3,12 @@ LIBS ======= +.. autosummary:: + :nosignatures: + + idecomp.libs.restricoes.Restricoes + idecomp.libs.usinas_hidreletricas.UsinasHidreletricas + .. toctree:: :maxdepth: 2 diff --git a/plans/infra-docs-overhaul/.implementation-state.json b/plans/infra-docs-overhaul/.implementation-state.json index 107b7af..17d7275 100644 --- a/plans/infra-docs-overhaul/.implementation-state.json +++ b/plans/infra-docs-overhaul/.implementation-state.json @@ -1,6 +1,6 @@ { "plan": "infra-docs-overhaul", - "last_updated": "2026-03-08T20:35:09Z", + "last_updated": "2026-03-09T21:12:02Z", "progressive": true, "tickets": { "ticket-001-modernize-pyproject-toml.md": { @@ -172,24 +172,124 @@ "agent": "open-source-documentation-writer" }, "ticket-008-create-architecture-page.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-009-create-faq-page.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-010-create-performance-guide.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-011-improve-api-reference-autosummary.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-012-update-index-toctree.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-013-expand-readme.md": { "status": "pending", @@ -218,8 +318,8 @@ "learning_extracted": true }, "epic-03-documentation-content": { - "phase": "outline", - "learning_extracted": false + "phase": "completed", + "learning_extracted": true }, "epic-04-repository-polish": { "phase": "outline", diff --git a/plans/infra-docs-overhaul/README.md b/plans/infra-docs-overhaul/README.md index d78e53c..a5fb46f 100644 --- a/plans/infra-docs-overhaul/README.md +++ b/plans/infra-docs-overhaul/README.md @@ -14,9 +14,9 @@ Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentat | Epic | Name | Tickets | Detail Level | Phase | | ---- | ------------------------------- | ------- | ------------ | --------- | -| 1 | Packaging & CI Modernization | 5 | Detailed | executing | -| 2 | Sphinx Modernization | 2 | Detailed | executing | -| 3 | Documentation Content Expansion | 5 | Outline | outline | +| 1 | Packaging & CI Modernization | 5 | Detailed | completed | +| 2 | Sphinx Modernization | 2 | Detailed | completed | +| 3 | Documentation Content Expansion | 5 | Refined | completed | | 4 | Repository Polish | 4 | Outline | outline | ## Progress @@ -30,11 +30,11 @@ Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentat | ticket-005 | Add Pre-commit Hooks Configuration | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | | ticket-006 | Migrate Sphinx Theme to Furo | epic-02 | completed | Detailed | 1.00 | 0.85 | ACCEPTABLE | | ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | epic-02 | completed | Detailed | 0.94 | 1.00 | EXCELLENT | -| ticket-008 | Create Architecture Documentation Page | epic-03 | pending | Outline | -- | -- | -- | -| ticket-009 | Create FAQ Documentation Page | epic-03 | pending | Outline | -- | -- | -- | -| ticket-010 | Create Performance Guide Page | epic-03 | pending | Outline | -- | -- | -- | -| ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | pending | Outline | -- | -- | -- | -| ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | pending | Outline | -- | -- | -- | +| ticket-008 | Create Architecture Documentation Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-009 | Create FAQ Documentation Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-010 | Create Performance Guide Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | | ticket-013 | Expand README with Badges and Sections | epic-04 | pending | Outline | -- | -- | -- | | ticket-014 | Create CONTRIBUTING.md | epic-04 | pending | Outline | -- | -- | -- | | ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | pending | Outline | -- | -- | -- | @@ -62,5 +62,6 @@ ticket-016 (installation docs) [independent] ## Notes - This is a **progressive plan**: Epics 1-2 have fully detailed tickets; Epics 3-4 have outline tickets that will be refined with learnings from earlier epics +- Epic 3 tickets have been **refined** with learnings from Epics 1-2 and are ready for implementation - All documentation content must be written in **Brazilian Portuguese** (pt_BR) - Key learnings from the inewave overhaul are embedded in the detailed tickets (regex version extraction, mypy manual stage, cfinterface workarounds) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md index a8ae230..af7845d 100644 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md @@ -1,32 +1,117 @@ # ticket-008 Create Architecture Documentation Page -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Create a new `arquitetura.rst` page in the documentation that describes idecomp's package structure, the cfinterface framework integration, the decomp/ and libs/ module organization, lazy import patterns, and the data flow from DECOMP files to pandas DataFrames. All content in Brazilian Portuguese. This page helps new users and contributors understand how idecomp is architected before diving into the API reference. +The idecomp documentation currently has an "Apresentacao" (overview) section and a "Geral" section with installation, tutorial, and contribution pages, but lacks a dedicated architecture page explaining the package's internal structure. New users and contributors have no guide to understand how idecomp maps DECOMP binary/text files to Python objects via the cfinterface framework. This ticket creates an `arquitetura.rst` page in a new `docs/source/guias/` directory to fill that gap. All content is written in Brazilian Portuguese. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `docs/source/guias/arquitetura.rst` (new file) - - `docs/source/index.rst` (adding to toctree, but done in ticket-012) -- **Key decisions needed**: - - Whether to create a `docs/source/guias/` directory or place the file in `docs/source/geral/` - - Whether to include UML-style diagrams or keep it text-only - - How deeply to describe cfinterface internals vs. linking to cfinterface docs -- **Open questions**: - - Does the `idecomp/decomp/__init__.py` eager import pattern (not lazy) need to be documented as-is, or is a migration to lazy imports planned? - - Should the architecture page include the `idecomp/config.py` module's role? - - What level of detail for the `idecomp/decomp/modelos/` subdirectory? +This is the first of three guide pages (architecture, FAQ, performance) that comprise Epic 3's content expansion. The architecture page provides foundational understanding that the FAQ and performance pages build upon. Ticket-012 will add this page to the index.rst toctree. + +### Current State + +- `docs/source/guias/` directory does not exist yet; it must be created. +- The Furo theme is active (`html_theme = "furo"` in `docs/source/conf.py`), with `language = "pt_BR"`. +- `idecomp/__init__.py` eagerly imports the `decomp` subpackage (`from . import decomp`). +- `idecomp/decomp/__init__.py` eagerly imports all 41 file classes (Dadger, Relato, DecOperSist, etc.). +- File classes in `idecomp/decomp/` inherit from cfinterface base classes: `RegisterFile` (for register-based files like dadger) and `BlockFile` (for block-based files like relato). CSV-based outputs use `ArquivoCSV`. +- Register/Block models live in `idecomp/decomp/modelos/` (45 files) and `idecomp/decomp/modelos/blocos/`. +- `idecomp/libs/` contains 2 modules: `restricoes.py` and `usinas_hidreletricas.py` with a `modelos/` subdirectory. +- intersphinx is configured for cfinterface at `https://rjmalves.github.io/cfinterface/`. + +## Specification + +### Requirements + +1. Create directory `docs/source/guias/` if it does not exist. +2. Create file `docs/source/guias/arquitetura.rst` containing: + - A title "Arquitetura do idecomp" + - A section "Visao Geral" (2-3 paragraphs) explaining that idecomp provides a Python interface to DECOMP files, built on the cfinterface framework. + - A section "Estrutura de Pacotes" describing the top-level layout: `idecomp/decomp/` (41 file classes for DECOMP files), `idecomp/libs/` (2 classes for auxiliary LIBS files), `idecomp/decomp/modelos/` (register/block models), `idecomp/decomp/modelos/blocos/` (block definitions), `idecomp/decomp/modelos/arquivoscsv/` (CSV-based output adapter). + - A section "Framework cfinterface" explaining the three file base classes: `RegisterFile` (e.g., Dadger), `BlockFile` (e.g., Relato), and `ArquivoCSV` (e.g., DecOperSist), with intersphinx cross-references to cfinterface documentation. + - A section "Fluxo de Dados" describing the data flow: raw DECOMP file on disk -> `ClassName.read(path)` -> parsed Python object -> `.property` accessors return `pd.DataFrame` or typed values -> optional `.write(path)` for input files. + - A section "Modelos e Registros" explaining how each file class delegates parsing to model classes in `modelos/` (Register subclasses for line-oriented formats, Block subclasses for section-oriented formats). +3. All prose must be in Brazilian Portuguese (pt_BR). +4. Use `.. code-block:: python` for all code examples (not anonymous `::` blocks) per Furo dark-mode convention from learnings. +5. Include at least one code example showing the read/write flow (e.g., `Dadger.read()` and property access). +6. Cross-reference cfinterface classes using intersphinx (e.g., `:class:`cfinterface.files.registerfile.RegisterFile``). + +### Inputs/Props + +- No runtime inputs. This is a static RST documentation file. + +### Outputs/Behavior + +- A new RST file at `docs/source/guias/arquitetura.rst` that renders correctly with `uv run sphinx-build -M html docs/source docs/build`. +- The page appears under the Furo sidebar when added to the toctree (done in ticket-012). + +### Error Handling + +- Not applicable (static documentation content). + +## Acceptance Criteria + +- [ ] Given the directory `docs/source/guias/` does not exist, when the ticket is implemented, then the directory `docs/source/guias/` exists and contains the file `arquitetura.rst`. +- [ ] Given the file `docs/source/guias/arquitetura.rst` exists, when running `uv run sphinx-build -M html docs/source docs/build 2>&1 | grep -c "arquitetura"`, then the output count is >= 1 (file is processed) and the build exits with code 0. +- [ ] Given the file `docs/source/guias/arquitetura.rst` is opened, when inspecting its content, then it contains exactly 5 sections: "Visao Geral", "Estrutura de Pacotes", "Framework cfinterface", "Fluxo de Dados", and "Modelos e Registros". +- [ ] Given the file `docs/source/guias/arquitetura.rst` is opened, when searching for `.. code-block:: python`, then at least 1 match is found (code example present with explicit directive). + +## Implementation Guide + +### Suggested Approach + +1. Create the `docs/source/guias/` directory. +2. Write `arquitetura.rst` with the RST structure. Use the existing `docs/source/geral/tutorial.rst` as a style reference for Brazilian Portuguese prose and `.. code-block:: python` usage. +3. For the package structure section, reference the actual module layout: `idecomp/decomp/__init__.py` imports 41 classes; `idecomp/libs/__init__.py` imports `UsinasHidreletricas`; `idecomp/decomp/modelos/` contains 45+ model files. +4. For the cfinterface section, use intersphinx cross-references. The mapping is already configured in `conf.py` line 69: `"cfinterface": ("https://rjmalves.github.io/cfinterface/", None)`. +5. For the data flow section, use a simple code example like: + ```python + from idecomp.decomp import Dadger + arq = Dadger.read("./dadger.rv0") + titulo = arq.te # acessa o registro TE + ``` +6. Verify the build locally: `uv run sphinx-build -M html docs/source docs/build`. + +### Key Files to Modify + +- `docs/source/guias/arquitetura.rst` (new file, ~80-120 lines) + +### Patterns to Follow + +- Follow the RST heading style used in `docs/source/geral/tutorial.rst`: `=` underline for title, `-` underline for sections. +- Follow the Brazilian Portuguese prose style from `tutorial.rst` and `apresentacao.rst`. +- Use `.. code-block:: python` (never anonymous `::` blocks) per Furo convention from learnings. + +### Pitfalls to Avoid + +- Do NOT add the page to `index.rst` toctree -- that is ticket-012's scope. +- Do NOT add Furo to the `extensions` list in `conf.py` -- it is a theme, not an extension (learnings). +- Do NOT use anonymous `::` code blocks -- Furo's monokai dark style only applies to `.. code-block::` directives. +- Do NOT deeply document cfinterface internals -- link to cfinterface docs via intersphinx instead. + +## Testing Requirements + +### Unit Tests + +- Not applicable (documentation content). + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. +- Verify the built HTML file exists at `docs/build/html/guias/arquitetura.html`. + +### E2E Tests + +- Not applicable. ## Dependencies -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (Furo theme must be active for consistent docs build) +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (completed -- Furo theme is active) - **Blocks**: ticket-012-update-index-toctree.md ## Effort Estimate -**Points**: 3 -**Confidence**: Low (will be re-estimated during refinement) +**Points**: 2 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md index 7377a01..f824cb6 100644 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md @@ -1,31 +1,128 @@ # ticket-009 Create FAQ Documentation Page -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Create a new `faq.rst` page with at least 15 frequently asked questions about using idecomp to handle DECOMP files. The FAQ covers installation issues, common reading/writing patterns, DataFrame manipulation, error handling, and compatibility with different DECOMP versions. All content in Brazilian Portuguese, organized into 5 thematic sections. +The idecomp documentation has no FAQ page. Users encountering common issues with DECOMP file handling -- installation problems, read/write patterns, DataFrame manipulation, error messages, and version compatibility -- must search through scattered tutorial content or open GitHub issues. A dedicated FAQ page in Brazilian Portuguese consolidates answers to the most common questions in one discoverable location. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `docs/source/guias/faq.rst` (new file) -- **Key decisions needed**: - - The 5 section categories (e.g., Instalacao, Leitura de Arquivos, Escrita de Arquivos, DataFrames, Erros Comuns) - - Which specific DECOMP file formats to highlight in FAQ examples - - Whether to reference specific class names or keep questions generic -- **Open questions**: - - Are there known user pain points from GitHub Issues that should be prioritized? - - Should the FAQ include questions about cfinterface directly or redirect to cfinterface docs? - - Should the FAQ reference the `tests/mocks/` directory structure for users who want example files? +This is the second of three guide pages in Epic 3. It is independent of ticket-008 (architecture) and ticket-010 (performance) and can be implemented in parallel. Ticket-012 will add this page to the index.rst toctree. + +### Current State + +- `docs/source/guias/` directory may or may not exist yet (ticket-008 creates it; this ticket must also create it if not present). +- The tutorial page (`docs/source/geral/tutorial.rst`) demonstrates the `ClassName.read()` / `.write()` pattern and DataFrame property access, serving as a style reference. +- `idecomp/decomp/__init__.py` exports 41 file classes. The most commonly used are: `Dadger`, `Relato`, `DecOperSist`, `Vazoes`, `Hidr`, `Caso`. +- File classes use `ClassName.read(path)` for reading and `instance.write(path)` for writing (input files only; output files have no `write` method). +- Some files support versioning via cfinterface's `VERSIONS` dict (e.g., `DecOperSist` has versions `"31.0.2"` and `"31.1.2"`). +- Mock data for examples lives at `examples/decomp/` (dadger.rv0, dec_oper_sist.csv, relato.rv0). + +## Specification + +### Requirements + +1. Create directory `docs/source/guias/` if it does not exist. +2. Create file `docs/source/guias/faq.rst` containing: + - A title "Perguntas Frequentes (FAQ)" + - Organized into 5 thematic sections using RST section headers: + 1. **Instalacao** (3 questions): pip install issues, version conflicts, cfinterface dependency. + 2. **Leitura de Arquivos** (4 questions): how to read a file, supported file formats, handling missing files, file encoding. + 3. **Escrita de Arquivos** (3 questions): how to write modified input files, why output files cannot be written, how to handle write errors. + 4. **DataFrames e Dados** (3 questions): how to access data as DataFrames, how to filter/modify DataFrames, how properties map to DECOMP blocks/registers. + 5. **Erros Comuns** (3 questions): common error messages and their solutions (e.g., cfinterface parse errors, version mismatch, missing registers). + - Minimum 16 Q&A entries total across all sections. + - Each question uses a bold RST inline markup or a subsection header. + - Each answer includes at least one `.. code-block:: python` example where applicable (minimum 8 code examples across all answers). +3. All prose must be in Brazilian Portuguese (pt_BR). +4. Use `.. code-block:: python` for all code examples (not anonymous `::` blocks). +5. Code examples must use the current class-based API (`ClassName.read()`), never deprecated patterns. + +### Inputs/Props + +- No runtime inputs. Static RST documentation file. + +### Outputs/Behavior + +- A new RST file at `docs/source/guias/faq.rst` that renders correctly with `uv run sphinx-build -M html docs/source docs/build`. + +### Error Handling + +- Not applicable (static documentation content). + +## Acceptance Criteria + +- [ ] Given the ticket is implemented, when checking the filesystem, then the file `docs/source/guias/faq.rst` exists. +- [ ] Given `docs/source/guias/faq.rst` exists, when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0 and `docs/build/html/guias/faq.html` is produced. +- [ ] Given the file `docs/source/guias/faq.rst` is opened, when counting RST section headers at the second level (underlined with `-`), then there are exactly 5 thematic sections. +- [ ] Given the file `docs/source/guias/faq.rst` is opened, when counting question entries (bold text lines or third-level headers), then there are at least 16 entries. +- [ ] Given the file `docs/source/guias/faq.rst` is opened, when counting occurrences of `.. code-block:: python`, then there are at least 8 occurrences. + +## Implementation Guide + +### Suggested Approach + +1. Create `docs/source/guias/` directory if it does not exist. +2. Write `faq.rst` with the 5-section structure. Use the following question structure for consistency: + + ```rst + Como ler um arquivo do DECOMP? + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Para ler qualquer arquivo do DECOMP, utilize o metodo ``read`` da classe correspondente: + + .. code-block:: python + + from idecomp.decomp import Dadger + arq = Dadger.read("./dadger.rv0") + ``` + +3. For the "Instalacao" section, cover: `pip install idecomp`, cfinterface version pin (`<=1.8.3`), and Python version compatibility. +4. For "Leitura de Arquivos", cover: basic read pattern, listing all 41 supported classes, handling `FileNotFoundError`, and file encoding (UTF-8 vs Latin-1 in older DECOMP files). +5. For "Escrita de Arquivos", cover: the `write()` method on input files, explain that output files (Relato, DecOperSist, etc.) are read-only, and handling write permissions. +6. For "DataFrames e Dados", cover: how properties like `arq.convergencia` return `pd.DataFrame`, how to filter DataFrames, and how properties map to internal Register/Block models. +7. For "Erros Comuns", cover: cfinterface parse errors (malformed DECOMP files), version detection with `VERSIONS` dict, and missing register/block handling. +8. Verify locally: `uv run sphinx-build -M html docs/source docs/build`. + +### Key Files to Modify + +- `docs/source/guias/faq.rst` (new file, ~150-200 lines) + +### Patterns to Follow + +- Use `^` underline for third-level subsection headers (questions) within `-`-underlined sections, matching RST conventions from `docs/source/geral/tutorial.rst`. +- Brazilian Portuguese prose style from existing docs. +- `.. code-block:: python` for all code (Furo dark-mode convention). + +### Pitfalls to Avoid + +- Do NOT add the page to `index.rst` toctree -- that is ticket-012's scope. +- Do NOT use deprecated API patterns (`le_arquivo`, `escreve_arquivo`) -- all examples must use `ClassName.read()` / `.write()`. +- Do NOT reference `tests/mocks/` directory in examples -- use generic file paths that users would have in their own environments. +- Do NOT use anonymous `::` code blocks. + +## Testing Requirements + +### Unit Tests + +- Not applicable (documentation content). + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. +- Verify `docs/build/html/guias/faq.html` exists. + +### E2E Tests + +- Not applicable. ## Dependencies -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (completed) - **Blocks**: ticket-012-update-index-toctree.md ## Effort Estimate -**Points**: 3 -**Confidence**: Low (will be re-estimated during refinement) +**Points**: 2 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md index 4f418fb..6444b29 100644 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md @@ -1,31 +1,135 @@ # ticket-010 Create Performance Guide Page -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Create a new `desempenho.rst` page documenting performance characteristics and optimization strategies for idecomp. Covers import times, file read/write times for common DECOMP files, batch processing patterns, memory usage considerations, and benchmarking guidance. All content in Brazilian Portuguese. +The idecomp documentation has no guidance on performance characteristics or optimization strategies. Users processing large DECOMP datasets or running batch operations have no reference for expected read/write times, memory considerations, or efficient patterns. This ticket creates a `desempenho.rst` page with practical performance guidance, written in Brazilian Portuguese. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `docs/source/guias/desempenho.rst` (new file) -- **Key decisions needed**: - - Whether to include actual benchmark numbers or describe how to run benchmarks - - Which DECOMP files to profile (dadger, relato, dec_oper_sist are likely candidates) - - Whether to mention the eager import pattern in `idecomp/decomp/__init__.py` as a performance consideration -- **Open questions**: - - Does idecomp have a `benchmarks/` directory? (Not observed in the repo root; may need to be created or the guide refers to ad-hoc profiling) - - Should the guide include comparison with raw file parsing (without idecomp)? - - Should lazy import migration be recommended as a performance improvement? +This is the third of three guide pages in Epic 3. It is independent of ticket-008 and ticket-009 and can be implemented in parallel. Ticket-012 will add this page to the index.rst toctree. + +### Current State + +- `docs/source/guias/` directory may or may not exist yet (created by ticket-008 or ticket-009). +- `idecomp/__init__.py` eagerly imports the `decomp` subpackage, which in turn eagerly imports all 41 file classes from `idecomp/decomp/__init__.py`. This means `import idecomp` triggers the import of all 41 class definitions. +- There is no `benchmarks/` directory in the repository. +- The codebase uses `pandas` DataFrames for all tabular data output. File classes like `DecOperSist` can produce large DataFrames with columns for estagio, cenario, patamar, etc. +- Some file classes support version detection via cfinterface's `VERSIONS` dict (e.g., `DecOperSist` checks versions `"31.0.2"` and `"31.1.2"`). +- Gallery examples in `examples/` demonstrate typical usage patterns with mock data. + +## Specification + +### Requirements + +1. Create directory `docs/source/guias/` if it does not exist. +2. Create file `docs/source/guias/desempenho.rst` containing: + - A title "Guia de Desempenho" + - A section "Importacao" (2-3 paragraphs) documenting the eager import behavior: `import idecomp` loads all 41 class definitions. Recommend importing specific classes when startup time matters (`from idecomp.decomp.dadger import Dadger` instead of `from idecomp.decomp import Dadger`). + - A section "Leitura e Escrita de Arquivos" describing typical I/O patterns: single-file read, batch processing of multiple files (e.g., reading all `dec_oper_sist.csv` files from multiple scenarios), and tips for efficient batch processing (reading files in a loop, collecting DataFrames, concatenating with `pd.concat`). + - A section "Uso de Memoria" explaining that each read file holds its parsed data in memory; for large batch operations, recommend reading, extracting the needed DataFrame, and discarding the file object. Include a code example showing this pattern. + - A section "Dicas de Otimizacao" with 3-5 practical tips: use specific imports, process files in batches with generators, filter DataFrames early, use `del` to free memory, and consider `gc.collect()` for very large batch operations. +3. All prose must be in Brazilian Portuguese (pt_BR). +4. Use `.. code-block:: python` for all code examples (not anonymous `::` blocks). +5. Include at least 3 code examples showing performance-relevant patterns. +6. Do NOT include actual benchmark numbers (the repository has no benchmarks directory). Instead, describe how users can profile their own workflows using Python's `time` module. + +### Inputs/Props + +- No runtime inputs. Static RST documentation file. + +### Outputs/Behavior + +- A new RST file at `docs/source/guias/desempenho.rst` that renders correctly with `uv run sphinx-build -M html docs/source docs/build`. + +### Error Handling + +- Not applicable (static documentation content). + +## Acceptance Criteria + +- [ ] Given the ticket is implemented, when checking the filesystem, then the file `docs/source/guias/desempenho.rst` exists. +- [ ] Given `docs/source/guias/desempenho.rst` exists, when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0 and `docs/build/html/guias/desempenho.html` is produced. +- [ ] Given the file `docs/source/guias/desempenho.rst` is opened, when inspecting section headers, then it contains exactly 4 sections: "Importacao", "Leitura e Escrita de Arquivos", "Uso de Memoria", and "Dicas de Otimizacao". +- [ ] Given the file `docs/source/guias/desempenho.rst` is opened, when counting occurrences of `.. code-block:: python`, then there are at least 3 occurrences. + +## Implementation Guide + +### Suggested Approach + +1. Create `docs/source/guias/` directory if it does not exist. +2. Write `desempenho.rst` following the 4-section structure. +3. For the "Importacao" section, demonstrate the two import styles: + + ```python + # Importa todos os 41 modulos (mais lento) + from idecomp.decomp import Dadger + + # Importa apenas o modulo necessario (mais rapido) + from idecomp.decomp.dadger import Dadger + ``` + +4. For the "Leitura e Escrita" section, show a batch processing pattern: + + ```python + from pathlib import Path + import pandas as pd + from idecomp.decomp.dec_oper_sist import DecOperSist + + dfs = [] + for caminho in Path("./resultados").glob("*/dec_oper_sist.csv"): + arq = DecOperSist.read(str(caminho)) + dfs.append(arq.tabela) + df_total = pd.concat(dfs, ignore_index=True) + ``` + +5. For "Uso de Memoria", show the read-extract-discard pattern: + ```python + arq = DecOperSist.read("./dec_oper_sist.csv") + df = arq.tabela + del arq # libera o objeto do arquivo da memoria + ``` +6. Verify locally: `uv run sphinx-build -M html docs/source docs/build`. + +### Key Files to Modify + +- `docs/source/guias/desempenho.rst` (new file, ~80-120 lines) + +### Patterns to Follow + +- RST heading style from `docs/source/geral/tutorial.rst`: `=` for title, `-` for sections. +- Brazilian Portuguese prose style from existing docs. +- `.. code-block:: python` for all code examples. + +### Pitfalls to Avoid + +- Do NOT add the page to `index.rst` toctree -- that is ticket-012's scope. +- Do NOT include actual benchmark numbers -- the repo has no benchmarks infrastructure, and numbers would become stale. +- Do NOT recommend lazy import migration -- that is a code change outside the scope of documentation. +- Do NOT use anonymous `::` code blocks. + +## Testing Requirements + +### Unit Tests + +- Not applicable (documentation content). + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. +- Verify `docs/build/html/guias/desempenho.html` exists. + +### E2E Tests + +- Not applicable. ## Dependencies -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md +- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (completed) - **Blocks**: ticket-012-update-index-toctree.md ## Effort Estimate -**Points**: 3 -**Confidence**: Low (will be re-estimated during refinement) +**Points**: 2 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md index ab63c58..a73bd2a 100644 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md +++ b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md @@ -1,34 +1,127 @@ # ticket-011 Improve API Reference with Autosummary Blocks -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Enhance the API reference pages in `docs/source/referencia/decomp/index.rst` and `docs/source/referencia/libs/index.rst` with autosummary directive blocks that generate method/attribute tables for each documented class. Currently these pages are plain toctree listings; autosummary adds navigable tables showing the public interface at a glance. +The API reference pages for `decomp/` and `libs/` currently use plain `.. toctree::` listings that link to individual class pages but provide no at-a-glance summary of what each class does. Sphinx's `.. autosummary::` directive can generate summary tables showing each class with its one-line docstring, making the API reference more navigable. The `autosummary_generate = True` setting is already enabled in `conf.py`, and the `sphinx.ext.autosummary` extension is already loaded. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `docs/source/referencia/decomp/index.rst` (add autosummary blocks) - - `docs/source/referencia/libs/index.rst` (add autosummary blocks) - - Individual `.rst` files in `docs/source/referencia/decomp/arquivos/` (may need autosummary directives per class) - - `docs/source/conf.py` (may need autosummary template customization) -- **Key decisions needed**: - - Whether to use `.. autosummary::` at the module index level or at each individual class page - - Whether to create custom autosummary templates in `_templates/` - - The canonical public class list for decomp/ (the 34 classes in `idecomp/decomp/__init__.py`) -- **Open questions**: - - Does the existing `autosummary_generate = True` in `conf.py` already generate stubs? - - Should autosummary show methods and attributes, or just classes? - - Are there autosummary templates from inewave that can be reused? +This ticket is independent of the three guide pages (tickets 008-010) and can be implemented in parallel. It improves the existing API reference section rather than creating new guide content. + +### Current State + +- `docs/source/referencia/decomp/index.rst` contains a `.. toctree::` with 41 entries pointing to individual class RST files in `arquivos/`. +- `docs/source/referencia/libs/index.rst` contains a `.. toctree::` with 2 entries (`restricoes`, `usinas_hidreletricas`). +- Individual class RST files (e.g., `docs/source/referencia/decomp/arquivos/dadger.rst`) use `.. autoclass::` with `:members:` to document each class and its register models. +- `conf.py` has `autosummary_generate = True` (line 30), `sphinx.ext.autosummary` in extensions (line 20), and `templates_path = ["_templates"]` (line 31). +- The `docs/source/_templates/autosummary/` directory does not exist yet. +- `numpydoc_show_class_members = False` (line 61) suppresses member tables on class pages; this ticket does NOT change that setting. + +## Specification + +### Requirements + +1. Add an `.. autosummary::` block to `docs/source/referencia/decomp/index.rst` listing all 41 file classes from `idecomp.decomp`. The block must appear before the existing `.. toctree::` block and use the `:nosignatures:` option to keep the table clean. +2. Add an `.. autosummary::` block to `docs/source/referencia/libs/index.rst` listing the 2 classes from `idecomp.libs` (`Restricoes`, `UsinasHidreletricas`). Same placement and options. +3. Each autosummary entry uses the fully qualified module path (e.g., `idecomp.decomp.dadger.Dadger`) so that autosummary can resolve the class and extract its docstring. +4. Keep the existing `.. toctree::` blocks intact -- the autosummary table supplements them, it does not replace them. +5. Add a brief introductory paragraph in Brazilian Portuguese above each autosummary block explaining that the table below shows all available classes with descriptions. + +### Inputs/Props + +- No runtime inputs. Modifications to existing RST files. + +### Outputs/Behavior + +- The `decomp/index.rst` page renders an autosummary table with 41 rows (one per file class) showing class name and one-line docstring. +- The `libs/index.rst` page renders an autosummary table with 2 rows. +- The existing toctree navigation is preserved unchanged. + +### Error Handling + +- Not applicable (static documentation content). + +## Acceptance Criteria + +- [ ] Given the file `docs/source/referencia/decomp/index.rst` is opened, when searching for `.. autosummary::`, then exactly 1 autosummary block is found containing 41 class entries. +- [ ] Given the file `docs/source/referencia/libs/index.rst` is opened, when searching for `.. autosummary::`, then exactly 1 autosummary block is found containing 2 class entries. +- [ ] Given both index files are modified, when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0. +- [ ] Given the build succeeds, when opening `docs/build/html/referencia/decomp/index.html` in a browser, then a summary table is visible listing class names with their docstring descriptions. + +## Implementation Guide + +### Suggested Approach + +1. Edit `docs/source/referencia/decomp/index.rst`: + - After the title and label, add a paragraph in Portuguese: "A tabela abaixo lista todas as classes disponiveis para manipulacao dos arquivos do DECOMP." + - Add the autosummary block: + + ```rst + .. autosummary:: + :nosignatures: + + idecomp.decomp.dadger.Dadger + idecomp.decomp.relato.Relato + idecomp.decomp.dec_oper_sist.DecOperSist + ... + ``` + + - List all 41 classes using the fully qualified path from their source module (not from the `__init__.py` re-export), as autosummary needs the actual module path to resolve docstrings. + - Keep the existing `.. toctree::` block below the autosummary block. + +2. Edit `docs/source/referencia/libs/index.rst`: + - Add a similar paragraph and autosummary block with: + + ```rst + .. autosummary:: + :nosignatures: + + idecomp.libs.restricoes.Restricoes + idecomp.libs.usinas_hidreletricas.UsinasHidreletricas + ``` + +3. Build and verify: `uv run sphinx-build -M html docs/source docs/build`. + +### Key Files to Modify + +- `docs/source/referencia/decomp/index.rst` (add ~50 lines for autosummary block) +- `docs/source/referencia/libs/index.rst` (add ~10 lines for autosummary block) + +### Patterns to Follow + +- Use `:nosignatures:` option on autosummary to keep tables clean (class names only, no `__init__` signatures). +- Use fully qualified module paths for autosummary entries (e.g., `idecomp.decomp.dadger.Dadger`), not re-exported paths. + +### Pitfalls to Avoid + +- Do NOT remove or modify the existing `.. toctree::` blocks -- they control sidebar navigation. +- Do NOT change `numpydoc_show_class_members` in `conf.py` -- it is intentionally `False`. +- Do NOT create custom autosummary templates in `_templates/` -- the default templates are sufficient for class summary tables. +- Do NOT use `.. autosummary:: :toctree:` option -- the individual class pages already exist via the toctree; adding `:toctree:` to autosummary would generate duplicate stub pages. + +## Testing Requirements + +### Unit Tests + +- Not applicable (documentation content). + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. +- Verify `docs/build/html/referencia/decomp/index.html` contains ` **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Update `docs/source/index.rst` to add a new "Guias" toctree section between the "Geral" and "Referencia" sections, linking to the architecture (`arquitetura.rst`), performance (`desempenho.rst`), and FAQ (`faq.rst`) pages created in tickets 008-010. This makes the new guide pages discoverable in the documentation sidebar. +Tickets 008, 009, and 010 create three new guide pages (`arquitetura.rst`, `faq.rst`, `desempenho.rst`) in `docs/source/guias/`. These pages are not yet linked from the main `index.rst` toctree, so they are undiscoverable in the documentation sidebar. This ticket adds a new "Guias" toctree section to `index.rst` to make them visible and navigable. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `docs/source/index.rst` (add new toctree section) - - `docs/source/guias/` directory may need to be created if not done by earlier tickets -- **Key decisions needed**: - - The exact placement of the "Guias" section in index.rst (between Geral and Referencia) - - The order of pages within the Guias section (architecture first, then performance, then FAQ) - - Whether to set a different maxdepth for the Guias section -- **Open questions**: - - Should the "Geral" section's contribuicao.rst link be moved to the Guias section, or kept in Geral? - - Should the Guias section caption be "Guias" or "Guias e Tutoriais"? +This is the final ticket in Epic 3. It depends on tickets 008, 009, and 010 being complete (the referenced `.rst` files must exist for the toctree to resolve without warnings). + +### Current State + +The file `docs/source/index.rst` has 3 toctree sections in this order: + +1. **Apresentacao** (maxdepth 3): `apresentacao/apresentacao.rst` +2. **Geral** (maxdepth 3): `geral/instalacao`, `geral/tutorial`, `examples/index.rst`, `geral/contribuicao` +3. **Referencia** (maxdepth 2): `referencia/decomp/index.rst`, `referencia/libs/index.rst` + +After the toctrees, there is a `:ref:\`genindex\`` link. + +## Specification + +### Requirements + +1. Add a new `.. toctree::` section to `docs/source/index.rst` between the "Geral" and "Referencia" sections. +2. The new section must use: + - `:caption: Guias` + - `:maxdepth: 2` +3. The toctree entries must be, in this order: + - `guias/arquitetura` + - `guias/desempenho` + - `guias/faq` +4. Do NOT modify the existing "Apresentacao", "Geral", or "Referencia" toctree sections. +5. Do NOT move `geral/contribuicao` to the Guias section -- it stays in "Geral". + +### Inputs/Props + +- No runtime inputs. Modification to an existing RST file. + +### Outputs/Behavior + +- The Furo sidebar renders a "Guias" section between "Geral" and "Referencia", containing three linked pages: Arquitetura, Desempenho, FAQ. + +### Error Handling + +- Not applicable (static documentation content). + +## Acceptance Criteria + +- [ ] Given the file `docs/source/index.rst` is opened, when inspecting toctree sections, then there are exactly 4 toctree blocks with captions in this order: "Apresentacao", "Geral", "Guias", "Referencia". +- [ ] Given the "Guias" toctree is present, when inspecting its entries, then it contains exactly 3 entries: `guias/arquitetura`, `guias/desempenho`, `guias/faq`. +- [ ] Given all guide pages exist (from tickets 008-010), when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0 with no warnings about missing toctree references. +- [ ] Given the build succeeds, when opening `docs/build/html/index.html`, then the sidebar shows a "Guias" section with 3 page links. + +## Implementation Guide + +### Suggested Approach + +1. Open `docs/source/index.rst`. +2. After the "Geral" toctree block (which ends with `geral/contribuicao`) and before the "Referencia" toctree block, insert: + + ```rst + .. toctree:: + :caption: Guias + :maxdepth: 2 + + guias/arquitetura + guias/desempenho + guias/faq + ``` + +3. Ensure there is a blank line before and after the new toctree block. +4. Verify locally: `uv run sphinx-build -M html docs/source docs/build`. + +### Key Files to Modify + +- `docs/source/index.rst` (add ~7 lines for the new toctree section) + +### Patterns to Follow + +- Match the existing toctree format: `:caption:` on its own line, `:maxdepth:` on the next, blank line, then entries with 3-space indent. +- Use relative paths without `.rst` extension, matching the convention used in the "Geral" section (e.g., `geral/instalacao` not `geral/instalacao.rst`). + +### Pitfalls to Avoid + +- Do NOT use `.rst` extension in toctree entries for the guias pages -- the existing "Geral" entries omit extensions (`geral/instalacao`, `geral/tutorial`, `geral/contribuicao`), so follow that convention. Note that `apresentacao/apresentacao.rst` and `referencia/*/index.rst` do include extensions -- both styles work, but within the same file, consistency with "Geral" is preferred for new entries. +- Do NOT modify any existing toctree sections. +- Do NOT reorder the 3 existing toctree sections. + +## Testing Requirements + +### Unit Tests + +- Not applicable (documentation content). + +### Integration Tests + +- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. +- Verify the built `docs/build/html/index.html` contains the text "Guias" as a sidebar caption. + +### E2E Tests + +- Not applicable. ## Dependencies @@ -28,4 +112,4 @@ Update `docs/source/index.rst` to add a new "Guias" toctree section between the ## Effort Estimate **Points**: 1 -**Confidence**: Low (will be re-estimated during refinement) +**Confidence**: High diff --git a/plans/infra-docs-overhaul/learnings/epic-03-summary.md b/plans/infra-docs-overhaul/learnings/epic-03-summary.md new file mode 100644 index 0000000..f98d2ef --- /dev/null +++ b/plans/infra-docs-overhaul/learnings/epic-03-summary.md @@ -0,0 +1,89 @@ +# Accumulated Learnings: Epics 01-03 + +**Plan**: infra-docs-overhaul +**Last updated**: 2026-03-09 +**Covers**: epic-01-packaging-ci-modernization, epic-02-sphinx-modernization, epic-03-documentation-content + +--- + +## Dependency & Packaging Conventions + +- `pyproject.toml` uses 4 extras: `test`, `lint`, `docs`, `dev`; `docs` includes `furo` (not `sphinx-rtd-theme`). File: `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. +- `cfinterface` pin `>=1.8,<=1.8.3` is immutable — do not widen or remove. File: `/home/rogerio/git/idecomp/pyproject.toml` line 9. +- CI installs only the minimal required extra per job via `uv sync --extra `. Never use `--all-extras` in CI. +- All tool invocations in CI use `uv run ` — never bare `ruff`, `pytest`, or `sphinx-build`. + +## CI Workflow Patterns + +- `main.yml` runs 4 independent parallel jobs: `lint`, `typecheck`, `test`, `docs`. No `needs:` dependencies. File: `/home/rogerio/git/idecomp/.github/workflows/main.yml`. +- `docs.yml` uses the official 2-job Pages pattern: `build` (sphinx-build + `upload-pages-artifact@v3`) then `deploy` (`deploy-pages@v4`). Concurrency group `"pages"` with `cancel-in-progress: false` is mandatory. File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. +- `release.yml` triggers on `push: tags: ["v*"]`. Version extracted via `grep -oP` from `idecomp/__init__.py` — never via Python import or `exec()`. File: `/home/rogerio/git/idecomp/.github/workflows/release.yml`. +- `main.yml` workflow `name:` is `tests` — badge URLs reference this filename. Do not rename. + +## Pre-commit Hook Conventions + +- mypy hook uses `stages: [manual]` to prevent cfinterface import errors from blocking commits. Run with `pre-commit run --hook-stage manual --all-files`. File: `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. +- ruff hook uses `args: [--fix]` for auto-correction. Without this flag it only reports and requires manual re-run. + +## mypy Configuration + +- `strict = true` replaces all explicit strict flags — do not repeat `disallow_untyped_defs` etc. alongside it. +- `warn_return_any = false` must appear in `[tool.mypy]` to override what `strict = true` enables. File: `/home/rogerio/git/idecomp/pyproject.toml`. + +## Sphinx / Furo Theme Conventions + +- `html_theme = "furo"` is the only required theme setting; Furo must NOT appear in `extensions`. File: `/home/rogerio/git/idecomp/docs/source/conf.py` line 43. +- Dual pygments settings required: `pygments_style = "friendly"` (light) and `pygments_dark_style = "monokai"` (dark). File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 39-40. +- Brand colors: `#2962ff` (light) / `#5c8aff` (dark), matching inewave sibling project verbatim. +- `"sidebar_hide_name": True` prevents duplicate project name display when `html_logo` is set. +- `sphinx.ext.githubpages` must remain in `extensions` regardless of theme — creates `.nojekyll` for GitHub Pages. +- `pio.renderers.default = "sphinx_gallery"` at the top of `conf.py` is required for plotly charts — do not remove. +- Local build: `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build`. + +## Sphinx Gallery Structure + +- 4 example scripts in `/home/rogerio/git/idecomp/examples/`: `plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`. +- Mock data at `/home/rogerio/git/idecomp/examples/decomp/`. New examples must add mock data here. +- `sphinx_gallery_conf` paths must not change. File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 72-76. +- All examples use class-based API (`ClassName.read()`). No deprecated `le_arquivo`/`escreve_arquivo` patterns exist. +- Gallery files use `:orphan:` directive — `examples/index.rst` and `sg_execution_times.rst` are sphinx-gallery-generated and must not be added to a toctree manually. + +## Documentation Content Conventions + +- Use explicit `.. code-block:: python` in all RST pages — Furo's monokai dark style only applies to syntax-highlighted blocks, not anonymous `::` blocks. Files: `/home/rogerio/git/idecomp/docs/source/guias/`. +- RST heading hierarchy: `=` underline for page title, `-` underline for sections, `^` underline for subsections (questions in FAQ). Reference file: `/home/rogerio/git/idecomp/docs/source/geral/tutorial.rst`. +- All prose is in Brazilian Portuguese (pt_BR). All new guide pages follow this convention. +- Guide pages live in `docs/source/guias/` — three files created: `arquitetura.rst`, `faq.rst`, `desempenho.rst`. Each is 178-374 lines; the ticket size estimates (80-200 lines) were conservative. +- Do NOT add guide pages to `index.rst` in the same ticket that creates them. The toctree update is always a separate, dependent ticket (ticket-012 pattern). +- `docs/source/index.rst` toctree order: Apresentacao → Geral → Guias → Referencia. The "Guias" section uses `:maxdepth: 2` and no `.rst` extension in entries (matching "Geral" convention, not "Referencia" convention). + +## Autosummary Conventions + +- `autosummary_generate = True` is already set in `conf.py` line 30. Do not change. +- Use `.. autosummary:: :nosignatures:` (no `:toctree:` option) — individual class pages already exist via the toctree; `:toctree:` would generate duplicate stub pages. +- Autosummary entries must use fully qualified module paths (e.g., `idecomp.decomp.dadger.Dadger`), not re-exported paths from `__init__.py`. +- The autosummary block goes before the existing `.. toctree::` block in each index file. +- The default autosummary templates are sufficient — do NOT create custom templates in `docs/source/_templates/autosummary/`. Files: `/home/rogerio/git/idecomp/docs/source/referencia/decomp/index.rst`, `/home/rogerio/git/idecomp/docs/source/referencia/libs/index.rst`. + +## Documentation Structure: :orphan: Directive Pattern + +- Pages not linked from any toctree produce Sphinx warnings about unreferenced documents. The `:orphan:` directive suppresses this warning. +- sphinx-gallery auto-generates `examples/index.rst` and `sg_execution_times.rst` with `:orphan:` already set; these files must NOT be manually managed or added to toctrees. +- New guide pages (in `guias/`) must NOT use `:orphan:` — they are properly linked via the "Guias" toctree in `index.rst`. Using `:orphan:` on linked pages is an error. +- If a page needs to exist without being in a toctree (e.g., a draft or a utility page), place `:orphan:` on line 1, before the title underline. + +## API & Codebase Reference Facts + +- `idecomp` exports 41 DECOMP file classes via `idecomp.decomp` (eager import of all 41 in `__init__.py`). +- `idecomp.libs` exports 2 auxiliary classes: `Restricoes` and `UsinasHidreletricas`. +- Three base class types: `RegisterFile` (line-oriented, e.g., Dadger), `BlockFile` (section-oriented, e.g., Relato), `ArquivoCSV` (CSV outputs, e.g., DecOperSist). +- Output files (Relato, DecOperSist, etc.) have no `write` method — they are read-only. +- Properties returning tabular data return `pd.DataFrame`; scalar properties return typed values; missing data returns `None`. +- intersphinx for cfinterface is configured at `conf.py` line 69: `"cfinterface": ("https://rjmalves.github.io/cfinterface/", None)`. + +## Release & Version Conventions (Epic 4) + +- Pushing a `v*` tag triggers `release.yml` and attempts PyPI publication. Coordinate version bumps and tag pushes explicitly. +- CONTRIBUTING.md must document: `pip install pre-commit && pre-commit install` for hooks, and `pre-commit run --hook-stage manual --all-files` for mypy. Explain the `stages: [manual]` design decision. +- Badge URL: `[![Tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](...)` — references `main.yml` by filename, not display name. +- `publish.yml` was renamed to `release.yml`. Any README or CONTRIBUTING docs referencing `publish.yml` must be updated. From a40fe22b7cfa2fe315236d9f0957356ea9436e42 Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 18:45:08 -0300 Subject: [PATCH 4/9] feat: complete epic-04 repository polish - Expand README with badges, code example, and project sections - Create CONTRIBUTING.md with environment setup and workflow guide - Update contribuicao.rst with modern tool references (ruff, uv run) - Reformat CHANGELOG to Keep a Changelog standard with compare links - Update installation documentation with Python 3.10+ and uv alternative Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 118 ++++++++++--- CONTRIBUTING.md | 108 ++++++++++++ README.md | 48 ++++-- docs/source/geral/contribuicao.rst | 118 ++++--------- docs/source/geral/instalacao.rst | 63 ++++--- .../.implementation-state.json | 102 ++++++++++-- plans/infra-docs-overhaul/README.md | 14 +- .../epic-04-repository-polish/learnings.md | 74 +++++++++ .../ticket-013-expand-readme.md | 124 ++++++++++++-- .../ticket-014-create-contributing.md | 133 ++++++++++++--- .../ticket-015-reformat-changelog.md | 156 ++++++++++++++++-- .../ticket-016-update-installation-docs.md | 141 ++++++++++++++-- 12 files changed, 974 insertions(+), 225 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a911fd..088aad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,88 +1,164 @@ -# v1.8.2 +# Changelog + +Todas as mudancas relevantes deste projeto serao documentadas neste arquivo. + +O formato segue o [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/). + +## [Nao Publicado] + +## [1.8.2] - 2026-02-04 + +### Modificado - Dependência cfinterface fixada em `>=1.8,<=1.8.3` para compatibilidade com a versão atual -# v1.8.1 +## [1.8.1] - 2026-02-04 + +### Modificado - Aumento nas capacidade máximas de estágios (25) e subsistemas (15) para processamento -# v1.8.0 +## [1.8.0] - 2026-02-02 + +### Modificado - Atualiza processamento do `postos.dat` para suporte a python `>=3.13` e pandas `>=3.0.0` -# v1.7.3 +## [1.7.3] - 2025-12-29 + +### Modificado - Atualiza registro AC COTARE do `dadger.rvX` com maior precisão de dígitos significativos -# v1.7.2 +## [1.7.2] - 2025-12-29 + +### Modificado - Atualiza registro AC COTARE do `dadger.rvX` para utilizar notação científica com formatador 'D' -# v1.7.1 +## [1.7.1] - 2025-01-22 + +### Modificado - Atualiza requisitos mínimos de dependências: `numpy` reduzida para `>=2.0` -# v1.7.0 +## [1.7.0] - 2025-01-10 + +### Adicionado + +- Suporte à leitura do arquivo de saída `dec_eco_evap.csv`, `dec_eco_qlat.csv`, `dec_estatevap.csv`, `dec_estatfpha.rvX`, `dec_oper_evap.csv`, `dec_oper_rhesoft.csv`, `eco_fpha_.rvX`. + +### Corrigido -- Gestão do projeto através de arquivo `pyproject.toml` em substituição ao par `setup.py` + `requirements.txt`. - Correção no processamento do registro de alteração cadastral de potência efetiva (AC POTEFE) do arquivo `dadger.rvX`. - Correção na expansão para DataFrame dos registros de duração dos patamares (DP) do arquivo `dadger.rvX`. - Correção na leitura da versão do DECOMP nos arquivos de saída. - Correção na leitura das probabilidades em casos deterministicos. + +### Modificado + +- Gestão do projeto através de arquivo `pyproject.toml` em substituição ao par `setup.py` + `requirements.txt`. - Atualizado o suporte ao registro de intercâmbio (IA) do arquivo `dadger.rvX`. -- Suporte à leitura do arquivo de saída `dec_eco_evap.csv`, `dec_eco_qlat.csv`, `dec_estatevap.csv`, `dec_estatfpha.rvX`, `dec_oper_evap.csv`, `dec_oper_rhesoft.csv`, `eco_fpha_.rvX`. - Dependência da cfinterface atualizada para v1.8.0. - Descontinuado o uso do `pylama` como linter para garantir padrões PEP de código devido à falta de suporte em Python >= 3.12. Adoção do [ruff](https://github.com/astral-sh/ruff) em substituição. -# v1.6.0 +## [1.6.0] - 2024-10-04 + +### Adicionado - Suporte à leitura do arquivo de saída `avl_turb_max.csv`. - Suporte à leitura dos arquivos de saída `oper_disp_usih.csv`, `oper_disp_usih_ree.csv` e`oper_disp_subm.csv`. -# v1.5.0 +## [1.5.0] - 2024-08-16 + +### Adicionado -- Dependência da cfinterface atualizada para v1.7.1. - Suporte à leitura do arquivo de saída `mapcut.rvX` [#35](https://github.com/rjmalves/idecomp/issues/35). - Suporte à leitura e escrita do arquivo de saída `cortdeco.rvX`[#35](https://github.com/rjmalves/idecomp/issues/35). + +### Modificado + +- Dependência da cfinterface atualizada para v1.7.1. - Simplifica a estrutura de armazenamento dos dados do arquivo `vazoes.rvX` permitindo alterações no número de cenários [#33](https://github.com/rjmalves/idecomp/issues/33). -# v1.4.0 +## [1.4.0] - 2024-04-22 + +### Adicionado -- Dependência da cfinterface atualizada para v1.7.0. -- Uso de slots nas definições de componentes..= - Novos dados em formatos das LIBS: restrições elétricas especiais. - Adicionado suporte ao registro DA (desvios de água) do arquivo dadger. -# v1.3.0 +### Modificado + +- Dependência da cfinterface atualizada para v1.7.0. +- Uso de slots nas definições de componentes. + +## [1.3.0] - 2024-02-23 + +### Adicionado - Suporte à leitura do arquivo de saída `avl_cortesfpha_dec`. + +### Modificado + - Padronização da nomenclatura da unidade de decisão do modelo como "estágio". - Atualizado o número máximo de cenários lidos no arquivo relato para o máximo suportado pelo modelo. -# v1.2.0 +## [1.2.0] - 2024-02-05 + +### Adicionado - Suporte à leitura do arquivo de saída `dec_fcf_cortes_00N.rvX`. - Suporte à leitura do arquivo de saída `oper_desvio_fpha.csv`. -# v1.1.1 +## [1.1.1] - 2024-01-04 + +### Corrigido - Fix na leitura do arquivo `vazoes.rvX` quando gerado por versões antigas do GEVAZP, onde o número de postos não fazia parte dos dados do arquivo. -# v1.1.0 +## [1.1.0] - 2023-12-29 + +### Modificado - Atualizada a modelagem dos registros do dadger sensíveis ao número de estágios para o máximo suportado pelo modelo (TI, VE). - Atualizada a modelagem com propriedades para registros do dadger de manutenção e disponibilidade (MP, MT, FD). - Atualizada na classe `Dadger` as propriedades para acesso aos registros atualizados (MP, MT, FD). -# v1.0.1 +## [1.0.1] - 2023-12-21 + +### Corrigido - Fix nas colunas o DataFrame de Volume Útil de Reservatórios do arquivo Relato -# v1.0.0 +## [1.0.0] - 2023-12-21 + +### Adicionado - Primeira major release - Suporte à leitura e escrita todos os arquivos de entrada utilizados oficialmente no modelo DECOMP - Suporte aos registros utilizados pelo polinômio de jusante em LIBS - Adicionados registros VL, VA e VU ao dadger - Registros do dadger com número de campos variáveis por patamar agora suportam o máximo do modelo DECOMP (5) + +### Depreciado + - Métodos le_arquivo e escreve_arquivo deprecados + +[Nao Publicado]: https://github.com/rjmalves/idecomp/compare/v1.8.2...HEAD +[1.8.2]: https://github.com/rjmalves/idecomp/compare/v1.8.1...v1.8.2 +[1.8.1]: https://github.com/rjmalves/idecomp/compare/v1.8.0...v1.8.1 +[1.8.0]: https://github.com/rjmalves/idecomp/compare/v1.7.3...v1.8.0 +[1.7.3]: https://github.com/rjmalves/idecomp/compare/v1.7.2...v1.7.3 +[1.7.2]: https://github.com/rjmalves/idecomp/compare/v1.7.1...v1.7.2 +[1.7.1]: https://github.com/rjmalves/idecomp/compare/v1.7.0...v1.7.1 +[1.7.0]: https://github.com/rjmalves/idecomp/compare/v1.6.0...v1.7.0 +[1.6.0]: https://github.com/rjmalves/idecomp/compare/v1.5.0...v1.6.0 +[1.5.0]: https://github.com/rjmalves/idecomp/compare/v1.4.0...v1.5.0 +[1.4.0]: https://github.com/rjmalves/idecomp/compare/v1.3.0...v1.4.0 +[1.3.0]: https://github.com/rjmalves/idecomp/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/rjmalves/idecomp/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/rjmalves/idecomp/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/rjmalves/idecomp/compare/v1.0.1...v1.1.0 +[1.0.1]: https://github.com/rjmalves/idecomp/compare/v1.0.0...v1.0.1 +[1.0.0]: https://github.com/rjmalves/idecomp/releases/tag/v1.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3abddc9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,108 @@ +# Como Contribuir + +Obrigado pelo interesse em contribuir com o _idecomp_! Este guia descreve o fluxo de trabalho recomendado para configurar o ambiente, manter a qualidade do código e enviar contribuições. + +## Configuracao do Ambiente + +Clone o repositório e instale as dependências de desenvolvimento com `uv`: + +```bash +git clone https://github.com/rjmalves/idecomp.git +cd idecomp +uv sync --extra dev +``` + +Verifique se a instalação está correta executando a suíte de testes: + +```bash +uv run pytest ./tests +``` + +## Hooks de Pre-commit + +O projeto utiliza [pre-commit](https://pre-commit.com/) para garantir a qualidade do código antes de cada commit. Instale o `pre-commit` e registre os hooks no repositório: + +```bash +pip install pre-commit +pre-commit install +``` + +Os hooks de `ruff` (lint) e `ruff-format` (formatação) são executados automaticamente em todo commit. + +O hook do `mypy` está configurado com `stages: [manual]` porque os stubs de tipo do `cfinterface` estão incompletos e bloqueariam todos os commits se executados automaticamente. Para executar a verificação completa de tipos manualmente: + +```bash +pre-commit run --hook-stage manual --all-files +``` + +Alternativamente, o `mypy` pode ser executado diretamente: + +```bash +uv run mypy ./idecomp +``` + +## Ferramentas de Qualidade + +| Ferramenta | Propósito | Comando | +| ------------- | ------------------------------- | ------------------------------ | +| `ruff check` | Linting (PEP8 e outras regras) | `uv run ruff check ./idecomp` | +| `ruff format` | Formatação automática do código | `uv run ruff format ./idecomp` | +| `mypy` | Verificação de tipagem estática | `uv run mypy ./idecomp` | +| `pytest` | Execução da suíte de testes | `uv run pytest ./tests` | + +Para verificar a formatação sem aplicar mudanças: + +```bash +uv run ruff format --check ./idecomp +``` + +## Convencoes de Codigo + +- **PEP8**: O estilo do código é verificado pelo `ruff`, seguindo as diretrizes do [PEP8](https://peps.python.org/pep-0008/). +- **Tipagem estatica obrigatoria**: Todas as variáveis e funções devem ter tipos declarados ou inferíveis. Evite tipos ambíguos ou que variam durante a execução. +- **Dados tabulares como DataFrame**: Propriedades das classes que retornam dados tabulares devem retornar `pd.DataFrame`, seguindo as formas normais de dados tabulares sempre que possível. +- **Nomenclatura em snake_case**: Propriedades das classes e colunas dos `pd.DataFrame` devem ser nomeadas em `snake_case`, evitando ambiguidades. + +## Executando Testes + +Para executar todos os testes: + +```bash +uv run pytest ./tests +``` + +Para executar com relatório de cobertura: + +```bash +uv run pytest --cov=idecomp ./tests +``` + +## Construindo a Documentacao + +Instale as dependências de documentação e gere o site localmente: + +```bash +uv sync --extra docs +uv run sphinx-build -M html docs/source docs/build +``` + +O site gerado estará disponível em `docs/build/html/index.html`. + +## Fluxo de Pull Requests + +1. Faça um fork do repositório e crie um branch descritivo a partir do `main`: + ```bash + git checkout -b minha-contribuicao + ``` +2. Implemente as mudanças, seguindo as convenções de código descritas acima. +3. Certifique-se de que os testes passam e que não há erros de lint ou tipagem: + ```bash + uv run pytest ./tests + uv run ruff check ./idecomp + uv run mypy ./idecomp + ``` +4. Faça commit das mudanças e envie o branch para o seu fork: + ```bash + git push origin minha-contribuicao + ``` +5. Abra um Pull Request no repositório principal. O CI executará automaticamente lint, verificação de tipos, testes e build da documentação. diff --git a/README.md b/README.md index a211510..1999a3e 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,62 @@ # idecomp -[![tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](https://github.com/rjmalves/idecomp/actions/workflows/main.yml) -[![codecov](https://codecov.io/gh/rjmalves/idecomp/branch/main/graph/badge.svg?token=ZSJBGO81JP)](https://codecov.io/gh/rjmalves/idecomp) +[![tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](https://github.com/rjmalves/idecomp/actions/workflows/main.yml) [![codecov](https://codecov.io/gh/rjmalves/idecomp/branch/main/graph/badge.svg?token=ZSJBGO81JP)](https://codecov.io/gh/rjmalves/idecomp) [![PyPI](https://img.shields.io/pypi/v/idecomp)](https://pypi.org/project/idecomp/) [![Python](https://img.shields.io/pypi/pyversions/idecomp)](https://pypi.org/project/idecomp/) [![Licenca](https://img.shields.io/pypi/l/idecomp)](https://github.com/rjmalves/idecomp/blob/main/LICENSE.md) [![docs](https://img.shields.io/badge/docs-online-blue)](https://rjmalves.github.io/idecomp/) O `idecomp` é um pacote Python para manipulação dos arquivos de entrada e saída do programa [DECOMP](http://www.cepel.br/pt_br/produtos/decomp-modelo-de-planejamento-da-operacao-de-sistemas-hidrotermicos-interligados-de-curto-prazo.htm). O DECOMP é desenvolvido pelo [CEPEL](http://www.cepel.br/) e utilizado para os estudos de planejamento e operação do Sistema Interligado Nacional (SIN). O idecomp oferece: -- Meios para leitura dos arquivos de entrada e saída do DECOMP +- Leitura e escrita de arquivos de entrada e saída do DECOMP +- Dados tabulares com pandas DataFrame para análise e pós-processamento +- Mapeamento classe-por-arquivo, com uma classe dedicada para cada arquivo do DECOMP +- Base sólida no framework [cfinterface](https://github.com/rjmalves/cfinterface) para leitura de formatos de colunas fixas +- Tipagem estática completa, compatível com mypy e verificadores de tipo +- Suporte a Python >= 3.10 com API moderna orientada a objetos -- Facilidades para estudo e análise dos dados utilizando DataFrames do pandas +## Exemplo Rápido -- Dados estruturados em modelos com o uso do paradigma de orientação a objetos (OOP) +```python +from idecomp.decomp import Dadger +# Leitura do arquivo dadger.rv0 +dadger = Dadger.read("dadger.rv0") + +# Acesso aos registros de usinas termelétricas no estágio 1 +termicas = dadger.ct(estagio=1) +print(f"Usinas termelétricas no estágio 1: {len(termicas)}") +``` ## Instalação -O idecomp é compatível com versões de Python >= 3.8. +O idecomp é compatível com versões de Python >= 3.10. -É possível instalar a versão distribuída oficialmente com pip: +Instalação com pip (recomendado): ``` -python -m pip install idecomp +pip install idecomp ``` -É possível realizar a instalação da versão de desenvolvimento fazendo o uso do Git. +Instalação alternativa com uv: ``` -pip install git+https://github.com/rjmalves/idecomp +uv add idecomp ``` ## Documentação -Guias, tutoriais e as referências podem ser encontrados no site oficial do pacote: https://rjmalves.github.io/idecomp +Guias, tutoriais e referências de API estão disponíveis no site oficial do pacote: +https://rjmalves.github.io/idecomp/ + +## Projetos Relacionados + +- [inewave](https://github.com/rjmalves/inewave) — pacote equivalente para manipulação dos arquivos do NEWAVE +- [cfinterface](https://github.com/rjmalves/cfinterface) — framework base para leitura e escrita de formatos de colunas fixas + +## Contribuindo + +Contribuições são bem-vindas! Consulte o [CONTRIBUTING.md](CONTRIBUTING.md) para instruções sobre como configurar o ambiente de desenvolvimento e enviar contribuições. + +## Licenca + +Distribuído sob a licença MIT. Consulte o arquivo [LICENSE.md](LICENSE.md) para mais detalhes. diff --git a/docs/source/geral/contribuicao.rst b/docs/source/geral/contribuicao.rst index 6d822ed..760ebca 100644 --- a/docs/source/geral/contribuicao.rst +++ b/docs/source/geral/contribuicao.rst @@ -1,48 +1,26 @@ Como contribuir? ================= -O framework `cfinterface` e dependências de desenvolvimento ------------------------------------------------------------- +.. note:: -O módulo *idecomp* é desenvolvido considerando o framework proposto no módulo `cfinterface `_. + Para instruções de configuração do ambiente de desenvolvimento, instalação de dependências + e fluxo de Pull Requests, consulte o arquivo `CONTRIBUTING.md `_ + na raiz do repositório. -A abordagem proposta no framework consiste em classificar os arquivos processados com relação à sua construção. São definidas três classes de arquvos: +O framework `cfinterface` +-------------------------- -- :obj:`~cfinterface.files.blockfile.BlockFile` -- :obj:`~cfinterface.files.sectionfile.SectionFile` -- :obj:`~cfinterface.files.registerfile.RegisterFile` +O módulo *idecomp* é desenvolvido com o framework `cfinterface `_, que oferece três modelos de arquivo: -Esta classificação independe do arquivo ser constituído de armazenamento no formato texto ou binário, sendo que -esta informação é fornecida na declaração da classe que modela o arquivo em questão. Também é possível fornecer -informações sobre a codificação padrão utilizada em casos de arquivos gerados com caracteres especiais. +- :obj:`~cfinterface.files.blockfile.BlockFile` — blocos com padrão específico de início/fim +- :obj:`~cfinterface.files.sectionfile.SectionFile` — seções obrigatórias em ordem fixa +- :obj:`~cfinterface.files.registerfile.RegisterFile` — linhas com formato constante -A modelagem de arquivos como :obj:`~cfinterface.files.blockfile.BlockFile` se baseia na definição de blocos do arquivo que -possuem um padrão específico, binário ou textual, que indica o início de um conjunto de informações que seja -autocontido e bem definido. Opcionalmente, também pode haver um padrão de terminação, ou o bloco pode determinar -a sua terminação seguindo outros critérios. Um bloco é definido como um elemento da classe :obj:`~cfinterface.components.block.Block`. -Exemplos de arquivos do módulo *idecomp* que são implementados através deste modelo são o :ref:`relato.rvX ` e :ref:`inviab_unic.rvX `. +Arquivos podem ser texto ou binário. Exemplos no *idecomp*: -O uso dos modelos da :obj:`~cfinterface.files.sectionfile.SectionFile` se baseia na definição de seções do arquivo que -sempre aparecem em uma mesma ordem e são obrigatórias. Da mesma forma, o arquivo pode ser binário ou textual. Diferentemente da modelagem -por :obj:`~cfinterface.files.blockfile.BlockFile`, que permite que um mesmo bloco apareça diversas vezes no arquivo, a abordagem -por seções não permite flexibilidade na definição de quais conteúdos aparecem e na ordem que aparecem. Podem, cada objeto -:obj:`~cfinterface.components.section.Section` pode definir o seu critério de fim, permitindo uma certa flexibilidade dentro de cada -seção. Exemplos de arquivos do módulo *idecomp* que são implementados através deste modelo são o :ref:`caso.dat ` e :ref:`arquivos.rvX `. - -Por fim, a abordagem por :obj:`~cfinterface.files.registerfile.RegisterFile` se baseia na definição de unidades mínimas de conteúdo -que ocupam sempre uma única linha e tem formato constante, que são chamadas de registros. Da mesma forma, o arquivo pode ser binário ou textual. -Registros podem ser vistos como blocos de uma só linha mas, devido à sua simplicidade, são de mais fácil definição, através da classe -:obj:`~cfinterface.components.register.Register`. A implementação de um registro consiste apenas na sua definição, visto que -a leitura e a escrita deste são inteiramente obtidas do formato dos seus campos. No *idecomp*, os arquivos :ref:`dadger.rvX ` e :ref:`dadgnl.rvX ` são modelados -seguindo esta abordagem. - - - -Para instalar as dependências de desenvolvimento, incluindo as necessárias para a geração automática do site:: - - $ git clone https://github.com/rjmalves/idecomp.git - $ cd idecomp - $ pip install -r dev-requirements.txt +- :obj:`~cfinterface.files.blockfile.BlockFile`: :ref:`relato.rvX `, :ref:`inviab_unic.rvX ` +- :obj:`~cfinterface.files.sectionfile.SectionFile`: :ref:`caso.dat `, :ref:`arquivos.rvX ` +- :obj:`~cfinterface.files.registerfile.RegisterFile`: :ref:`dadger.rvX `, :ref:`dadgnl.rvX ` .. warning:: @@ -50,68 +28,42 @@ Para instalar as dependências de desenvolvimento, incluindo as necessárias par automaticamente pelos scripts de CI no caso de qualquer modificação no branch `main`. -Diretrizes de modelagem para o módulo `idecomp` ------------------------------------------------- +Diretrizes de modelagem +------------------------ -A principal diretriz para o desenvolvimento do *idecomp* é a relação entre arquivos do modelo DECOMP e as classes -disponíveis para uso. Cada arquivo de entrada do modelo é mapeado para uma classe do módulo, que segue -o nome geralmente utilizado para o arquivo nos casos de exemplo que são fornecidos junto com o modelo pelo desenvolvedor -ou pelo ONS para publicação dos decks das revisões semanais do PMO. É utilizado sempre `PascalCase` para determinação dos nomes -das classes, sendo que abreviações que possivelmente se encontram nos nomes dos arquivos são ignoradas na mudança de caso. Por exemplo: +Cada arquivo de entrada do DECOMP é mapeado para uma classe com nome `PascalCase` (ignorando abreviações). Exemplos: -- `arquivos.rvX` é modelado na classe :ref:`Arquivos ` -- `dadger.rvX` é modelado na classe :ref:`Dadger ` -- `relato.rvX` é modelado na classe :ref:`Relato ` +- `arquivos.rvX` → classe :ref:`Arquivos ` +- `dadger.rvX` → classe :ref:`Dadger ` +- `relato.rvX` → classe :ref:`Relato ` -É convencionado, sempre que possível, que as propriedades das classes que contém os dados processados dos arquivos -lidem com objetos do tipo :obj:`~pandas.DataFrame` para a representação de dados tabulares. Além disso, se possível, -é recomendado processar a informação contida nos arquivos para que esteja na seguindo as formas normais -para dados tabulares, mesmo quando há divergência na representação textual nos arquivos de entrada do DECOMP. +**Dados tabulares**: Propriedades retornam :obj:`~pandas.DataFrame` quando possível, normalizadas para formas tabulares padrão. -As propriedades das classes e também as colunas dos :obj:`~pandas.DataFrame` que são produzidos são convencionados de -serem nomeados em `snake_case`. Além disso, deve-se evitar ao máximo ambiguidades na escolha dos nomes das propriedades e -das colunas. Alguns pontos recorrentes onde são encontradas ambiguidades e deve-se adotar um termo único são: +**Nomenclatura**: Use `snake_case` para propriedades e colunas, evitando ambiguidades: -- Propriedade ou :obj:`~pandas.DataFrame` que contenha informações de usinas (hidrelétricas, termelétricas, etc.) e venham e conter atributos - como código (`int`) e nome (`str`) convenciona-se chamar de *nome_usina* e *codigo_usina*, para garantir o único sentido possível. -- Propriedade ou :obj:`~pandas.DataFrame` que contenha informações relativas aos submercados de energia, que ora são - mencionados como subsistemas de energia, adota-se o termo único *submercado*. De modo semelhante, locais onde apareçam - informações desta entendidade são denominados *codigo_submercado* e *nome_submercado*. O mesmo raciocínio se aplica - ao se referir a REE. +- Usinas: `codigo_usina`, `nome_usina` +- Submercados/subsistemas: `codigo_submercado`, `nome_submercado` +- REE: similar aos submercados Convenções de código --------------------- -O *idecomp* considera critérios de qualidade de código em seus scripts de Integração Contínua (CI), além de uma bateria de testes unitários. -Desta forma, não é possível realizar uma *release* de uma versão que não passe em todos os testes estabelecidos ou não -atenda aos critérios de qualidade de código impostos. +Todas as releases passam por testes e controle de qualidade. As convenções são: -A primeira convenção é que sejam seguidas as diretrizes de sintaxe `PEP8 `_, provenientes do guia de estilo -do autor da linguagem. Além disso, não é recomendado que existam funções muito complexas, com uma quantidade -excessiva de *branches* e *loops*, o que piora e legibilidade do código. Isto pode ser garantido através de módulos -específicos para análise de qualidade de código, como será mencionado a seguir. A única exceção é a regra `E203 `_. - -Para garantir a formatação é recomendado utilizar o módulo `black `_, que realiza formatação automática e possui -integração nativa com alguns editores de texto no formato de *plugins* ou extensões. - -A segunda convenção é que seja utilizada tipagem estática. Isto é, não deve ser uitilizada uma variável em código a qual possua -tipo de dados que possa mudar durante a execução do mesmo. Além disso, não deve ser declarada uma variável cujo tipo não é possível de -ser inferido em qualquer situação, permanencendo incerto para o leitor o tipo de dados da variável a menos que seja feita uma -execução de teste do programa. +1. **PEP8**: Siga as diretrizes de sintaxe usando `ruff `_ para formatação (``ruff format``) e lint (``ruff check``). +2. **Tipagem estática**: Todas as variáveis devem ter tipos declarados ou inferíveis. Evite tipos que variam durante a execução. Procedimentos de teste ----------------------- -O *idecomp* realiza testes utilizando o pacote de testes de Python `pytest `_ -e controle da qualidade de código com `pylama `_. -A tipagem estática é garantida através do uso de `mypy `_ -, que é sempre executado nos scripts de Integração Contínua (CI). +Execute antes de cada ``git push``: + +.. code-block:: bash -Antes de realizar um ``git push`` é recomendado que se realize estes três procedimentos -descritos, que serão novamente executados pelo ambiente de CI:: + $ uv run pytest ./tests + $ uv run mypy ./idecomp + $ uv run ruff check ./idecomp - $ pytest ./tests - $ mypy ./idecomp - $ pylama ./idecomp --ignore E203 +Usa-se `pytest `_ para testes, `mypy `_ para tipagem, e `ruff `_ para lint. diff --git a/docs/source/geral/instalacao.rst b/docs/source/geral/instalacao.rst index 26ac018..adc042f 100644 --- a/docs/source/geral/instalacao.rst +++ b/docs/source/geral/instalacao.rst @@ -1,43 +1,64 @@ Instalação ============ -O *idecomp* é compatível com versões de Python >= 3.8. +O *idecomp* requer Python >= 3.10. -Em posse de uma instalação local de Python, é recomendado que se use um ambiente virtual para instalação de módulos de terceiros, sendo que o *idecomp* não é uma exceção. -Para mais detalhes sobre o uso de ambientes virtuais, recomenda-se a leitura do recurso oficial de Python para ambientes virtuais: `venv `_. - -Antes de prosseguir, é necessário verificar se está instalada a última versão do ``pip``, o gerenciador de pacotes de Python. Isso pode ser feito com, por exemplo:: - - $ python -m pip install ---upgrade pip +Use um ambiente virtual para instalar dependências. Veja a documentação oficial de `venv `_ para detalhes. Instalando a versão distribuída oficialmente --------------------------------------------- -É possível instalar a versão distribuída oficialmente com ``pip``:: +É possível instalar a versão distribuída oficialmente com ``pip``: + +.. code-block:: bash + + pip install idecomp + +Para atualizar para uma versão mais recente, basta adicionar a flag ``--upgrade``: + +.. code-block:: bash + + pip install --upgrade idecomp + +Para instalar uma versão específica: - $ pip install idecomp +.. code-block:: bash -Para atualizar para uma versão mais recente, basta adicionar a flag ``--upgrade``:: + pip install idecomp==x.y.z - $ pip install --upgrade idecomp +.. tip:: -Para instalar uma versão específica:: + Se você utiliza o gerenciador de pacotes `uv `_, é possível adicionar o *idecomp* ao seu projeto com: + + .. code-block:: bash + + uv add idecomp - $ pip install --upgrade idecomp==x.y.z Instalando a versão de desenvolvimento ---------------------------------------- +---------------------------------------- + +É possível realizar a instalação da versão de desenvolvimento clonando o repositório e instalando as dependências com `uv `_: + +.. code-block:: bash + + git clone https://github.com/rjmalves/idecomp.git + cd idecomp + uv sync --extra dev + +Também é possível instalar diretamente a partir do repositório remoto com ``pip``, sem necessidade de clonar: + +.. code-block:: bash -É possível realizar a instalação desta versão fazendo o uso do `Git `_. Para instalar a versão de desenvolvimento, é necessário -primeiramente desinstalar a versão instalada (se houver), com:: + pip install git+https://github.com/rjmalves/idecomp - $ pip uninstall idecomp -Em seguida, basta fazer:: +Verificando a instalação +-------------------------- - $ pip install git+https://github.com/rjmalves/idecomp +Após a instalação, verifique se o pacote está disponível: -Também é possível selecionar um branch ou release específicos:: +.. code-block:: bash - $ pip install git+https://github.com/rjmalves/idecomp@v1.0.0 + python -c "import idecomp; print(idecomp.__version__)" diff --git a/plans/infra-docs-overhaul/.implementation-state.json b/plans/infra-docs-overhaul/.implementation-state.json index 17d7275..b99fe83 100644 --- a/plans/infra-docs-overhaul/.implementation-state.json +++ b/plans/infra-docs-overhaul/.implementation-state.json @@ -1,6 +1,6 @@ { "plan": "infra-docs-overhaul", - "last_updated": "2026-03-09T21:12:02Z", + "last_updated": "2026-03-09T21:43:31Z", "progressive": true, "tickets": { "ticket-001-modernize-pyproject-toml.md": { @@ -292,20 +292,100 @@ "agent": "open-source-documentation-writer" }, "ticket-013-expand-readme.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-014-create-contributing.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-015-reformat-changelog.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" }, "ticket-016-update-installation-docs.md": { - "status": "pending", - "detail_level": "outline" + "status": "completed", + "detail_level": "refined", + "scores": { + "quality": 1.0, + "quality_breakdown": { + "conformance": 1.0, + "scope_adherence": 1.0, + "lint_cleanliness": 1.0, + "type_safety": 1.0, + "test_delta": 1.0 + }, + "readiness": 1.0, + "readiness_breakdown": { + "structure": 1.0, + "testability": 1.0, + "boundary": 1.0, + "dependency_clarity": 1.0, + "atomicity": 1.0 + } + }, + "workflow_step": null, + "agent": "open-source-documentation-writer" } }, "epics": { @@ -322,8 +402,8 @@ "learning_extracted": true }, "epic-04-repository-polish": { - "phase": "outline", - "learning_extracted": false + "phase": "completed", + "learning_extracted": true } } } diff --git a/plans/infra-docs-overhaul/README.md b/plans/infra-docs-overhaul/README.md index a5fb46f..e9bec3c 100644 --- a/plans/infra-docs-overhaul/README.md +++ b/plans/infra-docs-overhaul/README.md @@ -17,7 +17,7 @@ Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentat | 1 | Packaging & CI Modernization | 5 | Detailed | completed | | 2 | Sphinx Modernization | 2 | Detailed | completed | | 3 | Documentation Content Expansion | 5 | Refined | completed | -| 4 | Repository Polish | 4 | Outline | outline | +| 4 | Repository Polish | 4 | Refined | completed | ## Progress @@ -35,10 +35,10 @@ Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentat | ticket-010 | Create Performance Guide Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | | ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | | ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-013 | Expand README with Badges and Sections | epic-04 | pending | Outline | -- | -- | -- | -| ticket-014 | Create CONTRIBUTING.md | epic-04 | pending | Outline | -- | -- | -- | -| ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | pending | Outline | -- | -- | -- | -| ticket-016 | Update Installation Documentation | epic-04 | pending | Outline | -- | -- | -- | +| ticket-013 | Expand README with Badges and Sections | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-014 | Create CONTRIBUTING.md | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | +| ticket-016 | Update Installation Documentation | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | ## Dependency Graph @@ -61,7 +61,7 @@ ticket-016 (installation docs) [independent] ## Notes -- This is a **progressive plan**: Epics 1-2 have fully detailed tickets; Epics 3-4 have outline tickets that will be refined with learnings from earlier epics -- Epic 3 tickets have been **refined** with learnings from Epics 1-2 and are ready for implementation +- This is a **progressive plan**: Epics 1-2 have fully detailed tickets; Epics 3-4 have outline tickets that were refined with learnings from earlier epics +- All 4 epics have been **refined** and are ready for implementation - All documentation content must be written in **Brazilian Portuguese** (pt_BR) - Key learnings from the inewave overhaul are embedded in the detailed tickets (regex version extraction, mypy manual stage, cfinterface workarounds) diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md b/plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md new file mode 100644 index 0000000..6243b85 --- /dev/null +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md @@ -0,0 +1,74 @@ +# Epic 04 Learnings: Repository Polish + +**Epic**: epic-04-repository-polish +**Date**: 2026-03-09 +**Tickets**: ticket-013 through ticket-016 +**Files changed**: `README.md`, `CONTRIBUTING.md` (new), `CHANGELOG.md`, `docs/source/geral/contribuicao.rst`, `docs/source/geral/instalacao.rst` + +--- + +## Patterns Established + +- **Dual contributor guidance pattern**: CONTRIBUTING.md at the repo root covers environment setup and workflow (GitHub-standard location for contributor onboarding), while `contribuicao.rst` retains the architectural reference content (cfinterface framework explanation, class naming guidelines, DataFrame conventions). The RST file opens with a `.. note::` admonition linking to CONTRIBUTING.md to direct contributors to the right file. See `/home/rogerio/git/idecomp/CONTRIBUTING.md` and `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst`. + +- **CONTRIBUTING.md tool table pattern**: The "Ferramentas de Qualidade" section uses a markdown table with columns for tool name, purpose, and command. This is more scannable than prose lists and makes the `uv run ` prefix convention explicit for all four quality tools (`ruff check`, `ruff format`, `mypy`, `pytest`). See `/home/rogerio/git/idecomp/CONTRIBUTING.md`. + +- **Keep a Changelog with Portuguese category names**: CHANGELOG.md uses `## [X.Y.Z] - YYYY-MM-DD` version headings with `### Adicionado`, `### Corrigido`, `### Modificado`, `### Depreciado` subheadings. Git compare links appear at the bottom as reference-style markdown links. The `[Nao Publicado]` section sits above the first versioned entry. See `/home/rogerio/git/idecomp/CHANGELOG.md`. + +- **`.. tip::` admonition for secondary tooling alternatives**: When a primary command has a modern alternative (e.g., pip vs uv), the alternative is presented in a `.. tip::` admonition rather than inline in the main prose. This keeps the primary path clear while surfacing the alternative without burying it. See `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` lines 30-36. + +--- + +## Architectural Decisions + +- **README does not use accented Portuguese in installation section headings**: The README uses `## Instalacao`, `## Contribuindo`, `## Licenca` without diacritics (matching the pre-existing `## Instalacao` convention in the original file). The body prose retains full accented Portuguese. **Rejected alternative**: using accented headings throughout (rejected for consistency with the existing file's convention and to avoid diff noise). + +- **`contribuicao.rst` cfinterface section preserved verbatim**: The architectural reference content (BlockFile/SectionFile/RegisterFile explanation with cross-references to cfinterface docs) was kept intact and not moved to CONTRIBUTING.md. This content is appropriate for the documentation site, not a contributor onboarding guide. **Rejected alternative**: merging all content into CONTRIBUTING.md (rejected because RST cross-references and intersphinx links in that section would be lost in markdown). + +- **v1.8.2 CHANGELOG date assigned as 2026-02-04**: Since v1.8.2 had no git tag at the time of the implementation (it was a hotfix on the current branch), the ticket spec prescribed using the v1.8.1 tag date. The implementation followed this exactly. Future releases should add a tag for v1.8.2 before comparing dates. See `/home/rogerio/git/idecomp/CHANGELOG.md` line 9. + +- **README installation section uses fenced code blocks without language specifier**: The `pip install idecomp` and `uv add idecomp` commands appear in ` ``` ` blocks without a language specifier (not ` ```bash `), matching markdown convention for simple single-line shell commands where syntax highlighting adds no value. This differs from the RST convention in `instalacao.rst` which uses `.. code-block:: bash` for all commands. + +--- + +## Files & Structures Created + +- `/home/rogerio/git/idecomp/CONTRIBUTING.md` — New contributor onboarding guide covering environment setup, pre-commit hooks, quality tools table, code conventions, test execution, docs build, and PR workflow. 8 `##` sections, ~100 lines. +- `/home/rogerio/git/idecomp/CHANGELOG.md` — Reformatted from ad-hoc `# vX.Y.Z` format to Keep a Changelog standard. 165 lines covering 16 versions from v1.0.0 to v1.8.2 with Portuguese category subheadings and GitHub compare links. +- `/home/rogerio/git/idecomp/README.md` — Expanded from 37 lines to 63 lines. Added 4 shields.io badges (PyPI version, Python versions, license, docs) alongside existing CI and codecov badges. Added "Exemplo Rapido", "Documentacao", "Projetos Relacionados", "Contribuindo", and "Licenca" sections. +- `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` — Updated from 43 lines to 65 lines. Python version requirement corrected to `>= 3.10`, all code blocks converted to `.. code-block:: bash`, `uv` alternatives added, `pip install --upgrade pip` preamble removed, "Verificando a instalacao" subsection added. +- `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` — Updated to add `.. note::` referencing CONTRIBUTING.md at the top, replace `black`/`pylama` references with `ruff`, and add `uv run` prefix to all tool commands in the "Procedimentos de teste" section. + +--- + +## Conventions Adopted + +- **All tool commands in contributor documentation use `uv run ` prefix**: CONTRIBUTING.md, `contribuicao.rst`, and `instalacao.rst` all show `uv run pytest ./tests`, `uv run mypy ./idecomp`, `uv run ruff check ./idecomp` rather than bare invocations. This is consistent with the CI convention from Epic 1. + +- **The `pre-commit install` step uses bare `pip install pre-commit`, not `uv run pre-commit`**: Pre-commit is a system-level tool that installs git hooks; it should be available globally. The CONTRIBUTING.md correctly uses `pip install pre-commit && pre-commit install` rather than attempting to install it via the project's virtual environment. See `/home/rogerio/git/idecomp/CONTRIBUTING.md` "Hooks de Pre-commit" section. + +- **GitHub compare links in CHANGELOG use unbracketed version numbers in the anchor text**: The reference-style links at the bottom of CHANGELOG.md use `[1.8.2]:` (no `v` prefix in the bracket) while the URL itself uses `v1.8.1...v1.8.2` with the `v` prefix. The version heading uses `## [1.8.2] - YYYY-MM-DD` (no `v` in heading). This is the Keep a Changelog standard. See `/home/rogerio/git/idecomp/CHANGELOG.md` lines 148-164. + +- **`.. tip::` for uv alternatives in RST, not `.. note::`**: Alternative installation methods via `uv` use `.. tip::` admonitions (not `.. note::`) to signal that these are improvements over the primary path rather than cautionary information. This is consistent with Furo's rendering of admonition types. + +--- + +## Surprises & Deviations + +- **README "Exemplo Rapido" imports from `idecomp.decomp` package, not `idecomp.decomp.dadger` module**: The ticket spec showed `from idecomp.decomp.dadger import Dadger`, but the implementation used `from idecomp.decomp import Dadger`. The package-level import is cleaner for a README code example and is valid since `Dadger` is exported from `idecomp.decomp.__init__`. The ticket's "Pitfalls to Avoid" section only prohibited the deprecated `le_arquivo` API, not the import path style. + +- **CONTRIBUTING.md contains 8 `##` sections, not the "at least 6" specified in acceptance criteria C1**: The implementation added a "Fluxo de Pull Requests" section beyond what the minimum acceptance criterion required. All acceptance criteria were met, and the additional section covers the PR workflow requirement from the spec body. + +- **`instalacao.rst` grew to 65 lines, within the specified 55-70 line estimate**: The ticket estimate was accurate. The removal of the `pip install --upgrade pip` block and the addition of the "Verificando a instalacao" subsection nearly cancelled out. + +- **No surprises on CHANGELOG reformatting**: All 16 version entries categorized cleanly using the classification rules in the ticket's implementation guide. No version had ambiguous entries requiring judgment calls beyond the prescribed categories. + +--- + +## Recommendations for Future Epics + +- This is the final epic of the `infra-docs-overhaul` plan. The full modernization is complete. +- When adding a new release, add the version to CHANGELOG.md following the `## [X.Y.Z] - YYYY-MM-DD` pattern under the existing `## [Nao Publicado]` section, then move the entries down and update the compare links at the bottom. File: `/home/rogerio/git/idecomp/CHANGELOG.md`. +- When adding a new contributor (or updating the contributor workflow), update both CONTRIBUTING.md (environment/workflow) and `contribuicao.rst` (architectural guidance) — they have complementary scope and both should stay current. +- The "Exemplo Rapido" in README.md uses `dadger.ct(estagio=1)` — if the `ct` property API changes, this example will need updating. File: `/home/rogerio/git/idecomp/README.md` lines 18-28. +- Any new Python minimum version bump must be updated in at least 4 places: `pyproject.toml` (`requires-python`), `README.md`, `docs/source/geral/instalacao.rst`, and the Python badge URL (shields.io `pyversions` badge is automatic from PyPI, but prose statements must be updated manually). diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md index 381ca81..9670cd0 100644 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md @@ -1,32 +1,120 @@ # ticket-013 Expand README with Badges and Sections -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Expand the minimal README.md (currently 36 lines) with a comprehensive set of badges (CI, codecov, PyPI version, license, docs) and structured sections in Brazilian Portuguese covering features, installation, quick start, documentation links, and contributing. The README serves as the primary landing page for the GitHub repository. +The current `README.md` is a minimal 37-line file with only two badges (CI and codecov), a brief description paragraph, a short installation section referencing the outdated Python >= 3.8 requirement, and a documentation link. As the primary landing page for the GitHub repository, it needs to be expanded with a complete set of badges, structured sections in Brazilian Portuguese, a code example, and links to the newly created documentation site. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `/home/rogerio/git/idecomp/README.md` (rewrite/expand) -- **Key decisions needed**: - - Badge URL format for CI (depends on final workflow name from ticket-002: `actions/workflows/main.yml/badge.svg`) - - Which PyPI badge service to use (shields.io vs. pypi-version badge) - - Whether to include a "Projetos Relacionados" section linking to inewave, cfinterface - - Section ordering: badges, description, features, installation, quick start, docs, contributing, license -- **Open questions**: - - Should the README include a code example snippet (e.g., reading a dadger file)? - - Should it mention uv as an alternative to pip for installation? - - Should the Python version badge say >=3.10 to match the actual requirement (README currently says >=3.8)? +This is the first ticket in Epic 4 (Repository Polish). It modernizes the repository's most visible public-facing file. The badge URLs depend on CI workflow names established in ticket-002 (already completed). The documentation links reference the Sphinx site built and deployed in Epics 2-3. + +### Current State + +The file `/home/rogerio/git/idecomp/README.md` contains: + +- Two badges: `tests` (from `main.yml`) and `codecov` +- A description paragraph in Portuguese +- A 3-item feature bullet list +- An installation section with `Python >= 3.8` (incorrect, should be `>= 3.10`) +- A `pip install` and `pip install git+...` command pair +- A documentation link to `https://rjmalves.github.io/idecomp` + +## Specification + +### Requirements + +1. Add badges in a single row at the top: CI (already present), codecov (already present), PyPI version (shields.io), Python version (shields.io), License (shields.io), Docs (shields.io linking to GitHub Pages) +2. Keep the existing description paragraph; expand the feature bullet list to 5-6 items covering: file read/write, DataFrame output, class-per-file mapping, cfinterface foundation, type safety +3. Add a "Exemplo Rapido" section with a code block showing `Dadger.read()` and accessing a property +4. Modernize the "Instalacao" section: Python >= 3.10, `pip install idecomp` as primary, `uv add idecomp` as secondary alternative +5. Add a "Projetos Relacionados" section linking to `inewave` and `cfinterface` GitHub repos +6. Add a "Contribuindo" section with a one-liner pointing to `CONTRIBUTING.md` +7. Add a "Licenca" section referencing MIT and the `LICENSE.md` file +8. All prose in Brazilian Portuguese (pt_BR) + +### Inputs/Props + +- Badge URLs use `https://img.shields.io/` for PyPI, Python, license, and docs badges +- CI badge URL: `https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg` (already correct in file) +- Codecov badge URL: already correct in file +- PyPI badge: `https://img.shields.io/pypi/v/idecomp` +- Python badge: `https://img.shields.io/pypi/pyversions/idecomp` +- License badge: `https://img.shields.io/pypi/l/idecomp` +- Docs badge: `https://img.shields.io/badge/docs-online-blue` linking to `https://rjmalves.github.io/idecomp/` + +### Outputs/Behavior + +A complete README.md file of approximately 80-120 lines with all sections described above, rendering correctly on GitHub with all badges displayed inline. + +### Error Handling + +Not applicable (static markdown file). + +## Acceptance Criteria + +- [ ] Given the file `/home/rogerio/git/idecomp/README.md`, when inspected, then it contains exactly 6 badge image links in the first content block: tests (main.yml), codecov, PyPI version, Python versions, license, and docs +- [ ] Given the README badge block, when the CI badge URL is checked, then it references `actions/workflows/main.yml/badge.svg` (not `tests.yml` or any other filename) +- [ ] Given the README "Instalacao" section, when the Python version text is checked, then it states `Python >= 3.10` (not `>= 3.8`) +- [ ] Given the README "Instalacao" section, when the installation commands are checked, then it shows `pip install idecomp` as primary and `uv add idecomp` as a secondary alternative +- [ ] Given the README "Exemplo Rapido" section, when inspected, then it contains a fenced Python code block using `Dadger` class with the class-based `read()` API pattern + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/README.md` +2. Replace the badge block at the top with all 6 badges, each as `[![name](image-url)](link-url)` on a single line separated by spaces +3. Keep the existing description paragraph (lines 6-7 of current file) unchanged +4. Expand the feature bullet list (currently 3 items) to include: leitura e escrita de arquivos, dados tabulares com pandas DataFrame, mapeamento classe-por-arquivo, base no framework cfinterface, tipagem estatica +5. Add the "Exemplo Rapido" section with a `python` fenced code block showing: + ```python + from idecomp.decomp.dadger import Dadger + dadger = Dadger.read("dadger.rv0") + ``` +6. Rewrite the "Instalacao" section with the correct Python version and both pip/uv commands +7. Add sections: "Documentacao" (link to site), "Projetos Relacionados" (inewave, cfinterface), "Contribuindo" (link to CONTRIBUTING.md), "Licenca" (MIT) +8. All section headings use `##` markdown format (already the convention in the current file) + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/README.md` (rewrite) + +### Patterns to Follow + +- Badge formatting: `[![alt](img-url)](link-url)` with two trailing spaces for line break between badge line and next content +- Section headings in Portuguese without accents in markdown (matching current file's `## Instalacao` pattern — note: the current README uses `## Instalacao` without cedilla, but proper Portuguese uses accents; follow the existing convention for heading text) +- Code blocks use triple-backtick fences with `python` language specifier (markdown convention, not RST) + +### Pitfalls to Avoid + +- Do NOT use `actions/workflows/tests.yml` for the CI badge — the workflow file is `main.yml` even though its display name is `tests` +- Do NOT reference `publish.yml` anywhere — it was renamed to `release.yml` +- Do NOT state Python >= 3.8 — the actual requirement in `pyproject.toml` is `>= 3.10` +- Do NOT add a `dev-requirements.txt` reference — the project uses `pyproject.toml` extras +- Do NOT use the deprecated `le_arquivo`/`escreve_arquivo` API in the code example — use the class-based `ClassName.read()` pattern + +## Testing Requirements + +### Unit Tests + +Not applicable (static markdown file). + +### Integration Tests + +Not applicable. + +### E2E Tests + +Verify the README renders correctly by visually inspecting `https://github.com/rjmalves/idecomp` after pushing, or by running a local markdown preview tool. ## Dependencies -- **Blocked By**: ticket-002-restructure-ci-workflow.md (needs final workflow name for badge URL) +- **Blocked By**: ticket-002-restructure-ci-workflow.md (needs final workflow name for badge URL; already completed) - **Blocks**: None ## Effort Estimate **Points**: 2 -**Confidence**: Low (will be re-estimated during refinement) +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md index b1dcf81..c1d6425 100644 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md @@ -1,32 +1,127 @@ # ticket-014 Create CONTRIBUTING.md -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Create a standalone CONTRIBUTING.md file at the repository root documenting the full contributor workflow: environment setup with uv, dependency installation by group, pre-commit hook usage, testing with pytest, type checking with mypy, code formatting with ruff, code conventions (PEP8, static typing, DataFrame patterns), and PR workflow. Content extracted and updated from the existing `docs/source/geral/contribuicao.rst` which currently references deprecated tools (pylama, black, pip/requirements.txt). +The repository currently has no `CONTRIBUTING.md` file. Contributor guidance exists only in `docs/source/geral/contribuicao.rst`, which is outdated: it references `pylama` (replaced by `ruff`), `black` (replaced by `ruff format`), `pip install -r dev-requirements.txt` (replaced by `uv sync --extra dev`), and `pip install git+...` for development installation. A modern `CONTRIBUTING.md` at the repository root is the standard location for contributor onboarding on GitHub. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `/home/rogerio/git/idecomp/CONTRIBUTING.md` (new file) - - `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` (update to reference CONTRIBUTING.md or keep both in sync) -- **Key decisions needed**: - - Whether to keep `contribuicao.rst` as a full page or convert it to a redirect/summary pointing to CONTRIBUTING.md - - Which content from `contribuicao.rst` stays in docs vs. moves to CONTRIBUTING.md (cfinterface framework description is docs-appropriate; dev setup is CONTRIBUTING-appropriate) - - Whether to include the cfinterface framework explanation or keep it in the architecture page (ticket-008) -- **Open questions**: - - Should CONTRIBUTING.md reference pre-commit hook stages (normal vs. manual for mypy)? - - Should it include a section on running sphinx-build locally? - - Should the PR workflow section describe branch naming conventions? +This is the second ticket in Epic 4 (Repository Polish). It creates the contributor workflow document that the README (ticket-013) will link to. It depends on the pre-commit configuration (ticket-005) and pyproject.toml extras (ticket-001), both already completed. + +### Current State + +- `/home/rogerio/git/idecomp/CONTRIBUTING.md` does not exist +- `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` (118 lines) contains: + - cfinterface framework explanation (BlockFile, SectionFile, RegisterFile) + - Outdated dev setup: `pip install -r dev-requirements.txt` + - Code conventions: PEP8, static typing, DataFrame patterns + - Testing: references `pylama` and `black` (deprecated tools) +- Pre-commit config at `/home/rogerio/git/idecomp/.pre-commit-config.yaml` uses `ruff` + `ruff-format` + `mypy` (manual stage) +- `pyproject.toml` defines 4 extras: `test`, `lint`, `docs`, `dev` + +## Specification + +### Requirements + +1. Create `/home/rogerio/git/idecomp/CONTRIBUTING.md` with the following sections (all in pt_BR): + - **Configuracao do Ambiente**: Clone, `uv sync --extra dev`, verify with `uv run pytest ./tests` + - **Hooks de Pre-commit**: `pip install pre-commit && pre-commit install`, explain `stages: [manual]` for mypy, document `pre-commit run --hook-stage manual --all-files` for full mypy check + - **Ferramentas de Qualidade**: ruff (linting + formatting), mypy (tipagem estatica), pytest (testes) + - **Convencoes de Codigo**: PEP8 via ruff, tipagem estatica obrigatoria, propriedades retornam `pd.DataFrame` para dados tabulares, nomenclatura em `snake_case` + - **Executando Testes**: `uv run pytest ./tests`, `uv run pytest --cov=idecomp ./tests` for coverage + - **Construindo a Documentacao**: `uv sync --extra docs`, `uv run sphinx-build -M html docs/source docs/build` + - **Fluxo de Pull Requests**: fork, branch, commit, push, open PR; CI runs lint/typecheck/test/docs automatically +2. Update `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` to: + - Keep the cfinterface framework explanation (lines 1-37 of current file) intact — this is docs-appropriate architectural content + - Keep the "Diretrizes de modelagem" section (lines 53-81) intact — class naming and DataFrame conventions are reference material + - Replace the outdated "Convencoes de codigo" section (lines 83-101) with updated tool references: replace `black` with `ruff format`, remove `pylama` reference + - Replace the outdated "Procedimentos de teste" section (lines 104-118) with: `uv run pytest ./tests`, `uv run mypy ./idecomp`, `uv run ruff check ./idecomp` + - Add a note at the top of the file referencing `CONTRIBUTING.md` for environment setup instructions + +### Inputs/Props + +- All commands use `uv run ` prefix (CI convention from learnings) +- Pre-commit hook configuration from `/home/rogerio/git/idecomp/.pre-commit-config.yaml` +- Dependency extras from `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43 + +### Outputs/Behavior + +- A new `CONTRIBUTING.md` file at the repository root (~80-120 lines) +- An updated `contribuicao.rst` with correct tool references and a pointer to `CONTRIBUTING.md` + +### Error Handling + +Not applicable (static documentation files). + +## Acceptance Criteria + +- [ ] Given the file `/home/rogerio/git/idecomp/CONTRIBUTING.md`, when inspected, then it exists and contains at least 6 markdown `##` section headings +- [ ] Given the CONTRIBUTING.md "Configuracao do Ambiente" section, when the setup commands are checked, then it shows `uv sync --extra dev` (not `pip install -r dev-requirements.txt`) +- [ ] Given the CONTRIBUTING.md "Hooks de Pre-commit" section, when inspected, then it documents `pre-commit run --hook-stage manual --all-files` for mypy and explains the `stages: [manual]` rationale (cfinterface import errors) +- [ ] Given the CONTRIBUTING.md "Ferramentas de Qualidade" section, when tool names are checked, then it references `ruff` for linting and formatting (not `pylama` or `black`) +- [ ] Given the file `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst`, when the "Procedimentos de teste" section is checked, then it shows `uv run pytest`, `uv run mypy`, and `uv run ruff check` commands (not bare `pytest`, `mypy`, or `pylama`) + +## Implementation Guide + +### Suggested Approach + +1. Create `/home/rogerio/git/idecomp/CONTRIBUTING.md` with the section structure from the Requirements +2. For the "Configuracao do Ambiente" section, show the complete flow: `git clone`, `cd idecomp`, `uv sync --extra dev`, `uv run pytest ./tests` +3. For the "Hooks de Pre-commit" section, explain: ruff and ruff-format run automatically on every commit; mypy uses `stages: [manual]` because cfinterface type stubs are incomplete and would block every commit; to run mypy manually use `pre-commit run --hook-stage manual --all-files` or directly `uv run mypy ./idecomp` +4. For "Ferramentas de Qualidade", list each tool with its command: + - `uv run ruff check ./idecomp` (linting) + - `uv run ruff format --check ./idecomp` (format check) + - `uv run mypy ./idecomp` (type check) + - `uv run pytest ./tests` (tests) +5. For "Construindo a Documentacao", show: `uv sync --extra docs` then `uv run sphinx-build -M html docs/source docs/build` +6. Open `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` and: + - Add a `.. note::` admonition after the title directing readers to `CONTRIBUTING.md` for environment setup + - Replace `black` references with `ruff format` + - Replace `pylama ./idecomp --ignore E203` with `uv run ruff check ./idecomp` + - Replace `pytest ./tests` with `uv run pytest ./tests` + - Replace `mypy ./idecomp` with `uv run mypy ./idecomp` + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/CONTRIBUTING.md` (new file) +- `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` (update outdated sections) + +### Patterns to Follow + +- All tool invocations use `uv run ` prefix (established CI convention) +- Section headings in Portuguese without accents in markdown headings +- Use `.. code-block:: bash` in RST files (not anonymous `::` blocks) — consistent with the guide page convention from Epic 3 + +### Pitfalls to Avoid + +- Do NOT reference `dev-requirements.txt` — it no longer exists; use `uv sync --extra dev` +- Do NOT reference `pylama` or `black` — replaced by `ruff` +- Do NOT reference `publish.yml` — renamed to `release.yml` +- Do NOT delete the cfinterface framework explanation from `contribuicao.rst` — it is valuable architectural reference content that belongs in the docs site +- Do NOT use `--all-extras` in any command — CI convention is `--extra ` + +## Testing Requirements + +### Unit Tests + +Not applicable (static documentation files). + +### Integration Tests + +Not applicable. + +### E2E Tests + +Verify `contribuicao.rst` builds without Sphinx warnings by running `uv run sphinx-build -M html docs/source docs/build` and checking for no warnings related to `contribuicao.rst`. ## Dependencies -- **Blocked By**: ticket-001-modernize-pyproject-toml.md, ticket-005-add-pre-commit-hooks.md +- **Blocked By**: ticket-001-modernize-pyproject-toml.md, ticket-005-add-pre-commit-hooks.md (both completed) - **Blocks**: None ## Effort Estimate -**Points**: 3 -**Confidence**: Low (will be re-estimated during refinement) +**Points**: 2 +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md index b0fcdf5..b47eb25 100644 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md @@ -1,25 +1,147 @@ # ticket-015 Reformat CHANGELOG to Keep a Changelog Standard -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Reformat the existing CHANGELOG.md (88 lines, 15 versions from v1.0.0 to v1.8.2) from its current ad-hoc format (heading per version, bullet points) to the Keep a Changelog standard with Portuguese category names (Adicionado, Corrigido, Modificado, Removido). Preserve all existing content; only restructure it into the standard format. +The current `CHANGELOG.md` uses an ad-hoc format: each version is a top-level `# vX.Y.Z` heading followed by an unstructured bullet list. Entries mix different types of changes (new features, bug fixes, dependency updates) without categorization. The file has no dates, no `[Unreleased]` section, and no inter-version comparison links. Reformatting to the Keep a Changelog standard improves readability and enables automated tooling. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `/home/rogerio/git/idecomp/CHANGELOG.md` (reformat in place) -- **Key decisions needed**: - - Portuguese category names: Adicionado, Corrigido, Modificado, Removido, Depreciado, Seguranca - - Whether to add dates to version headers (current format has no dates) - - Whether to add a `[Unreleased]` section at the top - - How to categorize entries that are ambiguous (e.g., "Atualiza processamento" -- is that Modificado or Corrigido?) -- **Open questions**: - - Are release dates available from git tags or GitHub releases? - - Should the header link to a GitHub compare URL between versions? - - Should the "Primeira major release" note for v1.0.0 be kept as-is or expanded? +This is the third ticket in Epic 4 (Repository Polish). It is independent of all other tickets — no blocking dependencies. + +### Current State + +The file `/home/rogerio/git/idecomp/CHANGELOG.md` contains 89 lines covering 15 versions from v1.0.0 to v1.8.2. Each version uses a `# vX.Y.Z` heading with bullet points below. No dates, no categories, no `[Unreleased]` section. + +Git tag dates are available for all versions: + +- v1.8.2: no tag yet (unreleased on current branch) +- v1.8.1: 2026-02-04 +- v1.8.0: 2026-02-02 +- v1.7.3: 2025-12-29 +- v1.7.2: 2025-12-29 +- v1.7.1: 2025-01-22 +- v1.7.0: 2025-01-10 +- v1.6.0: 2024-10-04 +- v1.5.0: 2024-08-16 +- v1.4.0: 2024-04-22 +- v1.3.0: 2024-02-23 +- v1.2.0: 2024-02-05 +- v1.1.1: 2024-01-04 +- v1.1.0: 2023-12-29 +- v1.0.1: 2023-12-21 +- v1.0.0: 2023-12-21 + +## Specification + +### Requirements + +1. Reformat `/home/rogerio/git/idecomp/CHANGELOG.md` to follow the [Keep a Changelog](https://keepachangelog.com/) standard +2. Use Portuguese category names as `###` subheadings under each version: + - **Adicionado** — new features and file support + - **Corrigido** — bug fixes + - **Modificado** — changes to existing functionality, dependency updates, refactors + - **Removido** — removed features or deprecated items + - **Depreciado** — newly deprecated features (if any) +3. Add a `## [Nao Publicado]` section at the top for unreleased changes (currently empty) +4. Version headings format: `## [X.Y.Z] - YYYY-MM-DD` using dates from git tags listed above +5. For v1.8.2, use `2026-02-04` as date (same as v1.8.1 release, since it was a hotfix with no separate tag yet) +6. Add GitHub compare links at the bottom of the file for each version pair +7. Preserve ALL existing content — only restructure, do not delete or rephrase entries +8. Add a file header with the project name and a reference to the Keep a Changelog standard + +### Inputs/Props + +- Current file: `/home/rogerio/git/idecomp/CHANGELOG.md` (89 lines) +- Git tag dates listed in Context section above +- GitHub repo URL: `https://github.com/rjmalves/idecomp` + +### Outputs/Behavior + +A reformatted CHANGELOG.md of approximately 140-180 lines with: + +- Header block explaining the format +- `[Nao Publicado]` section +- Each version with date and categorized entries +- Compare links at the bottom + +### Error Handling + +Not applicable (static markdown file). + +## Acceptance Criteria + +- [ ] Given the file `/home/rogerio/git/idecomp/CHANGELOG.md`, when version headings are checked, then every version from v1.0.0 to v1.8.2 uses the format `## [X.Y.Z] - YYYY-MM-DD` +- [ ] Given the CHANGELOG version v1.7.0, when its entries are checked, then entries are categorized under `### Adicionado`, `### Corrigido`, and `### Modificado` subheadings (v1.7.0 has all three types) +- [ ] Given the CHANGELOG, when the top of the file is checked, then it contains a `## [Nao Publicado]` section before the first versioned section +- [ ] Given the bottom of the CHANGELOG, when compare links are checked, then there is a link `[X.Y.Z]: https://github.com/rjmalves/idecomp/compare/vPREV...vX.Y.Z` for each version pair +- [ ] Given the CHANGELOG entry count, when original entries are compared to reformatted entries, then no content has been deleted — all original bullet points are present (possibly reclassified under category headings) + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/CHANGELOG.md` +2. Add a header: + + ```markdown + # Changelog + + Todas as mudancas relevantes deste projeto serao documentadas neste arquivo. + + O formato segue o [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/). + + ## [Nao Publicado] + ``` + +3. For each existing version entry, classify every bullet point: + - Entries starting with "Suporte a leitura/escrita", "Adicionado", "Novos dados" -> **Adicionado** + - Entries starting with "Fix", "Correcao" -> **Corrigido** + - Entries starting with "Atualiza", "Gestao do projeto", "Dependencia", "Uso de slots", "Padronizacao", "Simplifica", "Descontinuado" -> **Modificado** + - Entries about deprecation ("Metodos le_arquivo e escreve_arquivo deprecados") -> **Depreciado** +4. Rewrite each version heading from `# vX.Y.Z` to `## [X.Y.Z] - YYYY-MM-DD` using the dates from git tags +5. Under each version heading, group entries by category with `###` subheadings +6. At the bottom, add compare links: + ```markdown + [Nao Publicado]: https://github.com/rjmalves/idecomp/compare/v1.8.2...HEAD + [1.8.2]: https://github.com/rjmalves/idecomp/compare/v1.8.1...v1.8.2 + [1.8.1]: https://github.com/rjmalves/idecomp/compare/v1.8.0...v1.8.1 + + ... + [1.0.0]: https://github.com/rjmalves/idecomp/releases/tag/v1.0.0 + ``` + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/CHANGELOG.md` (reformat in place) + +### Patterns to Follow + +- Keep a Changelog standard: `## [version] - date` with `### Category` subheadings +- Portuguese category names: Adicionado, Corrigido, Modificado, Removido, Depreciado +- Compare links use GitHub `compare/vOLD...vNEW` URL format + +### Pitfalls to Avoid + +- Do NOT delete or rephrase any existing entry text — only move entries under category headings +- Do NOT add entries for pre-1.0 versions (they are not in the current CHANGELOG and adding them is out of scope) +- Do NOT use accented characters in section headings (e.g., use "Nao Publicado" not "Nao Publicado" — matching the project's heading convention of omitting accents) +- Do NOT invent dates — use only the git tag dates listed in the Context section + +## Testing Requirements + +### Unit Tests + +Not applicable (static markdown file). + +### Integration Tests + +Not applicable. + +### E2E Tests + +Verify the CHANGELOG renders correctly on GitHub by visual inspection or local markdown preview. ## Dependencies @@ -29,4 +151,4 @@ Reformat the existing CHANGELOG.md (88 lines, 15 versions from v1.0.0 to v1.8.2) ## Effort Estimate **Points**: 2 -**Confidence**: Low (will be re-estimated during refinement) +**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md index 6b590e5..0361cec 100644 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md +++ b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md @@ -1,24 +1,131 @@ # ticket-016 Update Installation Documentation -> **[OUTLINE]** This ticket requires refinement before execution. -> It will be refined with learnings from earlier epics. +## Context -## Objective +### Background -Update the installation documentation page (`docs/source/geral/instalacao.rst`) to reflect the current Python >= 3.10 requirement (currently says >= 3.8), add uv as a secondary installation method alongside pip, and modernize the commands to use current best practices. All content in Brazilian Portuguese. +The Sphinx documentation page `docs/source/geral/instalacao.rst` contains outdated information: it states Python >= 3.8 (actual requirement is >= 3.10), recommends `python -m pip install --upgrade pip` as a preamble step, uses anonymous `::` code blocks instead of `.. code-block:: bash`, and does not mention `uv` as an installation alternative. The page needs to be modernized to match the current project configuration. -## Anticipated Scope +### Relation to Epic -- **Files likely to be modified**: - - `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` (update content) -- **Key decisions needed**: - - Whether to present pip as primary and uv as secondary, or give them equal weight - - Whether to add a section about installing from source with uv (development setup) - - Whether to remove the `pip install --upgrade pip` preamble (modernize to just `pip install idecomp`) -- **Open questions**: - - Should the page mention conda/mamba as installation alternatives? - - Should it include a section on verifying the installation (`python -c "import idecomp; print(idecomp.__version__)"`) - - Should the development installation section reference `uv sync --extra dev` instead of `pip install git+...`? +This is the fourth and final ticket in Epic 4 (Repository Polish). It is independent of all other tickets. It updates the Sphinx documentation installation page to be consistent with the modernized `pyproject.toml` (ticket-001, completed) and the README installation section (ticket-013). + +### Current State + +The file `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` contains 43 lines with: + +- Title "Instalacao" with `=` underline (correct RST heading hierarchy) +- Python >= 3.8 compatibility statement (incorrect) +- A paragraph about virtual environments with a link to the `venv` docs +- A `pip install --upgrade pip` preamble section +- "Instalando a versao distribuida oficialmente" section: `pip install idecomp`, `pip install --upgrade idecomp`, `pip install --upgrade idecomp==x.y.z` +- "Instalando a versao de desenvolvimento" section: `pip uninstall idecomp` then `pip install git+...` +- All code blocks use anonymous `::` syntax (not `.. code-block:: bash`) + +## Specification + +### Requirements + +1. Update Python version requirement from `>= 3.8` to `>= 3.10` +2. Remove the `pip install --upgrade pip` preamble — it is unnecessary for modern Python +3. Keep the virtual environment recommendation paragraph (it is still valid) +4. Modernize "Instalando a versao distribuida oficialmente" section: + - Primary: `pip install idecomp` + - Show upgrade: `pip install --upgrade idecomp` + - Show version pin: `pip install idecomp==x.y.z` + - Secondary alternative: `uv add idecomp` (in a separate subsection or note) +5. Modernize "Instalando a versao de desenvolvimento" section: + - Primary: `git clone` + `cd idecomp` + `uv sync --extra dev` + - Alternative: `pip install git+https://github.com/rjmalves/idecomp` + - Remove the `pip uninstall idecomp` preamble step (unnecessary) +6. Add a "Verificando a instalacao" subsection: `python -c "import idecomp; print(idecomp.__version__)"` +7. Replace all anonymous `::` code blocks with explicit `.. code-block:: bash` directives (Furo theme convention from Epic 3 learnings) +8. All prose in Brazilian Portuguese (pt_BR) + +### Inputs/Props + +- Actual Python requirement: `requires-python = ">= 3.10"` from `/home/rogerio/git/idecomp/pyproject.toml` line 13 +- Development extras: `uv sync --extra dev` installs test + lint + docs (from pyproject.toml lines 32-43) +- RST heading hierarchy: `=` for page title, `-` for sections, `^` for subsections (from Epic 3 conventions) + +### Outputs/Behavior + +An updated `instalacao.rst` of approximately 55-70 lines that renders correctly in the Sphinx/Furo documentation site with syntax-highlighted bash code blocks. + +### Error Handling + +Not applicable (static RST documentation file). + +## Acceptance Criteria + +- [ ] Given the file `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst`, when the Python version text is checked, then it states `Python >= 3.10` (not `>= 3.8`) +- [ ] Given the file, when searched for `pip install --upgrade pip` or `pip install ---upgrade pip`, then no such preamble line exists +- [ ] Given the file, when code blocks are checked, then every code block uses `.. code-block:: bash` directive (no anonymous `::` blocks) +- [ ] Given the file, when the `uv` tool is searched, then at least one section mentions `uv add idecomp` or `uv sync --extra dev` as an alternative installation method +- [ ] Given the file, when a "Verificando a instalacao" subsection is searched, then it contains `python -c "import idecomp; print(idecomp.__version__)"` in a code block + +## Implementation Guide + +### Suggested Approach + +1. Open `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` +2. Update line 4: change `Python >= 3.8` to `Python >= 3.10` +3. Keep the virtual environment paragraph (lines 6-7) but simplify slightly +4. Remove the `pip install --upgrade pip` block entirely (lines 9-11) +5. Rewrite "Instalando a versao distribuida oficialmente" section: + - Use `.. code-block:: bash` for all code blocks + - Show `pip install idecomp`, upgrade, and version pin commands + - Add a `.. tip::` admonition for the `uv` alternative: `uv add idecomp` +6. Rewrite "Instalando a versao de desenvolvimento" section: + - Primary flow: `git clone`, `cd idecomp`, `uv sync --extra dev` + - Alternative: `pip install git+https://github.com/rjmalves/idecomp` + - Remove the `pip uninstall` preamble +7. Add "Verificando a instalacao" subsection with `-` underline: + + ```rst + Verificando a instalacao + ------------------------ + + Apos a instalacao, verifique se o pacote esta disponivel: + + .. code-block:: bash + + python -c "import idecomp; print(idecomp.__version__)" + ``` + +8. Verify RST heading hierarchy: `=` for title, `-` for sections, `^` for subsections (if any) + +### Key Files to Modify + +- `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` (update) + +### Patterns to Follow + +- Use `.. code-block:: bash` for all shell commands (Furo monokai dark style only applies to syntax-highlighted blocks, per Epic 3 learnings) +- RST heading hierarchy: `=` for page title, `-` for sections (matching `/home/rogerio/git/idecomp/docs/source/geral/tutorial.rst` convention) +- Use `.. tip::` admonition for the uv alternative (non-intrusive, visually distinct) + +### Pitfalls to Avoid + +- Do NOT use anonymous `::` code blocks — Furo dark mode does not highlight them properly +- Do NOT remove the virtual environment recommendation paragraph — it is still valid guidance +- Do NOT use `--all-extras` in any uv command — use `--extra dev` specifically +- Do NOT add conda/mamba instructions — out of scope for this ticket +- Do NOT change the filename or its position in the toctree — `geral/instalacao` is already linked from `index.rst` + +## Testing Requirements + +### Unit Tests + +Not applicable (static RST documentation file). + +### Integration Tests + +Not applicable. + +### E2E Tests + +Verify the page builds without Sphinx warnings by running `uv run sphinx-build -M html docs/source docs/build` and checking for no warnings related to `instalacao.rst`. ## Dependencies @@ -27,5 +134,5 @@ Update the installation documentation page (`docs/source/geral/instalacao.rst`) ## Effort Estimate -**Points**: 2 -**Confidence**: Low (will be re-estimated during refinement) +**Points**: 1 +**Confidence**: High From aeb89963b81aafe6e45fe95fd156aaebeb19981b Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 18:51:27 -0300 Subject: [PATCH 5/9] fix: address code review findings from plan completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Python version in FAQ (3.8 → 3.10) - Fix code-block language for shell commands in FAQ (python → bash) - Add branch guard to docs.yml deploy job - Add graceful fallback for pre-existing releases in release.yml - Add -W --keep-going to Sphinx CI build in main.yml Co-Authored-By: Claude Opus 4.6 --- .claude/.plan-executing | 1 - .github/workflows/docs.yml | 1 + .github/workflows/main.yml | 2 +- .github/workflows/release.yml | 2 +- docs/source/guias/faq.rst | 12 +-- .../learnings/epic-04-summary.md | 94 +++++++++++++++++++ 6 files changed, 103 insertions(+), 9 deletions(-) delete mode 100644 .claude/.plan-executing create mode 100644 plans/infra-docs-overhaul/learnings/epic-04-summary.md diff --git a/.claude/.plan-executing b/.claude/.plan-executing deleted file mode 100644 index 584c847..0000000 --- a/.claude/.plan-executing +++ /dev/null @@ -1 +0,0 @@ -active diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ffc5415..6523161 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -35,6 +35,7 @@ jobs: deploy: needs: build + if: github.event_name == 'push' || github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: name: github-pages diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 722e087..982475b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -73,4 +73,4 @@ jobs: - name: Install the project run: uv sync --extra docs - name: Sphinx build - run: uv run sphinx-build -M html docs/source docs/build + run: uv run sphinx-build -M html -W --keep-going docs/source docs/build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 89e1d01..8274781 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,4 +44,4 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release create ${{ github.ref_name }} --generate-notes + gh release create ${{ github.ref_name }} --generate-notes || echo "Release already exists" diff --git a/docs/source/guias/faq.rst b/docs/source/guias/faq.rst index d9ab723..1ea1e81 100644 --- a/docs/source/guias/faq.rst +++ b/docs/source/guias/faq.rst @@ -12,13 +12,13 @@ Como instalo o idecomp? O *idecomp* está disponível no PyPI e pode ser instalado com o ``pip``: -.. code-block:: python +.. code-block:: bash pip install idecomp Para instalar uma versão específica: -.. code-block:: python +.. code-block:: bash pip install idecomp==1.8.2 @@ -28,8 +28,8 @@ de dependências. Qual versão do Python é compatível com o idecomp? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -O *idecomp* requer **Python 3.8 ou superior**. As versões 3.10, 3.11 e 3.12 são as -mais testadas e recomendadas para uso em produção. +O *idecomp* requer **Python 3.10 ou superior**. As versões 3.10, 3.11 e 3.12 são +testadas no CI e recomendadas para uso em produção. Estou com problemas de conflito de versão com o cfinterface. O que fazer? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,14 +39,14 @@ que é responsável pelo mecanismo de parse dos arquivos. A versão compatível ``<=1.8.3``. Caso você tenha uma versão incompatível instalada, force a reinstalação com a restrição correta: -.. code-block:: python +.. code-block:: bash pip install "cfinterface<=1.8.3" --force-reinstall Se o conflito persistir, crie um ambiente virtual limpo e instale o *idecomp* a partir do zero: -.. code-block:: python +.. code-block:: bash python -m venv .venv source .venv/bin/activate # Linux/macOS diff --git a/plans/infra-docs-overhaul/learnings/epic-04-summary.md b/plans/infra-docs-overhaul/learnings/epic-04-summary.md new file mode 100644 index 0000000..1c89b76 --- /dev/null +++ b/plans/infra-docs-overhaul/learnings/epic-04-summary.md @@ -0,0 +1,94 @@ +# Accumulated Learnings: Epics 01-04 (Full Plan) + +**Plan**: infra-docs-overhaul +**Last updated**: 2026-03-09 +**Covers**: epic-01 through epic-04 — complete plan retrospective + +--- + +## Dependency & Packaging Conventions + +- `pyproject.toml` uses 4 extras: `test`, `lint`, `docs`, `dev`; `dev` self-references the other three. File: `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. +- `cfinterface` pin `>=1.8,<=1.8.3` is immutable — do not widen or remove. File: `/home/rogerio/git/idecomp/pyproject.toml` line 9. +- CI installs only the minimal required extra per job via `uv sync --extra `. Never use `--all-extras` in CI. +- All tool invocations in CI and contributor docs use `uv run ` — never bare `ruff`, `pytest`, or `sphinx-build`. +- Python minimum requirement is `>= 3.10` — stated in `pyproject.toml`, `README.md`, and `instalacao.rst`. All three must be kept in sync. + +## CI Workflow Patterns + +- `main.yml` runs 4 independent parallel jobs: `lint`, `typecheck`, `test`, `docs`. No `needs:` dependencies. File: `/home/rogerio/git/idecomp/.github/workflows/main.yml`. +- `docs.yml` uses the official 2-job Pages pattern: `build` then `deploy`. Concurrency group `"pages"` with `cancel-in-progress: false` is mandatory. File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. +- `release.yml` triggers on `push: tags: ["v*"]`. Version extracted via `grep -oP` — never via Python import or `exec()`. File: `/home/rogerio/git/idecomp/.github/workflows/release.yml`. +- `main.yml` workflow `name:` is `tests` — badge URLs reference this filename. Do not rename. +- `publish.yml` was renamed to `release.yml`. Any reference to `publish.yml` must be updated everywhere. + +## Pre-commit Hook Conventions + +- mypy hook uses `stages: [manual]` to prevent cfinterface import errors from blocking commits. Run with `pre-commit run --hook-stage manual --all-files`. File: `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. +- ruff hook uses `args: [--fix]` for auto-correction; without it, ruff only reports and requires manual re-run. +- `pip install pre-commit && pre-commit install` uses bare `pip`, not `uv run pre-commit` — pre-commit is a system-level git hook installer. + +## mypy Configuration + +- `strict = true` replaces all explicit strict flags — do not repeat individual flags alongside it. +- `warn_return_any = false` must appear in `[tool.mypy]` to override what `strict = true` enables. File: `/home/rogerio/git/idecomp/pyproject.toml`. + +## Sphinx / Furo Theme Conventions + +- `html_theme = "furo"` is the only required theme setting; Furo must NOT appear in `extensions`. File: `/home/rogerio/git/idecomp/docs/source/conf.py`. +- Dual pygments settings required: `pygments_style = "friendly"` (light) and `pygments_dark_style = "monokai"` (dark). +- Brand colors: `#2962ff` (light) / `#5c8aff` (dark), matching inewave sibling project. +- `sphinx.ext.githubpages` must remain in `extensions` — creates `.nojekyll` for GitHub Pages. +- `pio.renderers.default = "sphinx_gallery"` at the top of `conf.py` is required for plotly charts. +- Local build: `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build`. + +## RST Documentation Conventions + +- Use explicit `.. code-block:: python` or `.. code-block:: bash` in all RST pages — Furo monokai dark style only applies to syntax-highlighted blocks, not anonymous `::` blocks. +- RST heading hierarchy: `=` underline for page title, `-` for sections, `^` for subsections. Reference: `/home/rogerio/git/idecomp/docs/source/geral/tutorial.rst`. +- All prose is in Brazilian Portuguese (pt_BR). Markdown headings omit diacritics (convention from pre-existing files); RST body prose retains full accented Portuguese. +- Do NOT add new guide pages (`guias/`) to `index.rst` in the same ticket that creates them — toctree updates are always a separate dependent ticket. +- Pages not linked from any toctree should use `:orphan:` on line 1. Pages properly linked via a toctree must NOT have `:orphan:`. + +## Autosummary Conventions + +- Use `.. autosummary:: :nosignatures:` (no `:toctree:` option) — individual class pages already exist; `:toctree:` generates duplicate stubs. +- Autosummary entries use fully qualified module paths (e.g., `idecomp.decomp.dadger.Dadger`), not re-exported paths from `__init__.py`. +- The autosummary block goes before the existing `.. toctree::` block in each reference index file. Files: `/home/rogerio/git/idecomp/docs/source/referencia/decomp/index.rst`, `/home/rogerio/git/idecomp/docs/source/referencia/libs/index.rst`. + +## Sphinx Gallery Structure + +- 4 example scripts in `/home/rogerio/git/idecomp/examples/`: `plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`. +- Mock data at `/home/rogerio/git/idecomp/examples/decomp/`. New examples must add mock data here. +- sphinx-gallery auto-generates `examples/index.rst` and `sg_execution_times.rst` — do NOT add these to a toctree manually. +- All examples use class-based API (`ClassName.read()`). No deprecated `le_arquivo`/`escreve_arquivo` patterns exist. + +## API Reference Facts + +- `idecomp` exports 41 DECOMP file classes via `idecomp.decomp` and 2 auxiliary classes via `idecomp.libs` (`Restricoes`, `UsinasHidreletricas`). +- Three base class types: `RegisterFile` (line-oriented), `BlockFile` (section-oriented), `ArquivoCSV` (CSV outputs, read-only). +- Properties returning tabular data return `pd.DataFrame`; scalar properties return typed values; missing data returns `None`. +- intersphinx for cfinterface: `conf.py` line 69: `"cfinterface": ("https://rjmalves.github.io/cfinterface/", None)`. + +## Contributor Documentation Pattern + +- CONTRIBUTING.md (root, markdown) handles environment setup, pre-commit hooks, quality tools, code conventions, PR workflow. +- `contribuicao.rst` (docs site) handles cfinterface architecture, class naming guidelines, DataFrame conventions. +- `contribuicao.rst` opens with `.. note::` admonition linking to CONTRIBUTING.md for environment setup. +- Files: `/home/rogerio/git/idecomp/CONTRIBUTING.md`, `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst`. + +## CHANGELOG Conventions + +- Keep a Changelog format: `## [X.Y.Z] - YYYY-MM-DD` headings, `### Adicionado/Corrigido/Modificado/Depreciado` subheadings in Portuguese. +- `## [Nao Publicado]` section sits above the first versioned entry. +- GitHub compare links appear as reference-style links at the bottom of the file. +- Version anchor text uses no `v` prefix; URL uses `v` prefix: `[1.8.2]: .../compare/v1.8.1...v1.8.2`. +- File: `/home/rogerio/git/idecomp/CHANGELOG.md`. + +## Plan-wide Quality Observations + +- All 16 tickets scored 1.0 quality except ticket-006 (Furo theme migration, 0.85) where lint/type scoring was neutral due to conf.py-only changes. +- Mean quality score across all 16 tickets: ~0.99. No ticket fell below the 0.75 gate. +- Pure documentation and configuration tickets (markdown, RST, YAML, TOML) consistently score 1.0 because lint, type safety, and test delta dimensions default to 1.0 for non-code files. +- The `open-source-documentation-writer` agent handled all Epic 2-4 tickets; `python-task-automation-developer` handled Epic 1. +- Documentation-only tickets benefit from explicit pitfall lists in ticket specs — every case where an agent avoided a known error (e.g., not using `publish.yml`, not using `--all-extras`, not deleting cfinterface section from RST) traced back to a "Pitfalls to Avoid" entry in the ticket. From 00bc91f867e3318ebecd92045737b51bd6a1c0c0 Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 21:07:52 -0300 Subject: [PATCH 6/9] refactor: upgrade deps, add strict mypy types, drop Python 3.10 - Upgrade cfinterface >= 1.9.0, pandas >= 3.0.0, numpy >= 2.2.1 - Drop Python 3.10, add 3.13 and 3.14 to CI matrix - Add strict mypy type annotations to all 97 source files (0 errors) - Fix ruff lint violations (import sorting, modern type syntax) - Align ruff/mypy config with inewave sibling project - Fix test_neq_restricoes for cfinterface 1.9.x identity-based remove - Update docs/README for Python >= 3.11 and cfinterface >= 1.9.0 - Update pre-commit hooks to ruff v0.15.5, mypy as pre-commit stage Co-Authored-By: Claude Opus 4.6 --- .github/workflows/main.yml | 4 +- .pre-commit-config.yaml | 6 +- README.md | 4 +- docs/source/geral/instalacao.rst | 2 +- docs/source/guias/faq.rst | 8 +- idecomp/decomp/arquivos.py | 39 +- idecomp/decomp/avl_cortesfpha_dec.py | 9 +- idecomp/decomp/avl_turb_max.py | 4 +- idecomp/decomp/caso.py | 11 +- idecomp/decomp/cortdeco.py | 49 +- idecomp/decomp/custos.py | 21 +- idecomp/decomp/dadger.py | 403 ++-- idecomp/decomp/dadgnl.py | 56 +- idecomp/decomp/dec_avl_evap.py | 9 +- idecomp/decomp/dec_cortes_evap.py | 9 +- idecomp/decomp/dec_desvfpha.py | 9 +- idecomp/decomp/dec_eco_cotajus.py | 9 +- idecomp/decomp/dec_eco_discr.py | 9 +- idecomp/decomp/dec_eco_evap.py | 9 +- idecomp/decomp/dec_eco_qlat.py | 9 +- idecomp/decomp/dec_estatevap.py | 9 +- idecomp/decomp/dec_estatfpha.py | 18 +- idecomp/decomp/dec_fcf_cortes.py | 9 +- idecomp/decomp/dec_oper_evap.py | 11 +- idecomp/decomp/dec_oper_gnl.py | 9 +- idecomp/decomp/dec_oper_interc.py | 9 +- idecomp/decomp/dec_oper_ree.py | 9 +- idecomp/decomp/dec_oper_rhesoft.py | 11 +- idecomp/decomp/dec_oper_sist.py | 9 +- idecomp/decomp/dec_oper_usie.py | 9 +- idecomp/decomp/dec_oper_usih.py | 11 +- idecomp/decomp/dec_oper_usit.py | 9 +- idecomp/decomp/decomptim.py | 11 +- idecomp/decomp/eco_fpha.py | 9 +- idecomp/decomp/fcfnw.py | 9 +- idecomp/decomp/hidr.py | 24 +- idecomp/decomp/inviabunic.py | 17 +- idecomp/decomp/mapcut.py | 59 +- idecomp/decomp/modelos/arquivos.py | 19 +- .../decomp/modelos/arquivoscsv/arquivocsv.py | 25 +- idecomp/decomp/modelos/avl_cortesfpha_dec.py | 5 +- idecomp/decomp/modelos/blocos/tabelacsv.py | 26 +- idecomp/decomp/modelos/blocos/versaomodelo.py | 10 +- idecomp/decomp/modelos/caso.py | 13 +- idecomp/decomp/modelos/cortdeco.py | 67 +- idecomp/decomp/modelos/custos.py | 33 +- idecomp/decomp/modelos/dadger.py | 1694 +++++++++-------- idecomp/decomp/modelos/dadgnl.py | 94 +- idecomp/decomp/modelos/dec_avl_evap.py | 5 +- idecomp/decomp/modelos/dec_cortes_evap.py | 5 +- idecomp/decomp/modelos/dec_desvfpha.py | 5 +- idecomp/decomp/modelos/dec_eco_cotajus.py | 5 +- idecomp/decomp/modelos/dec_eco_discr.py | 5 +- idecomp/decomp/modelos/dec_eco_evap.py | 5 +- idecomp/decomp/modelos/dec_eco_qlat.py | 5 +- idecomp/decomp/modelos/dec_estatevap.py | 5 +- idecomp/decomp/modelos/dec_estatfpha.py | 11 +- idecomp/decomp/modelos/dec_fcf_cortes.py | 5 +- idecomp/decomp/modelos/dec_oper_evap.py | 5 +- idecomp/decomp/modelos/dec_oper_gnl.py | 5 +- idecomp/decomp/modelos/dec_oper_interc.py | 5 +- idecomp/decomp/modelos/dec_oper_ree.py | 5 +- idecomp/decomp/modelos/dec_oper_rhesoft.py | 5 +- idecomp/decomp/modelos/dec_oper_sist.py | 5 +- idecomp/decomp/modelos/dec_oper_usie.py | 5 +- idecomp/decomp/modelos/dec_oper_usih.py | 5 +- idecomp/decomp/modelos/dec_oper_usit.py | 5 +- idecomp/decomp/modelos/decomptim.py | 15 +- idecomp/decomp/modelos/eco_fpha.py | 5 +- idecomp/decomp/modelos/fcfnw.py | 15 +- idecomp/decomp/modelos/hidr.py | 103 +- idecomp/decomp/modelos/inviabunic.py | 45 +- idecomp/decomp/modelos/mapcut.py | 60 +- idecomp/decomp/modelos/oper_desvio_fpha.py | 5 +- idecomp/decomp/modelos/postos.py | 11 +- idecomp/decomp/modelos/relato.py | 330 ++-- idecomp/decomp/modelos/relgnl.py | 84 +- idecomp/decomp/modelos/vazoes.py | 111 +- idecomp/decomp/oper_desvio_fpha.py | 9 +- idecomp/decomp/oper_disp_usih.py | 9 +- idecomp/decomp/oper_disp_usih_ree.py | 9 +- idecomp/decomp/oper_disp_usih_subm.py | 9 +- idecomp/decomp/postos.py | 29 +- idecomp/decomp/relato.py | 113 +- idecomp/decomp/relgnl.py | 30 +- idecomp/decomp/vazoes.py | 68 +- idecomp/libs/modelos/restricoes.py | 405 ++-- idecomp/libs/modelos/usinas_hidreletricas.py | 84 +- idecomp/libs/restricoes.py | 482 +++-- idecomp/libs/usinas_hidreletricas.py | 120 +- pyproject.toml | 30 +- tests/libs/test_restricoes.py | 2 +- 92 files changed, 2636 insertions(+), 2565 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 982475b..7fe7c84 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12"] + python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 - name: Install uv @@ -73,4 +73,4 @@ jobs: - name: Install the project run: uv sync --extra docs - name: Sphinx build - run: uv run sphinx-build -M html -W --keep-going docs/source docs/build + run: uv run sphinx-build -b html -W --keep-going docs/source docs/build/html diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d3e8d0..748062d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.15.5 hooks: - id: ruff args: [--fix] @@ -10,8 +10,8 @@ repos: hooks: - id: mypy name: mypy - entry: uv run mypy ./idecomp + entry: uv run --no-sync mypy ./idecomp language: system types: [python] pass_filenames: false - stages: [manual] + stages: [pre-commit] diff --git a/README.md b/README.md index 1999a3e..526883c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ O idecomp oferece: - Mapeamento classe-por-arquivo, com uma classe dedicada para cada arquivo do DECOMP - Base sólida no framework [cfinterface](https://github.com/rjmalves/cfinterface) para leitura de formatos de colunas fixas - Tipagem estática completa, compatível com mypy e verificadores de tipo -- Suporte a Python >= 3.10 com API moderna orientada a objetos +- Suporte a Python >= 3.11 com API moderna orientada a objetos ## Exemplo Rápido @@ -29,7 +29,7 @@ print(f"Usinas termelétricas no estágio 1: {len(termicas)}") ## Instalação -O idecomp é compatível com versões de Python >= 3.10. +O idecomp é compatível com versões de Python >= 3.11. Instalação com pip (recomendado): diff --git a/docs/source/geral/instalacao.rst b/docs/source/geral/instalacao.rst index adc042f..e7ed1a3 100644 --- a/docs/source/geral/instalacao.rst +++ b/docs/source/geral/instalacao.rst @@ -1,7 +1,7 @@ Instalação ============ -O *idecomp* requer Python >= 3.10. +O *idecomp* requer Python >= 3.11. Use um ambiente virtual para instalar dependências. Veja a documentação oficial de `venv `_ para detalhes. diff --git a/docs/source/guias/faq.rst b/docs/source/guias/faq.rst index 1ea1e81..d5a08cf 100644 --- a/docs/source/guias/faq.rst +++ b/docs/source/guias/faq.rst @@ -28,20 +28,20 @@ de dependências. Qual versão do Python é compatível com o idecomp? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -O *idecomp* requer **Python 3.10 ou superior**. As versões 3.10, 3.11 e 3.12 são +O *idecomp* requer **Python 3.11 ou superior**. As versões 3.11, 3.12, 3.13 e 3.14 são testadas no CI e recomendadas para uso em produção. Estou com problemas de conflito de versão com o cfinterface. O que fazer? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ O *idecomp* depende da biblioteca `cfinterface `_, -que é responsável pelo mecanismo de parse dos arquivos. A versão compatível é fixada em -``<=1.8.3``. Caso você tenha uma versão incompatível instalada, force a reinstalação com +que é responsável pelo mecanismo de parse dos arquivos. A versão compatível é +``>=1.9.0``. Caso você tenha uma versão incompatível instalada, force a reinstalação com a restrição correta: .. code-block:: bash - pip install "cfinterface<=1.8.3" --force-reinstall + pip install "cfinterface>=1.9.0" --force-reinstall Se o conflito persistir, crie um ambiente virtual limpo e instale o *idecomp* a partir do zero: diff --git a/idecomp/decomp/arquivos.py b/idecomp/decomp/arquivos.py index f1b112c..1335664 100644 --- a/idecomp/decomp/arquivos.py +++ b/idecomp/decomp/arquivos.py @@ -1,7 +1,8 @@ -from idecomp.decomp.modelos.arquivos import BlocoNomesArquivos +from typing import TypeVar from cfinterface.files.sectionfile import SectionFile -from typing import TypeVar, Optional, List + +from idecomp.decomp.modelos.arquivos import BlocoNomesArquivos class Arquivos(SectionFile): @@ -19,7 +20,7 @@ class Arquivos(SectionFile): SECTIONS = [BlocoNomesArquivos] - def __le_nome_por_indice(self, indice: int) -> Optional[str]: + def __le_nome_por_indice(self, indice: int) -> str | None: b = self.data.get_sections_of_type(BlocoNomesArquivos) if isinstance(b, BlocoNomesArquivos): if indice in b.data.index: @@ -28,14 +29,14 @@ def __le_nome_por_indice(self, indice: int) -> Optional[str]: return dado return None - def __atualiza_nome_por_indice(self, indice: int, nome: str): + def __atualiza_nome_por_indice(self, indice: int, nome: str) -> None: b = self.data.get_sections_of_type(BlocoNomesArquivos) if isinstance(b, BlocoNomesArquivos): if indice in b.data.index: b.data.iloc[indice, 0] = nome @property - def arquivos(self) -> List[str]: + def arquivos(self) -> list[str]: """ Os nomes dos arquivos utilizados. @@ -48,78 +49,78 @@ def arquivos(self) -> List[str]: ) @property - def dadger(self) -> Optional[str]: + def dadger(self) -> str | None: """ Nome do arquivo de dados gerais utilizado pelo DECOMP. """ return self.__le_nome_por_indice(0) @dadger.setter - def dadger(self, arq: str): + def dadger(self, arq: str) -> None: self.__atualiza_nome_por_indice(0, arq) @property - def vazoes(self) -> Optional[str]: + def vazoes(self) -> str | None: """ Nome do arquivo de vazões incrementais afluentes. """ return self.__le_nome_por_indice(1) @vazoes.setter - def vazoes(self, arq: str): + def vazoes(self, arq: str) -> None: self.__atualiza_nome_por_indice(1, arq) @property - def hidr(self) -> Optional[str]: + def hidr(self) -> str | None: """ Nome do arquivo de cadastro dos dados das hidrelétricas. """ return self.__le_nome_por_indice(2) @hidr.setter - def hidr(self, arq: str): + def hidr(self, arq: str) -> None: self.__atualiza_nome_por_indice(2, arq) @property - def mlt(self) -> Optional[str]: + def mlt(self) -> str | None: """ Nome do arquivo com as médias mensais de longo termo (MLT). """ return self.__le_nome_por_indice(3) @mlt.setter - def mlt(self, arq: str): + def mlt(self, arq: str) -> None: self.__atualiza_nome_por_indice(3, arq) @property - def perdas(self) -> Optional[str]: + def perdas(self) -> str | None: """ Nome do arquivo com as perdas no sistema. """ return self.__le_nome_por_indice(4) @perdas.setter - def perdas(self, arq: str): + def perdas(self, arq: str) -> None: self.__atualiza_nome_por_indice(4, arq) @property - def dadgnl(self) -> Optional[str]: + def dadgnl(self) -> str | None: """ Nome do arquivo com os dados das usinas térmicas GNL. """ return self.__le_nome_por_indice(5) @dadgnl.setter - def dadgnl(self, arq: str): + def dadgnl(self, arq: str) -> None: self.__atualiza_nome_por_indice(5, arq) @property - def caminho(self) -> Optional[str]: + def caminho(self) -> str | None: """ Caminho para os executáveis do DECOMP. """ return self.__le_nome_por_indice(6) @caminho.setter - def caminho(self, arq: str): + def caminho(self, arq: str) -> None: self.__atualiza_nome_por_indice(6, arq) diff --git a/idecomp/decomp/avl_cortesfpha_dec.py b/idecomp/decomp/avl_cortesfpha_dec.py index 3b1a225..0461dfb 100644 --- a/idecomp/decomp/avl_cortesfpha_dec.py +++ b/idecomp/decomp/avl_cortesfpha_dec.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.avl_cortesfpha_dec import TabelaCortesFpha +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.avl_cortesfpha_dec import TabelaCortesFpha +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo class AvlCortesFpha(ArquivoCSV): @@ -14,7 +13,7 @@ class AvlCortesFpha(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaCortesFpha] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/avl_turb_max.py b/idecomp/decomp/avl_turb_max.py index 72ed2c5..a514436 100644 --- a/idecomp/decomp/avl_turb_max.py +++ b/idecomp/decomp/avl_turb_max.py @@ -1,5 +1,3 @@ -from typing import Optional - import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV @@ -14,7 +12,7 @@ class AvlTurbMax(ArquivoCSV): BLOCKS = [TabelaAvlTurbMax] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/caso.py b/idecomp/decomp/caso.py index b5be5d0..b0c6959 100644 --- a/idecomp/decomp/caso.py +++ b/idecomp/decomp/caso.py @@ -1,7 +1,8 @@ -from idecomp.decomp.modelos.caso import NomeCaso +from typing import Any, TypeVar from cfinterface.files.sectionfile import SectionFile -from typing import TypeVar, Optional + +from idecomp.decomp.modelos.caso import NomeCaso class Caso(SectionFile): @@ -18,11 +19,11 @@ class Caso(SectionFile): SECTIONS = [NomeCaso] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) @property - def arquivos(self) -> Optional[str]: + def arquivos(self) -> str | None: """ Caminho para o arquivo `arquivos.dat` de entrada do DECOMP. @@ -35,7 +36,7 @@ def arquivos(self) -> Optional[str]: return None @arquivos.setter - def arquivos(self, a: str): + def arquivos(self, a: str) -> None: b = self.data.get_sections_of_type(NomeCaso) if isinstance(b, NomeCaso): b.data = a diff --git a/idecomp/decomp/cortdeco.py b/idecomp/decomp/cortdeco.py index 7d91d3d..120398b 100644 --- a/idecomp/decomp/cortdeco.py +++ b/idecomp/decomp/cortdeco.py @@ -1,8 +1,9 @@ +from typing import IO, Any, TypeVar + +import pandas as pd # type: ignore[import-untyped] from cfinterface.files.sectionfile import SectionFile -from idecomp.decomp.modelos.cortdeco import SecaoDadosCortdeco -import pandas as pd # type: ignore -from typing import TypeVar, Optional, Union, List, IO +from idecomp.decomp.modelos.cortdeco import SecaoDadosCortdeco class Cortdeco(SectionFile): @@ -16,25 +17,25 @@ class Cortdeco(SectionFile): SECTIONS = [SecaoDadosCortdeco] STORAGE = "BINARY" - def __obtem_secao_cortdeco(self) -> Optional[pd.DataFrame]: + def __obtem_secao_cortdeco(self) -> pd.DataFrame | None: s = self.data.get_sections_of_type(SecaoDadosCortdeco) return s.data if not isinstance(s, list) and s is not None else None @classmethod def read( cls, - content: Union[str, bytes], + content: str | bytes, tamanho_registro: int = 0, registro_ultimo_corte_no: pd.DataFrame = pd.DataFrame(), numero_total_cortes: int = 0, numero_patamares_carga: int = 3, numero_estagios: int = 0, - codigos_uhes: List[int] = [], - codigos_uhes_tempo_viagem: List[int] = [], - codigos_submercados: List[int] = [], + codigos_uhes: list[int] = [], + codigos_uhes_tempo_viagem: list[int] = [], + codigos_submercados: list[int] = [], lag_maximo_tempo_viagem: int = 3, - *args, - **kwargs + *args: Any, + **kwargs: Any, ) -> "Cortdeco": a = super().read( content, @@ -48,26 +49,26 @@ def read( codigos_submercados=codigos_submercados, lag_maximo_tempo_viagem=lag_maximo_tempo_viagem, *args, - **kwargs + **kwargs, ) - return a + return a # type: ignore[return-value] def write( self, - to: Union[str, IO], + to: str | IO[Any], df_registro_ultimo_corte_no: pd.DataFrame = pd.DataFrame(), - *args, - **kwargs - ): + *args: Any, + **kwargs: Any, + ) -> None: super().write( to, df_registro_ultimo_corte_no=df_registro_ultimo_corte_no, *args, - **kwargs + **kwargs, ) @property - def cortes(self) -> Optional[pd.DataFrame]: + def cortes(self) -> pd.DataFrame | None: """ Retorna o conjunto dos cortes de Benders construídos durante o cálculo da política @@ -99,13 +100,13 @@ def cortes(self) -> Optional[pd.DataFrame]: return self.__obtem_secao_cortdeco() @cortes.setter - def cortes(self, df: pd.DataFrame): + def cortes(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_cortdeco() if dados is not None: dados = df @property - def coeficientes_volume_armazenado(self) -> Optional[pd.DataFrame]: + def coeficientes_volume_armazenado(self) -> pd.DataFrame | None: """ Retorna o conjunto dos coeficientes dos cortes de Benders construídos durante o cálculo da política para os eixos de volume armazenado @@ -159,7 +160,7 @@ def __cria_data_frame(df_cortes: pd.DataFrame) -> pd.DataFrame: return None @property - def coeficientes_defluencia_tempo_viagem(self) -> Optional[pd.DataFrame]: + def coeficientes_defluencia_tempo_viagem(self) -> pd.DataFrame | None: """ Retorna o conjunto dos coeficientes dos cortes de Benders construídos durante o cálculo da política para os eixos de vazão defluente passada @@ -188,9 +189,7 @@ def __cria_data_frame(df_cortes: pd.DataFrame) -> pd.DataFrame: "uhe", expand=True )[1] df_melted["codigo_usina"] = ( - df_melted["variable"] - .str.split("_", expand=True)[0] - .astype(int) + df_melted["variable"].str.split("_", expand=True)[0].astype(int) ) df_melted["lag"] = ( df_melted["variable"] @@ -223,7 +222,7 @@ def __cria_data_frame(df_cortes: pd.DataFrame) -> pd.DataFrame: return None @property - def coeficientes_geracao_gnl(self) -> Optional[pd.DataFrame]: + def coeficientes_geracao_gnl(self) -> pd.DataFrame | None: """ Retorna o conjunto dos coeficientes dos cortes de Benders construídos durante o cálculo da política para os eixos de geração térmica diff --git a/idecomp/decomp/custos.py b/idecomp/decomp/custos.py index 57db1cf..0c9acdb 100644 --- a/idecomp/decomp/custos.py +++ b/idecomp/decomp/custos.py @@ -1,9 +1,10 @@ -from idecomp.decomp.modelos.custos import BlocoRelatorioCustos +from typing import Any, TypeVar +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block from cfinterface.files.blockfile import BlockFile -from typing import List, TypeVar, Optional -import pandas as pd # type: ignore + +from idecomp.decomp.modelos.custos import BlocoRelatorioCustos class Custos(BlockFile): @@ -22,14 +23,14 @@ class Custos(BlockFile): BlocoRelatorioCustos, ] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) self.__relatorios_variaveis_duais = None self.__relatorios_fcf = None def __concatena_blocos( - self, blocos, indice_data: int - ) -> Optional[pd.DataFrame]: + self, blocos: list[Any], indice_data: int + ) -> pd.DataFrame | None: """ Adiciona uma coluna com o estágio de cada bloco, assumindo a mesma ordem das séries de energia. @@ -52,7 +53,7 @@ def __concatena_blocos( return None @property - def relatorio_variaveis_duais(self) -> Optional[pd.DataFrame]: + def relatorio_variaveis_duais(self) -> pd.DataFrame | None: """ Obtém a tabela das variáveis duais do armazenamento de cada UHE, nos problemas resolvidos pelo DECOMP. @@ -66,7 +67,7 @@ def relatorio_variaveis_duais(self) -> Optional[pd.DataFrame]: :rtype: pd.DataFrame | None """ if self.__relatorios_variaveis_duais is None: - blocos_custos: List[BlocoRelatorioCustos] = [] + blocos_custos: list[BlocoRelatorioCustos] = [] for b in self.data.of_type(BlocoRelatorioCustos): blocos_custos.append(b) self.__relatorios_variaveis_duais = self.__concatena_blocos( @@ -75,7 +76,7 @@ def relatorio_variaveis_duais(self) -> Optional[pd.DataFrame]: return self.__relatorios_variaveis_duais @property - def relatorio_fcf(self) -> Optional[pd.DataFrame]: + def relatorio_fcf(self) -> pd.DataFrame | None: """ Obtém a tabela dos cortes da FCF nos quais o DECOMP acoplou durante a resolução dos seus problemas. @@ -89,7 +90,7 @@ def relatorio_fcf(self) -> Optional[pd.DataFrame]: :rtype: pd.DataFrame | None """ if self.__relatorios_fcf is None: - blocos_custos: List[BlocoRelatorioCustos] = [] + blocos_custos: list[BlocoRelatorioCustos] = [] for b in self.data.of_type(BlocoRelatorioCustos): blocos_custos.append(b) self.__relatorios_fcf = self.__concatena_blocos(blocos_custos, 1) diff --git a/idecomp/decomp/dadger.py b/idecomp/decomp/dadger.py index f8749e2..7fde43b 100644 --- a/idecomp/decomp/dadger.py +++ b/idecomp/decomp/dadger.py @@ -1,7 +1,7 @@ -from typing import Any, List, Optional, Type, TypeVar, Union +from typing import Any, TypeVar, Union import numpy as np -import pandas # type: ignore +import pandas # type: ignore[import-untyped] from cfinterface.components.register import Register from cfinterface.files.registerfile import RegisterFile @@ -217,7 +217,7 @@ class Dadger(RegisterFile): DA, ] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) def __expande_colunas_df(self, df: pandas.DataFrame) -> pandas.DataFrame: @@ -229,8 +229,9 @@ def __expande_colunas_df(self, df: pandas.DataFrame) -> pandas.DataFrame: num_elementos = len(df.at[0, c]) particoes_coluna = [f"{c}_{i}" for i in range(1, num_elementos + 1)] df[particoes_coluna] = df.apply( - lambda linha: linha[c] - + [np.nan] * max(0, num_elementos - len(linha[c])), + lambda linha: ( + linha[c] + [np.nan] * max(0, num_elementos - len(linha[c])) + ), axis=1, result_type="expand", ) @@ -238,8 +239,8 @@ def __expande_colunas_df(self, df: pandas.DataFrame) -> pandas.DataFrame: return df def __registros_ou_df( - self, t: Type[T], **kwargs - ) -> Optional[Union[T, List[T], pandas.DataFrame]]: + self, t: type[T], **kwargs: Any + ) -> T | list[T] | pandas.DataFrame | None: if kwargs.get("df"): return self.__expande_colunas_df(self._as_df(t)) else: @@ -247,7 +248,7 @@ def __registros_ou_df( return self.data.get_registers_of_type(t, **kwargs_sem_df) @property - def te(self) -> Optional[TE]: + def te(self) -> TE | None: """ Obtém o (único) registro que define o nome do estudo no :class:`Dadger` @@ -263,10 +264,10 @@ def te(self) -> Optional[TE]: def sb( self, - codigo_submercado: Optional[int] = None, - nome_submercado: Optional[str] = None, + codigo_submercado: int | None = None, + nome_submercado: str | None = None, df: bool = False, - ) -> Optional[Union[SB, List[SB], pandas.DataFrame]]: + ) -> SB | list[SB] | pandas.DataFrame | None: """ Obtém um registro que define os submercados existentes no estudo descrito pelo :class:`Dadger`. @@ -287,12 +288,12 @@ def sb( def uh( self, - codigo_usina: Optional[int] = None, - codigo_ree: Optional[int] = None, - volume_inicial: Optional[float] = None, - evaporacao: Optional[int] = None, + codigo_usina: int | None = None, + codigo_ree: int | None = None, + volume_inicial: float | None = None, + evaporacao: int | None = None, df: bool = False, - ) -> Optional[Union[UH, List[UH], pandas.DataFrame]]: + ) -> UH | list[UH] | pandas.DataFrame | None: """ Obtém um registro que define uma usina hidrelétrica existente no estudo descrito pelo :class:`Dadger`. @@ -323,13 +324,13 @@ def uh( def ue( self, - codigo_usina: Optional[int] = None, - codigo_submercado: Optional[int] = None, - nome_usina: Optional[str] = None, - codigo_usina_montante: Optional[int] = None, - codigo_usina_jusante: Optional[int] = None, + codigo_usina: int | None = None, + codigo_submercado: int | None = None, + nome_usina: str | None = None, + codigo_usina_montante: int | None = None, + codigo_usina_jusante: int | None = None, df: bool = False, - ) -> Optional[Union[UE, List[UE], pandas.DataFrame]]: + ) -> UE | list[UE] | pandas.DataFrame | None: """ Obtém um registro que define uma usina elevatória existente no estudo descrito pelo :class:`Dadger`. @@ -366,12 +367,12 @@ def ue( def ct( self, - codigo_usina: Optional[int] = None, - estagio: Optional[int] = None, - codigo_submercado: Optional[int] = None, - nome_usina: Optional[str] = None, + codigo_usina: int | None = None, + estagio: int | None = None, + codigo_submercado: int | None = None, + nome_usina: str | None = None, df: bool = False, - ) -> Optional[Union[CT, List[CT], pandas.DataFrame]]: + ) -> CT | list[CT] | pandas.DataFrame | None: """ Obtém um registro que define uma usina termelétrica existente no estudo descrito pelo :class:`Dadger`. @@ -402,11 +403,11 @@ def ct( def dp( self, - estagio: Optional[int] = None, - codigo_submercado: Optional[int] = None, - numero_patamares: Optional[int] = None, + estagio: int | None = None, + codigo_submercado: int | None = None, + numero_patamares: int | None = None, df: bool = False, - ) -> Optional[Union[DP, List[DP], pandas.DataFrame]]: + ) -> DP | list[DP] | pandas.DataFrame | None: """ Obtém um registro que define as durações dos patamares no estudo descrito pelo :class:`Dadger`. @@ -437,11 +438,11 @@ def dp( def pq( self, - nome: Optional[str] = None, - codigo_submercado: Optional[int] = None, - estagio: Optional[int] = None, + nome: str | None = None, + codigo_submercado: int | None = None, + estagio: int | None = None, df: bool = False, - ) -> Optional[Union[PQ, List[PQ], pandas.DataFrame]]: + ) -> PQ | list[PQ] | pandas.DataFrame | None: """ Obtém um registro que define as gerações das pequenas usinas no estudo descrito pelo :class:`Dadger`. @@ -471,11 +472,11 @@ def pq( def ia( self, - estagio: Optional[int] = None, - nome_submercado_de: Optional[str] = None, - nome_submercado_para: Optional[str] = None, + estagio: int | None = None, + nome_submercado_de: str | None = None, + nome_submercado_para: str | None = None, df: bool = False, - ) -> Optional[Union[IA, List[IA], pandas.DataFrame]]: + ) -> IA | list[IA] | pandas.DataFrame | None: """ Obtém um registro que define os limites de intercâmbio no estudo descrito pelo :class:`Dadger`. @@ -504,11 +505,11 @@ def ia( def ri( self, - codigo_usina: Optional[int] = None, - estagio: Optional[int] = None, - codigo_submercado: Optional[int] = None, + codigo_usina: int | None = None, + estagio: int | None = None, + codigo_submercado: int | None = None, df: bool = False, - ) -> Optional[Union[RI, List[RI], pandas.DataFrame]]: + ) -> RI | list[RI] | pandas.DataFrame | None: """ Obtém um registro que define as restrições de Itaipu no estudo descrito pelo :class:`Dadger`. @@ -539,8 +540,8 @@ def ac( codigo_usina: int, modificacao: Any, df: bool = False, - **kwargs, - ) -> Optional[Union[AC, List[AC], pandas.DataFrame]]: + **kwargs: Any, + ) -> AC | list[AC] | pandas.DataFrame | None: """ Obtém um registro que define modificações nos parâmetros das UHE em um :class:`Dadger`. @@ -562,12 +563,12 @@ def ac( def cd( self, - codigo_curva: Optional[int] = None, - codigo_submercado: Optional[int] = None, - nome_curva: Optional[str] = None, - estagio: Optional[int] = None, + codigo_curva: int | None = None, + codigo_submercado: int | None = None, + nome_curva: str | None = None, + estagio: int | None = None, df: bool = False, - ) -> Optional[Union[CD, List[CD], pandas.DataFrame]]: + ) -> CD | list[CD] | pandas.DataFrame | None: """ Obtém um registro que define as curvas de déficit no estudo descrito pelo :class:`Dadger`. @@ -597,7 +598,7 @@ def cd( ) @property - def tx(self) -> Optional[TX]: + def tx(self) -> TX | None: """ Obtém o (único) registro que define a taxa de desconto aplicada no estudo definido no :class:`Dadger` @@ -612,7 +613,7 @@ def tx(self) -> Optional[TX]: return None @property - def gp(self) -> Optional[GP]: + def gp(self) -> GP | None: """ Obtém o (único) registro que define o gap para convergência considerado no estudo definido no :class:`Dadger` @@ -627,7 +628,7 @@ def gp(self) -> Optional[GP]: return None @property - def ni(self) -> Optional[NI]: + def ni(self) -> NI | None: """ Obtém o (único) registro que define o número máximo de iterações do DECOMP no estudo definido no :class:`Dadger` @@ -642,7 +643,7 @@ def ni(self) -> Optional[NI]: return None @property - def dt(self) -> Optional[DT]: + def dt(self) -> DT | None: """ Obtém o (único) registro que define a data de referência do estudo definido no :class:`Dadger` @@ -658,11 +659,11 @@ def dt(self) -> Optional[DT]: def re( self, - codigo_restricao: Optional[int] = None, - estagio_inicial: Optional[int] = None, - estagio_final: Optional[int] = None, + codigo_restricao: int | None = None, + estagio_inicial: int | None = None, + estagio_final: int | None = None, df: bool = False, - ) -> Optional[Union[RE, List[RE], pandas.DataFrame]]: + ) -> RE | list[RE] | pandas.DataFrame | None: """ Obtém um registro que cadastra uma restrição elétrica existente no estudo descrito pelo :class:`Dadger`. @@ -691,10 +692,10 @@ def re( def lu( # noqa self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, df: bool = False, - ) -> Optional[Union[LU, List[LU], pandas.DataFrame]]: + ) -> LU | list[LU] | pandas.DataFrame | None: """ Obtém um registro que especifica os limites inferiores e superiores por patamar de uma restrição elétrica existente @@ -742,7 +743,7 @@ def lu( # noqa """ - def cria_registro() -> Optional[LU]: + def cria_registro() -> LU | None: re = self.re(codigo_restricao=codigo_restricao) if isinstance(re, list) or re is None: return None @@ -786,12 +787,12 @@ def cria_registro() -> Optional[LU]: def fu( self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, - codigo_usina: Optional[int] = None, - coeficiente: Optional[float] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, + codigo_usina: int | None = None, + coeficiente: float | None = None, df: bool = False, - ) -> Optional[Union[FU, List[FU], pandas.DataFrame]]: + ) -> FU | list[FU] | pandas.DataFrame | None: """ Obtém um registro que cadastra os coeficientes das restrições elétricas. @@ -823,12 +824,12 @@ def fu( def ft( self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, - codigo_usina: Optional[int] = None, - coeficiente: Optional[float] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, + codigo_usina: int | None = None, + coeficiente: float | None = None, df: bool = False, - ) -> Optional[Union[FT, List[FT], pandas.DataFrame]]: + ) -> FT | list[FT] | pandas.DataFrame | None: """ Obtém um registro que cadastra os coeficientes das restrições elétricas. @@ -860,13 +861,13 @@ def ft( def fi( self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, - codigo_submercado_de: Optional[int] = None, - codigo_submercado_para: Optional[int] = None, - coeficiente: Optional[float] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, + codigo_submercado_de: int | None = None, + codigo_submercado_para: int | None = None, + coeficiente: float | None = None, df: bool = False, - ) -> Optional[Union[FI, List[FI], pandas.DataFrame]]: + ) -> FI | list[FI] | pandas.DataFrame | None: """ Obtém um registro que cadastra os coeficientes das restrições elétricas. @@ -901,10 +902,10 @@ def fi( def vi( self, - codigo_usina: Optional[int] = None, - duracao: Optional[int] = None, + codigo_usina: int | None = None, + duracao: int | None = None, df: bool = False, - ) -> Optional[Union[VI, List[VI], pandas.DataFrame]]: + ) -> VI | list[VI] | pandas.DataFrame | None: """ Obtém um registro que especifica os tempos de viagem da água em uma UHE existente no no estudo descrito @@ -926,8 +927,8 @@ def vi( ) def ir( - self, tipo: Optional[str] = None, df: bool = False - ) -> Optional[Union[IR, List[IR], pandas.DataFrame]]: + self, tipo: str | None = None, df: bool = False + ) -> IR | list[IR] | pandas.DataFrame | None: """ Obtém um registro que especifica os relatórios de saída a serem produzidos pelo DECOMP após a execução do estudo @@ -946,8 +947,8 @@ def ir( return self.__registros_ou_df(IR, tipo=tipo, df=df) def rt( - self, restricao: Optional[str] = None, df: bool = False - ) -> Optional[Union[RT, List[RT], pandas.DataFrame]]: + self, restricao: str | None = None, df: bool = False + ) -> RT | list[RT] | pandas.DataFrame | None: """ Obtém um registro que especifica uma retirada de restrição de soleira de vertedouro ou canal de desvio. @@ -966,10 +967,10 @@ def rt( def fc( self, - tipo: Optional[str] = None, - caminho: Optional[str] = None, + tipo: str | None = None, + caminho: str | None = None, df: bool = False, - ) -> Optional[Union[FC, List[FC], pandas.DataFrame]]: + ) -> FC | list[FC] | pandas.DataFrame | None: """ Obtém um registro que especifica os caminhos para os arquivos com a FCF do NEWAVE. @@ -989,8 +990,8 @@ def fc( return self.__registros_ou_df(FC, tipo=tipo, caminho=caminho, df=df) def ea( - self, codigo_ree: Optional[int] = None, df: bool = False - ) -> Optional[Union[EA, List[EA], pandas.DataFrame]]: + self, codigo_ree: int | None = None, df: bool = False + ) -> EA | list[EA] | pandas.DataFrame | None: """ Obtém um registro que especifica a ENA dos meses anteriores ao estudo. @@ -1007,10 +1008,10 @@ def ea( def es( self, - codigo_ree: Optional[int] = None, - numero_semanas: Optional[int] = None, + codigo_ree: int | None = None, + numero_semanas: int | None = None, df: bool = False, - ) -> Optional[Union[ES, List[ES], pandas.DataFrame]]: + ) -> ES | list[ES] | pandas.DataFrame | None: """ Obtém um registro que especifica a ENA das semanas anteriores ao estudo. @@ -1031,8 +1032,8 @@ def es( ) def qi( - self, codigo_usina: Optional[int] = None, df: bool = False - ) -> Optional[Union[QI, List[QI], pandas.DataFrame]]: + self, codigo_usina: int | None = None, df: bool = False + ) -> QI | list[QI] | pandas.DataFrame | None: """ Obtém um registro que especifica o tempo de viagem para cálculo da ENA. @@ -1049,8 +1050,8 @@ def qi( return self.__registros_ou_df(QI, codigo_usina=codigo_usina, df=df) def ti( - self, codigo_usina: Optional[int] = None, df: bool = False - ) -> Optional[Union[TI, List[TI], pandas.DataFrame]]: + self, codigo_usina: int | None = None, df: bool = False + ) -> TI | list[TI] | pandas.DataFrame | None: """ Obtém um registro que especifica as taxas de irrigação por posto (UHE) existente no estudo especificado no :class:`Dadger` @@ -1068,10 +1069,10 @@ def ti( def da( self, - codigo_usina_retirada: Optional[int] = None, - codigo_usina_retorno: Optional[int] = None, + codigo_usina_retirada: int | None = None, + codigo_usina_retorno: int | None = None, df: bool = False, - ) -> Optional[Union[DA, List[DA], pandas.DataFrame]]: + ) -> DA | list[DA] | pandas.DataFrame | None: """ Obtém um registro que especifica as retiradas de água para outros usos (desvios de água) por usina (UHE) existente no @@ -1097,10 +1098,10 @@ def da( def mp( self, - codigo_usina: Optional[int] = None, - frequencia: Optional[int] = None, + codigo_usina: int | None = None, + frequencia: int | None = None, df: bool = False, - ) -> Optional[Union[MP, List[MP], pandas.DataFrame]]: + ) -> MP | list[MP] | pandas.DataFrame | None: """ Obtém um registro que especifica as manutenções programadas por UHE existente no estudo especificado no :class:`Dadger` @@ -1122,10 +1123,10 @@ def mp( def mt( self, - codigo_usina: Optional[int] = None, - codigo_submercado: Optional[int] = None, + codigo_usina: int | None = None, + codigo_submercado: int | None = None, df: bool = False, - ) -> Optional[Union[MT, List[MT], pandas.DataFrame]]: + ) -> MT | list[MT] | pandas.DataFrame | None: """ Obtém um registro que especifica as manutenções programadas por UTE existente no estudo especificado no :class:`Dadger` @@ -1150,10 +1151,10 @@ def mt( def fd( self, - codigo_usina: Optional[int] = None, - frequencia: Optional[int] = None, + codigo_usina: int | None = None, + frequencia: int | None = None, df: bool = False, - ) -> Optional[Union[FD, List[FD], pandas.DataFrame]]: + ) -> FD | list[FD] | pandas.DataFrame | None: """ Obtém um registro que especifica os fatores de disponibilidade por UHE existente no estudo especificado no :class:`Dadger` @@ -1175,18 +1176,18 @@ def fd( def fp( self, - codigo_usina: Optional[int] = None, - estagio: Optional[int] = None, - tipo_entrada_janela_turbinamento: Optional[int] = None, - numero_pontos_turbinamento: Optional[int] = None, - limite_inferior_janela_turbinamento: Optional[float] = None, - limite_superior_janela_turbinamento: Optional[float] = None, - tipo_entrada_janela_volume: Optional[int] = None, - numero_pontos_volume: Optional[int] = None, - limite_inferior_janela_volume: Optional[float] = None, - limite_superior_janela_volume: Optional[float] = None, + codigo_usina: int | None = None, + estagio: int | None = None, + tipo_entrada_janela_turbinamento: int | None = None, + numero_pontos_turbinamento: int | None = None, + limite_inferior_janela_turbinamento: float | None = None, + limite_superior_janela_turbinamento: float | None = None, + tipo_entrada_janela_volume: int | None = None, + numero_pontos_volume: int | None = None, + limite_inferior_janela_volume: float | None = None, + limite_superior_janela_volume: float | None = None, df: bool = False, - ) -> Optional[Union[FP, List[FP], pandas.DataFrame]]: + ) -> FP | list[FP] | pandas.DataFrame | None: """ Obtém um registro que especifica as taxas de irrigação por posto (UHE) existente no estudo especificado no :class:`Dadger` @@ -1242,8 +1243,8 @@ def fp( ) def rq( - self, codigo_ree: Optional[int] = None, df: bool = False - ) -> Optional[Union[RQ, List[RQ], pandas.DataFrame]]: + self, codigo_ree: int | None = None, df: bool = False + ) -> RQ | list[RQ] | pandas.DataFrame | None: """ Obtém um registro que especifica as vazões mínimas históricas por REE existentes no estudo especificado no :class:`Dadger` @@ -1260,8 +1261,8 @@ def rq( return self.__registros_ou_df(RQ, codigo_ree=codigo_ree, df=df) def ve( - self, codigo_usina: Optional[int] = None, df: bool = False - ) -> Optional[Union[VE, List[VE], pandas.DataFrame]]: + self, codigo_usina: int | None = None, df: bool = False + ) -> VE | list[VE] | pandas.DataFrame | None: """ Obtém um registro que especifica os volumes de espera por posto (UHE) existente no estudo especificado no :class:`Dadger` @@ -1279,11 +1280,11 @@ def ve( def hv( self, - codigo_restricao: Optional[int] = None, - estagio_inicial: Optional[int] = None, - estagio_final: Optional[int] = None, + codigo_restricao: int | None = None, + estagio_inicial: int | None = None, + estagio_final: int | None = None, df: bool = False, - ) -> Optional[Union[HV, List[HV], pandas.DataFrame]]: + ) -> HV | list[HV] | pandas.DataFrame | None: """ Obtém um registro que cadastra uma restrição de volume mínimo armazenado existente no estudo descrito pelo :class:`Dadger`. @@ -1312,10 +1313,10 @@ def hv( def lv( # noqa self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, df: bool = False, - ) -> Optional[Union[LV, List[LV], pandas.DataFrame]]: + ) -> LV | list[LV] | pandas.DataFrame | None: """ Obtém um registro que especifica os limites inferior e superior de uma restrição de volume mínimo existente @@ -1363,7 +1364,7 @@ def lv( # noqa """ - def cria_registro() -> Optional[LV]: + def cria_registro() -> LV | None: hv = self.hv(codigo_restricao=codigo_restricao) if isinstance(hv, list) or hv is None: return None @@ -1405,13 +1406,13 @@ def cria_registro() -> Optional[LV]: def cv( self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, - codigo_usina: Optional[int] = None, - coeficiente: Optional[float] = None, - tipo: Optional[str] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, + codigo_usina: int | None = None, + coeficiente: float | None = None, + tipo: str | None = None, df: bool = False, - ) -> Optional[Union[CV, List[CV], pandas.DataFrame]]: + ) -> CV | list[CV] | pandas.DataFrame | None: """ Obtém um registro que cadastra os coeficientes das restrições de volume. @@ -1446,11 +1447,11 @@ def cv( def hq( self, - codigo_restricao: Optional[int] = None, - estagio_inicial: Optional[int] = None, - estagio_final: Optional[int] = None, + codigo_restricao: int | None = None, + estagio_inicial: int | None = None, + estagio_final: int | None = None, df: bool = False, - ) -> Optional[Union[HQ, List[HQ], pandas.DataFrame]]: + ) -> HQ | list[HQ] | pandas.DataFrame | None: """ Obtém um registro que cadastra uma restrição de vazão existente no estudo descrito pelo :class:`Dadger`. @@ -1479,10 +1480,10 @@ def hq( def lq( # noqa self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, df: bool = False, - ) -> Optional[Union[LQ, List[LQ], pandas.DataFrame]]: + ) -> LQ | list[LQ] | pandas.DataFrame | None: """ Obtém um registro que especifica os limites inferiores e superiores por patamar de uma restrição de vazão existente @@ -1530,7 +1531,7 @@ def lq( # noqa """ - def cria_registro() -> Optional[LQ]: + def cria_registro() -> LQ | None: hq = self.hq(codigo_restricao=codigo_restricao) if isinstance(hq, list) or hq is None: return None @@ -1572,13 +1573,13 @@ def cria_registro() -> Optional[LQ]: def cq( self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, - codigo_usina: Optional[int] = None, - coeficiente: Optional[float] = None, - tipo: Optional[str] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, + codigo_usina: int | None = None, + coeficiente: float | None = None, + tipo: str | None = None, df: bool = False, - ) -> Optional[Union[CQ, List[CQ], pandas.DataFrame]]: + ) -> CQ | list[CQ] | pandas.DataFrame | None: """ Obtém um registro que cadastra os coeficientes das restrições de vazão. @@ -1613,16 +1614,16 @@ def cq( def he( self, - codigo_restricao: Optional[int] = None, - estagio: Optional[int] = None, - tipo_limite: Optional[int] = None, - forma_calculo_produtibilidades: Optional[int] = None, - tipo_valores_produtibilidades: Optional[int] = None, - tipo_penalidade: Optional[int] = None, - valor_penalidade: Optional[float] = None, - arquivo_produtibilidades: Optional[str] = None, + codigo_restricao: int | None = None, + estagio: int | None = None, + tipo_limite: int | None = None, + forma_calculo_produtibilidades: int | None = None, + tipo_valores_produtibilidades: int | None = None, + tipo_penalidade: int | None = None, + valor_penalidade: float | None = None, + arquivo_produtibilidades: str | None = None, df: bool = False, - ) -> Optional[Union[HE, List[HE], pandas.DataFrame]]: + ) -> HE | list[HE] | pandas.DataFrame | None: """ Obtém um registro que cadastra uma restrição de energia armazenada existente no estudo descrito pelo :class:`Dadger`. @@ -1671,11 +1672,11 @@ def he( def cm( self, - codigo_restricao: Optional[int] = None, - codigo_ree: Optional[int] = None, - coeficiente: Optional[float] = None, + codigo_restricao: int | None = None, + codigo_ree: int | None = None, + coeficiente: float | None = None, df: bool = False, - ) -> Optional[Union[CM, List[CM], pandas.DataFrame]]: + ) -> CM | list[CM] | pandas.DataFrame | None: """ Obtém um registro que cadastra os coeficientes das restrições de energia armazenada. @@ -1702,7 +1703,7 @@ def cm( ) @property - def ev(self) -> Optional[EV]: + def ev(self) -> EV | None: """ Obtém o (único) registro que define a evaporação :class:`Dadger` @@ -1717,7 +1718,7 @@ def ev(self) -> Optional[EV]: return None @property - def fj(self) -> Optional[FJ]: + def fj(self) -> FJ | None: """ Obtém o (único) registro que define o arquivo `polinjus` :class:`Dadger` @@ -1732,7 +1733,7 @@ def fj(self) -> Optional[FJ]: return None @property - def pu(self) -> Optional[PU]: + def pu(self) -> PU | None: """ Obtém o (único) registro que define se será usado PL único. @@ -1746,7 +1747,7 @@ def pu(self) -> Optional[PU]: return None @property - def rc(self) -> Optional[RC]: + def rc(self) -> RC | None: """ Obtém o (único) registro que insere restrições do tipo escada. @@ -1762,11 +1763,11 @@ def rc(self) -> Optional[RC]: def pe( self, - codigo_submercado: Optional[int] = None, - tipo: Optional[int] = None, - penalidade: Optional[float] = None, + codigo_submercado: int | None = None, + tipo: int | None = None, + penalidade: float | None = None, df: bool = False, - ) -> Optional[Union[PE, List[PE], pandas.DataFrame]]: + ) -> PE | list[PE] | pandas.DataFrame | None: """ Obtém um registro que altera penalidades de vertimento, intercâmbio e desvios. @@ -1794,11 +1795,11 @@ def pe( def ts( self, - tolerancia_primaria: Optional[float] = None, - tolerancia_secundaria: Optional[float] = None, - zera_coeficientes: Optional[int] = None, - tolerancia_teste_otimalidade: Optional[float] = None, - ) -> Optional[Union[TS, List[TS]]]: + tolerancia_primaria: float | None = None, + tolerancia_secundaria: float | None = None, + zera_coeficientes: int | None = None, + tolerancia_teste_otimalidade: float | None = None, + ) -> TS | list[TS] | None: """ Obtém um registro que altera as tolerâncias do solver. @@ -1825,13 +1826,13 @@ def ts( def pv( self, - penalidade_variaveis_folga: Optional[float] = None, - tolerancia_viabilidade_restricoes: Optional[float] = None, - iteracoes_atualizacao_penalidade: Optional[int] = None, - fator_multiplicacao_folga: Optional[float] = None, - valor_inicial_variaveis_folga: Optional[float] = None, - valor_final_variaveis_folga: Optional[float] = None, - ) -> Optional[Union[PV, List[PV]]]: + penalidade_variaveis_folga: float | None = None, + tolerancia_viabilidade_restricoes: float | None = None, + iteracoes_atualizacao_penalidade: int | None = None, + fator_multiplicacao_folga: float | None = None, + valor_inicial_variaveis_folga: float | None = None, + valor_final_variaveis_folga: float | None = None, + ) -> PV | list[PV] | None: """ Obtém um registro que altera as penalidades das variáveis de folga. @@ -1869,10 +1870,10 @@ def pv( def cx( self, - codigo_newave: Optional[int] = None, - codigo_decomp: Optional[int] = None, + codigo_newave: int | None = None, + codigo_decomp: int | None = None, df: bool = False, - ) -> Optional[Union[CX, List[CX], pandas.DataFrame]]: + ) -> CX | list[CX] | pandas.DataFrame | None: """ Obtém um registro que altera as tolerâncias do solver. @@ -1892,7 +1893,7 @@ def cx( ) @property - def fa(self) -> Optional[FA]: + def fa(self) -> FA | None: """ Obtém o (único) registro que define o arquivo de índices. @@ -1906,7 +1907,7 @@ def fa(self) -> Optional[FA]: return None @property - def vt(self) -> Optional[VT]: + def vt(self) -> VT | None: """ Obtém o (único) registro que define o arquivo com cenários de vento. @@ -1921,7 +1922,7 @@ def vt(self) -> Optional[VT]: return None @property - def cs(self) -> Optional[CS]: + def cs(self) -> CS | None: """ Obtém o (único) registro que habilita a consistência de dados. @@ -1937,9 +1938,9 @@ def cs(self) -> Optional[CS]: def vl( self, - codigo_usina_influenciada: Optional[int] = None, + codigo_usina_influenciada: int | None = None, df: bool = False, - ) -> Optional[Union[VL, List[VL], pandas.DataFrame]]: + ) -> VL | list[VL] | pandas.DataFrame | None: """ Obtém um registro que define uma usina hidrelétrica que sofre influência de vazão lateral na cota de jusante existente @@ -1962,10 +1963,10 @@ def vl( def vu( self, - codigo_usina_influenciada: Optional[int] = None, - codigo_usina_influenciadora: Optional[int] = None, + codigo_usina_influenciada: int | None = None, + codigo_usina_influenciadora: int | None = None, df: bool = False, - ) -> Optional[Union[VU, List[VU], pandas.DataFrame]]: + ) -> VU | list[VU] | pandas.DataFrame | None: """ Obtém um registro que define uma usina hidrelétrica que tem influencia sobre a vazão de jusante da primeira existente @@ -1991,10 +1992,10 @@ def vu( def va( self, - codigo_usina_influenciada: Optional[int] = None, - codigo_posto_influenciador: Optional[int] = None, + codigo_usina_influenciada: int | None = None, + codigo_posto_influenciador: int | None = None, df: bool = False, - ) -> Optional[Union[VA, List[VA], pandas.DataFrame]]: + ) -> VA | list[VA] | pandas.DataFrame | None: """ Obtém um registro que define um posto que tem influencia sobre a vazão de jusante da primeira existente @@ -2018,9 +2019,7 @@ def va( df=df, ) - def pd( - self, algoritmo: Optional[str] = None - ) -> Optional[Union[PD, List[PD]]]: + def pd(self, algoritmo: str | None = None) -> PD | list[PD] | None: """ Obtém um registro que especifica o algoritmo usado para a solução. diff --git a/idecomp/decomp/dadgnl.py b/idecomp/decomp/dadgnl.py index e952658..85cd183 100644 --- a/idecomp/decomp/dadgnl.py +++ b/idecomp/decomp/dadgnl.py @@ -1,8 +1,10 @@ +from typing import Any, TypeVar + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.register import Register from cfinterface.files.registerfile import RegisterFile -from idecomp.decomp.modelos.dadgnl import TG, GS, NL, GL -from typing import Type, List, Optional, TypeVar, Union -import pandas as pd # type: ignore + +from idecomp.decomp.modelos.dadgnl import GL, GS, NL, TG class Dadgnl(RegisterFile): @@ -23,21 +25,17 @@ class Dadgnl(RegisterFile): REGISTERS = [TG, GS, NL, GL] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) def __expande_colunas_df(self, df: pd.DataFrame) -> pd.DataFrame: - colunas_com_listas = df.map( - lambda linha: isinstance(linha, list) - ).all() + colunas_com_listas = df.map(lambda linha: isinstance(linha, list)).all() nomes_colunas = [ c for c in colunas_com_listas[colunas_com_listas].index ] for c in nomes_colunas: num_elementos = len(df.at[0, c]) - particoes_coluna = [ - f"{c}_{i}" for i in range(1, num_elementos + 1) - ] + particoes_coluna = [f"{c}_{i}" for i in range(1, num_elementos + 1)] df[particoes_coluna] = df.apply( lambda linha: linha[c], axis=1, result_type="expand" ) @@ -45,8 +43,8 @@ def __expande_colunas_df(self, df: pd.DataFrame) -> pd.DataFrame: return df def __registros_ou_df( - self, t: Type[T], **kwargs - ) -> Optional[Union[T, List[T], pd.DataFrame]]: + self, t: type[T], **kwargs: Any + ) -> T | list[T] | pd.DataFrame | None: if kwargs.get("df"): return self.__expande_colunas_df(self._as_df(t)) else: @@ -55,12 +53,12 @@ def __registros_ou_df( def tg( self, - codigo_usina: Optional[int] = None, - codigo_submercado: Optional[int] = None, - nome: Optional[str] = None, - estagio: Optional[int] = None, + codigo_usina: int | None = None, + codigo_submercado: int | None = None, + nome: str | None = None, + estagio: int | None = None, df: bool = False, - ) -> Optional[Union[TG, List[TG], pd.DataFrame]]: + ) -> TG | list[TG] | pd.DataFrame | None: """ Obtém um registro que define uma usina termelétrica existente no estudo descrito pelo :class:`Dadgnl`. @@ -94,10 +92,10 @@ def tg( def gs( self, - mes: Optional[int] = None, - semanas: Optional[int] = None, + mes: int | None = None, + semanas: int | None = None, df: bool = False, - ) -> Optional[Union[GS, List[GS], pd.DataFrame]]: + ) -> GS | list[GS] | pd.DataFrame | None: """ Obtém um registro que define o número de semanas em cada mês de estudo no :class:`Dadgnl`. @@ -117,11 +115,11 @@ def gs( def nl( self, - codigo_usina: Optional[int] = None, - codigo_submercado: Optional[int] = None, - lag: Optional[int] = None, + codigo_usina: int | None = None, + codigo_submercado: int | None = None, + lag: int | None = None, df: bool = False, - ) -> Optional[Union[NL, List[NL], pd.DataFrame]]: + ) -> NL | list[NL] | pd.DataFrame | None: """ Obtém um registro que define o número de lags para o despacho de uma UTE. @@ -149,12 +147,12 @@ def nl( def gl( self, - codigo_usina: Optional[int] = None, - codigo_submercado: Optional[int] = None, - estagio: Optional[int] = None, - data_inicio: Optional[str] = None, + codigo_usina: int | None = None, + codigo_submercado: int | None = None, + estagio: int | None = None, + data_inicio: str | None = None, df: bool = False, - ) -> Optional[Union[GL, List[GL], pd.DataFrame]]: + ) -> GL | list[GL] | pd.DataFrame | None: """ Obtém um registro que define o despacho por patamar e a duração dos patamares para uma UTE GNL. diff --git a/idecomp/decomp/dec_avl_evap.py b/idecomp/decomp/dec_avl_evap.py index e133a6d..d1ba041 100644 --- a/idecomp/decomp/dec_avl_evap.py +++ b/idecomp/decomp/dec_avl_evap.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_avl_evap import TabelaAvlEvap +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_avl_evap import TabelaAvlEvap class DecAvlEvap(ArquivoCSV): @@ -14,7 +13,7 @@ class DecAvlEvap(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaAvlEvap] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_cortes_evap.py b/idecomp/decomp/dec_cortes_evap.py index 1a439c5..a914f81 100644 --- a/idecomp/decomp/dec_cortes_evap.py +++ b/idecomp/decomp/dec_cortes_evap.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_cortes_evap import TabelaCortesEvap +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_cortes_evap import TabelaCortesEvap class DecCortesEvap(ArquivoCSV): @@ -14,7 +13,7 @@ class DecCortesEvap(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaCortesEvap] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_desvfpha.py b/idecomp/decomp/dec_desvfpha.py index 53b1a53..a7b930f 100644 --- a/idecomp/decomp/dec_desvfpha.py +++ b/idecomp/decomp/dec_desvfpha.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_desvfpha import TabelaDesvFpha +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_desvfpha import TabelaDesvFpha class DecDesvFpha(ArquivoCSV): @@ -14,7 +13,7 @@ class DecDesvFpha(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaDesvFpha] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_eco_cotajus.py b/idecomp/decomp/dec_eco_cotajus.py index 8789530..4bc15af 100644 --- a/idecomp/decomp/dec_eco_cotajus.py +++ b/idecomp/decomp/dec_eco_cotajus.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_eco_cotajus import TabelaEcoCotajus +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_eco_cotajus import TabelaEcoCotajus class DecEcoCotajus(ArquivoCSV): @@ -15,7 +14,7 @@ class DecEcoCotajus(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaEcoCotajus] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_eco_discr.py b/idecomp/decomp/dec_eco_discr.py index f07c6fa..f130f9a 100644 --- a/idecomp/decomp/dec_eco_discr.py +++ b/idecomp/decomp/dec_eco_discr.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_eco_discr import TabelaEcoDiscr +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_eco_discr import TabelaEcoDiscr class DecEcoDiscr(ArquivoCSV): @@ -15,7 +14,7 @@ class DecEcoDiscr(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaEcoDiscr] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_eco_evap.py b/idecomp/decomp/dec_eco_evap.py index c0940d2..165a5c4 100644 --- a/idecomp/decomp/dec_eco_evap.py +++ b/idecomp/decomp/dec_eco_evap.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_eco_evap import TabelaEcoEvap +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_eco_evap import TabelaEcoEvap class DecEcoEvap(ArquivoCSV): @@ -15,7 +14,7 @@ class DecEcoEvap(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaEcoEvap] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_eco_qlat.py b/idecomp/decomp/dec_eco_qlat.py index 7c73c1e..4334638 100644 --- a/idecomp/decomp/dec_eco_qlat.py +++ b/idecomp/decomp/dec_eco_qlat.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_eco_qlat import TabelaEcoQlat +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_eco_qlat import TabelaEcoQlat class DecEcoQlat(ArquivoCSV): @@ -15,7 +14,7 @@ class DecEcoQlat(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaEcoQlat] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_estatevap.py b/idecomp/decomp/dec_estatevap.py index f86624a..651bad2 100644 --- a/idecomp/decomp/dec_estatevap.py +++ b/idecomp/decomp/dec_estatevap.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_estatevap import TabelaEstatEvap +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_estatevap import TabelaEstatEvap class DecEstatEvap(ArquivoCSV): @@ -15,7 +14,7 @@ class DecEstatEvap(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaEstatEvap] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_estatfpha.py b/idecomp/decomp/dec_estatfpha.py index 9ca6ff8..5a5ecad 100644 --- a/idecomp/decomp/dec_estatfpha.py +++ b/idecomp/decomp/dec_estatfpha.py @@ -1,10 +1,12 @@ +from typing import TypeVar + +import pandas as pd # type: ignore +from cfinterface.files.blockfile import BlockFile + +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_estatfpha import ( BlocoDesvios, ) -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from cfinterface.files.blockfile import BlockFile -from typing import Optional, Type, TypeVar -import pandas as pd # type: ignore class DecEstatFpha(BlockFile): @@ -20,7 +22,7 @@ class DecEstatFpha(BlockFile): ENCODING = "iso-8859-1" T = TypeVar("T") - def _bloco_por_tipo(self, bloco: Type[T], indice: int) -> Optional[T]: + def _bloco_por_tipo(self, bloco: type[T], indice: int) -> T | None: """ Obtém um gerador de blocos de um tipo, se houver algum no arquivo. @@ -33,15 +35,13 @@ def _bloco_por_tipo(self, bloco: Type[T], indice: int) -> Optional[T]: """ try: return next( - b - for i, b in enumerate(self.data.of_type(bloco)) - if i == indice + b for i, b in enumerate(self.data.of_type(bloco)) if i == indice ) except StopIteration: return None @property - def versao(self) -> Optional[str]: + def versao(self) -> str | None: """ A versão do modelo utilizada para executar o caso. diff --git a/idecomp/decomp/dec_fcf_cortes.py b/idecomp/decomp/dec_fcf_cortes.py index 1400d44..ab82669 100644 --- a/idecomp/decomp/dec_fcf_cortes.py +++ b/idecomp/decomp/dec_fcf_cortes.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_fcf_cortes import TabelaFcfCortes +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_fcf_cortes import TabelaFcfCortes class DecFcfCortes(ArquivoCSV): @@ -14,7 +13,7 @@ class DecFcfCortes(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaFcfCortes] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_evap.py b/idecomp/decomp/dec_oper_evap.py index c1e5f22..fc58415 100644 --- a/idecomp/decomp/dec_oper_evap.py +++ b/idecomp/decomp/dec_oper_evap.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_evap import ( - TabelaOperEvapv31, TabelaOperEvap, + TabelaOperEvapv31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperEvap(ArquivoCSV): """ @@ -21,7 +20,7 @@ class DecOperEvap(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_gnl.py b/idecomp/decomp/dec_oper_gnl.py index e7f99ce..8faccfc 100644 --- a/idecomp/decomp/dec_oper_gnl.py +++ b/idecomp/decomp/dec_oper_gnl.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_oper_gnl import TabelaOperGnl, TabelaOperGnlv31 +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_oper_gnl import TabelaOperGnl, TabelaOperGnlv31 class DecOperGnl(ArquivoCSV): @@ -19,7 +18,7 @@ class DecOperGnl(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_interc.py b/idecomp/decomp/dec_oper_interc.py index 918e305..2350cf1 100644 --- a/idecomp/decomp/dec_oper_interc.py +++ b/idecomp/decomp/dec_oper_interc.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_interc import ( TabelaOperInterc, TabelaOperIntercv31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperInterc(ArquivoCSV): """ @@ -21,7 +20,7 @@ class DecOperInterc(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_ree.py b/idecomp/decomp/dec_oper_ree.py index 65fb909..ff32c01 100644 --- a/idecomp/decomp/dec_oper_ree.py +++ b/idecomp/decomp/dec_oper_ree.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.dec_oper_ree import TabelaOperRee, TabelaOperReev31 +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.dec_oper_ree import TabelaOperRee, TabelaOperReev31 class DecOperRee(ArquivoCSV): @@ -18,7 +17,7 @@ class DecOperRee(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_rhesoft.py b/idecomp/decomp/dec_oper_rhesoft.py index 30d110e..eafd5a1 100644 --- a/idecomp/decomp/dec_oper_rhesoft.py +++ b/idecomp/decomp/dec_oper_rhesoft.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_rhesoft import ( - TabelaOperRheSoftv31, TabelaOperRheSoft, + TabelaOperRheSoftv31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperRheSoft(ArquivoCSV): """ @@ -22,7 +21,7 @@ class DecOperRheSoft(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_sist.py b/idecomp/decomp/dec_oper_sist.py index 889aad5..b1c2bcd 100644 --- a/idecomp/decomp/dec_oper_sist.py +++ b/idecomp/decomp/dec_oper_sist.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_sist import ( TabelaOperSist, TabelaOperSistv31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperSist(ArquivoCSV): """ @@ -21,7 +20,7 @@ class DecOperSist(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_usie.py b/idecomp/decomp/dec_oper_usie.py index 29da477..bdcc7c9 100644 --- a/idecomp/decomp/dec_oper_usie.py +++ b/idecomp/decomp/dec_oper_usie.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_usie import ( TabelaOperUsie, TabelaOperUsiev31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperUsie(ArquivoCSV): """ @@ -21,7 +20,7 @@ class DecOperUsie(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_usih.py b/idecomp/decomp/dec_oper_usih.py index 6dc3ace..a5f4b7e 100644 --- a/idecomp/decomp/dec_oper_usih.py +++ b/idecomp/decomp/dec_oper_usih.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_usih import ( - TabelaOperUsihv31, TabelaOperUsih, + TabelaOperUsihv31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperUsih(ArquivoCSV): """ @@ -21,7 +20,7 @@ class DecOperUsih(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/dec_oper_usit.py b/idecomp/decomp/dec_oper_usit.py index 064b92d..0d09b30 100644 --- a/idecomp/decomp/dec_oper_usit.py +++ b/idecomp/decomp/dec_oper_usit.py @@ -1,13 +1,12 @@ +import pandas as pd # type: ignore + +from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo from idecomp.decomp.modelos.dec_oper_usit import ( TabelaOperUsit, TabelaOperUsitv31, ) -from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore - class DecOperUsit(ArquivoCSV): """ @@ -21,7 +20,7 @@ class DecOperUsit(ArquivoCSV): } @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/decomptim.py b/idecomp/decomp/decomptim.py index d9d4af9..889961e 100644 --- a/idecomp/decomp/decomptim.py +++ b/idecomp/decomp/decomptim.py @@ -1,8 +1,9 @@ -from idecomp.decomp.modelos.decomptim import BlocoTemposEtapas +from typing import Any, TypeVar +import pandas as pd # type: ignore[import-untyped] from cfinterface.files.blockfile import BlockFile -from typing import TypeVar, Optional -import pandas as pd # type: ignore + +from idecomp.decomp.modelos.decomptim import BlocoTemposEtapas class Decomptim(BlockFile): @@ -21,11 +22,11 @@ class Decomptim(BlockFile): BlocoTemposEtapas, ] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) @property - def tempos_etapas(self) -> Optional[pd.DataFrame]: + def tempos_etapas(self) -> pd.DataFrame | None: """ Obtém a tabela dos tempos de execução do DECOMP existente no :class:`Decomptim` diff --git a/idecomp/decomp/eco_fpha.py b/idecomp/decomp/eco_fpha.py index ccd5e39..6e28271 100644 --- a/idecomp/decomp/eco_fpha.py +++ b/idecomp/decomp/eco_fpha.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo -from idecomp.decomp.modelos.eco_fpha import TabelaEcoFpha +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModelo +from idecomp.decomp.modelos.eco_fpha import TabelaEcoFpha class EcoFpha(ArquivoCSV): @@ -15,7 +14,7 @@ class EcoFpha(ArquivoCSV): BLOCKS = [VersaoModelo, TabelaEcoFpha] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/fcfnw.py b/idecomp/decomp/fcfnw.py index 8e70442..cf66a59 100644 --- a/idecomp/decomp/fcfnw.py +++ b/idecomp/decomp/fcfnw.py @@ -1,8 +1,9 @@ -from idecomp.decomp.modelos.fcfnw import BlocoCortesFCF +from typing import TypeVar -from cfinterface.files.blockfile import BlockFile -from typing import TypeVar, Optional import pandas as pd # type: ignore +from cfinterface.files.blockfile import BlockFile + +from idecomp.decomp.modelos.fcfnw import BlocoCortesFCF class Fcfnw(BlockFile): @@ -20,7 +21,7 @@ class Fcfnw(BlockFile): BLOCKS = [BlocoCortesFCF] @property - def cortes(self) -> Optional[pd.DataFrame]: + def cortes(self) -> pd.DataFrame | None: """ Obtém a tabela de cortes do NEWAVE existente no :class:`Fcfnw`. diff --git a/idecomp/decomp/hidr.py b/idecomp/decomp/hidr.py index c37edee..7ad7e78 100644 --- a/idecomp/decomp/hidr.py +++ b/idecomp/decomp/hidr.py @@ -1,10 +1,10 @@ -from cfinterface.files.registerfile import RegisterFile -from idecomp.decomp.modelos.hidr import RegistroUHEHidr -from idecomp.config import MESES_ABREV -import pandas as pd # type: ignore +from typing import IO, Any, TypeVar +import pandas as pd # type: ignore[import-untyped] +from cfinterface.files.registerfile import RegisterFile -from typing import TypeVar, List, Optional, Union, IO +from idecomp.config import MESES_ABREV +from idecomp.decomp.modelos.hidr import RegistroUHEHidr class Hidr(RegisterFile): @@ -18,15 +18,15 @@ class Hidr(RegisterFile): REGISTERS = [RegistroUHEHidr] STORAGE = "BINARY" - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) - self.__df: Optional[pd.DataFrame] = None + self.__df: pd.DataFrame | None = None - def write(self, to: Union[str, IO], *args, **kwargs): + def write(self, to: str | IO[Any], *args: Any, **kwargs: Any) -> None: self.__atualiza_registros() super().write(to, *args, **kwargs) - def __monta_df_de_registros(self) -> Optional[pd.DataFrame]: + def __monta_df_de_registros(self) -> pd.DataFrame | None: registros = self.data.get_registers_of_type(RegistroUHEHidr) if registros is None: return None @@ -160,8 +160,8 @@ def __monta_df_de_registros(self) -> Optional[pd.DataFrame]: ) return df - def __atualiza_registros(self): - registros: List[RegistroUHEHidr] = [r for r in self.data][1:] + def __atualiza_registros(self) -> None: + registros: list[RegistroUHEHidr] = [r for r in self.data][1:] # type: ignore[assignment] for (_, linha), r in zip(self.cadastro.iterrows(), registros): r.nome = linha["nome_usina"] r.posto = linha["posto"] @@ -279,5 +279,5 @@ def cadastro(self) -> pd.DataFrame: return self.__df @cadastro.setter - def cadastro(self, df: pd.DataFrame): + def cadastro(self, df: pd.DataFrame) -> None: self.__df = df diff --git a/idecomp/decomp/inviabunic.py b/idecomp/decomp/inviabunic.py index 858988a..62922b9 100644 --- a/idecomp/decomp/inviabunic.py +++ b/idecomp/decomp/inviabunic.py @@ -1,9 +1,12 @@ -from idecomp.decomp.modelos.inviabunic import BlocoInviabilidadesIteracoes -from idecomp.decomp.modelos.inviabunic import BlocoInviabilidadesSimFinal +from typing import Any, TypeVar +import pandas as pd # type: ignore[import-untyped] from cfinterface.files.blockfile import BlockFile -from typing import TypeVar, Optional -import pandas as pd # type: ignore + +from idecomp.decomp.modelos.inviabunic import ( + BlocoInviabilidadesIteracoes, + BlocoInviabilidadesSimFinal, +) class InviabUnic(BlockFile): @@ -22,11 +25,11 @@ class InviabUnic(BlockFile): BlocoInviabilidadesSimFinal, ] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) @property - def inviabilidades_iteracoes(self) -> Optional[pd.DataFrame]: + def inviabilidades_iteracoes(self) -> pd.DataFrame | None: """ Tabela das inviabilidades visitadas pelo modelo durante as iterações. As colunas são: @@ -48,7 +51,7 @@ def inviabilidades_iteracoes(self) -> Optional[pd.DataFrame]: return None @property - def inviabilidades_simulacao_final(self) -> Optional[pd.DataFrame]: + def inviabilidades_simulacao_final(self) -> pd.DataFrame | None: """ Tabela das inviabilidades visitadas pelo modelo durante a simulação final. As colunas são: diff --git a/idecomp/decomp/mapcut.py b/idecomp/decomp/mapcut.py index 7cd0729..e27218d 100644 --- a/idecomp/decomp/mapcut.py +++ b/idecomp/decomp/mapcut.py @@ -1,11 +1,10 @@ -from cfinterface.files.sectionfile import SectionFile -from idecomp.decomp.modelos.mapcut import SecaoDadosMapcut -import pandas as pd # type: ignore -from typing import List from datetime import datetime +from typing import Any, TypeVar +import pandas as pd # type: ignore[import-untyped] +from cfinterface.files.sectionfile import SectionFile -from typing import TypeVar, Optional +from idecomp.decomp.modelos.mapcut import SecaoDadosMapcut class Mapcut(SectionFile): @@ -19,15 +18,15 @@ class Mapcut(SectionFile): SECTIONS = [SecaoDadosMapcut] STORAGE = "BINARY" - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) - def __obtem_dados(self) -> Optional[SecaoDadosMapcut]: + def __obtem_dados(self) -> SecaoDadosMapcut | None: s = self.data.get_sections_of_type(SecaoDadosMapcut) return s if not isinstance(s, list) else None @property - def numero_iteracoes(self) -> Optional[int]: + def numero_iteracoes(self) -> int | None: """ O número de iterações. @@ -40,7 +39,7 @@ def numero_iteracoes(self) -> Optional[int]: return None @property - def numero_cortes(self) -> Optional[int]: + def numero_cortes(self) -> int | None: """ O número de cortes de Benders. @@ -53,7 +52,7 @@ def numero_cortes(self) -> Optional[int]: return None @property - def numero_submercados(self) -> Optional[int]: + def numero_submercados(self) -> int | None: """ O número de submercados. @@ -66,7 +65,7 @@ def numero_submercados(self) -> Optional[int]: return None @property - def numero_uhes(self) -> Optional[int]: + def numero_uhes(self) -> int | None: """ O número de usinas hidrelétricas. @@ -79,7 +78,7 @@ def numero_uhes(self) -> Optional[int]: return None @property - def numero_cenarios(self) -> Optional[int]: + def numero_cenarios(self) -> int | None: """ O número de cenarios. @@ -92,7 +91,7 @@ def numero_cenarios(self) -> Optional[int]: return None @property - def registro_ultimo_corte_no(self) -> Optional[pd.DataFrame]: + def registro_ultimo_corte_no(self) -> pd.DataFrame | None: """ Retorna os dados dos índices do último registro de cortes para cada estágio, para leitura do @@ -111,7 +110,7 @@ def registro_ultimo_corte_no(self) -> Optional[pd.DataFrame]: return None @property - def tamanho_corte(self) -> Optional[int]: + def tamanho_corte(self) -> int | None: """ O tamanho do corte (tamanho do registro no arquivo cortdeco). @@ -125,7 +124,7 @@ def tamanho_corte(self) -> Optional[int]: return None @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início do estudo. @@ -138,7 +137,7 @@ def data_inicio(self) -> Optional[datetime]: return None @property - def codigos_uhes(self) -> Optional[List[int]]: + def codigos_uhes(self) -> list[int] | None: """ Os códigos das usinas hidráulicas. @@ -151,7 +150,7 @@ def codigos_uhes(self) -> Optional[List[int]]: return None @property - def codigos_uhes_jusante(self) -> Optional[List[int]]: + def codigos_uhes_jusante(self) -> list[int] | None: """ Os códigos das usinas hidráulicas a jusante. @@ -164,7 +163,7 @@ def codigos_uhes_jusante(self) -> Optional[List[int]]: return None @property - def indice_no_arvore(self) -> Optional[list]: + def indice_no_arvore(self) -> list[int] | None: """ Os índice do nó pai na árvore de cenários para cada nó da árvore. @@ -178,7 +177,7 @@ def indice_no_arvore(self) -> Optional[list]: return None @property - def numero_estagios(self) -> Optional[int]: + def numero_estagios(self) -> int | None: """ O número de estágios. @@ -191,7 +190,7 @@ def numero_estagios(self) -> Optional[int]: return None @property - def numero_semanas(self) -> Optional[int]: + def numero_semanas(self) -> int | None: """ O número de semanas. @@ -204,7 +203,7 @@ def numero_semanas(self) -> Optional[int]: return None @property - def numero_uhes_tempo_viagem(self) -> Optional[int]: + def numero_uhes_tempo_viagem(self) -> int | None: """ O número de usinas hidraúlicas com tempo de viagem da água. @@ -218,7 +217,7 @@ def numero_uhes_tempo_viagem(self) -> Optional[int]: return None @property - def maximo_lag_tempo_viagem(self) -> Optional[int]: + def maximo_lag_tempo_viagem(self) -> int | None: """ O máximo lag de tempo de viagem. @@ -231,7 +230,7 @@ def maximo_lag_tempo_viagem(self) -> Optional[int]: return None @property - def indice_primeiro_no_estagio(self) -> Optional[list]: + def indice_primeiro_no_estagio(self) -> list[int] | None: """ O índice do primeiro nó de cada estágio. @@ -244,7 +243,7 @@ def indice_primeiro_no_estagio(self) -> Optional[list]: return None @property - def patamares_por_estagio(self) -> Optional[list]: + def patamares_por_estagio(self) -> list[int] | None: """ O número de patamares de carga por estágio. @@ -257,7 +256,7 @@ def patamares_por_estagio(self) -> Optional[list]: return None @property - def lag_tempo_viagem_por_uhe(self) -> Optional[list]: + def lag_tempo_viagem_por_uhe(self) -> list[Any] | None: """ O lag (em estágios) de uma usina para cada período. @@ -270,7 +269,7 @@ def lag_tempo_viagem_por_uhe(self) -> Optional[list]: return None @property - def codigos_uhes_tempo_viagem(self) -> Optional[List[int]]: + def codigos_uhes_tempo_viagem(self) -> list[int] | None: """ Os códigos das usinas hidráulicas com tempo de viagem. @@ -283,7 +282,7 @@ def codigos_uhes_tempo_viagem(self) -> Optional[List[int]]: return None @property - def dados_tempo_viagem(self) -> Optional[pd.DataFrame]: + def dados_tempo_viagem(self) -> pd.DataFrame | None: """ Obtém a tabela com os dados de tempo de viagem. @@ -302,7 +301,7 @@ def dados_tempo_viagem(self) -> Optional[pd.DataFrame]: return None @property - def codigos_submercados_gnl(self) -> Optional[List[int]]: + def codigos_submercados_gnl(self) -> list[int] | None: """ Os códigos das usinas térmicas com despacho antecipado (GNL). @@ -316,7 +315,7 @@ def codigos_submercados_gnl(self) -> Optional[List[int]]: return None @property - def dados_gnl(self) -> Optional[pd.DataFrame]: + def dados_gnl(self) -> pd.DataFrame | None: """ Obtém a tabela com os dados de usinas GNL. @@ -335,7 +334,7 @@ def dados_gnl(self) -> Optional[pd.DataFrame]: return None @property - def dados_custos(self) -> Optional[pd.DataFrame]: + def dados_custos(self) -> pd.DataFrame | None: """ Obtém a tabela com os dados de custos. diff --git a/idecomp/decomp/modelos/arquivos.py b/idecomp/decomp/modelos/arquivos.py index 32b9e1d..cf2aca0 100644 --- a/idecomp/decomp/modelos/arquivos.py +++ b/idecomp/decomp/modelos/arquivos.py @@ -1,8 +1,9 @@ -from cfinterface.components.section import Section +from typing import IO, Any + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from typing import IO, List -import pandas as pd # type: ignore +from cfinterface.components.section import Section class BlocoNomesArquivos(Section): @@ -13,7 +14,9 @@ class BlocoNomesArquivos(Section): __slots__ = ["__linha"] - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__linha = Line([LiteralField(80, 0)]) @@ -32,12 +35,12 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): - def converte_tabela_em_df(): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] + def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(data={"Nome": nomes}) return df - nomes: List[str] = [] + nomes: list[str] = [] while True: linha = file.readline() if len(linha) == 0: @@ -47,7 +50,7 @@ def converte_tabela_em_df(): nomes.append(dados[0]) # Override - def write(self, file: IO, *args, **kwargs): + def write(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] if not isinstance(self.data, pd.DataFrame): raise ValueError("Dados do arquivos não foram lidos") for _, linha in self.data.iterrows(): diff --git a/idecomp/decomp/modelos/arquivoscsv/arquivocsv.py b/idecomp/decomp/modelos/arquivoscsv/arquivocsv.py index e445ca3..c456efa 100644 --- a/idecomp/decomp/modelos/arquivoscsv/arquivocsv.py +++ b/idecomp/decomp/modelos/arquivoscsv/arquivocsv.py @@ -1,13 +1,14 @@ +from typing import TypeVar + +import pandas as pd # type: ignore +from cfinterface.components.block import Block +from cfinterface.files.blockfile import BlockFile + +from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV, TabelaCSVLibs from idecomp.decomp.modelos.blocos.versaomodelo import ( VersaoModelo, VersaoModeloLibs, ) -from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV, TabelaCSVLibs - -from cfinterface.components.block import Block -from cfinterface.files.blockfile import BlockFile -import pandas as pd # type: ignore -from typing import List, Type, TypeVar, Optional class ArquivoCSV(BlockFile): @@ -23,12 +24,12 @@ class ArquivoCSV(BlockFile): implementado para cada arquivo específico a ser lido. """ - BLOCKS: List[Type[Block]] = [VersaoModelo] + BLOCKS: list[type[Block]] = [VersaoModelo] ENCODING = "iso-8859-1" T = TypeVar("T") - def _bloco_por_tipo(self, bloco: Type[T], indice: int) -> Optional[T]: + def _bloco_por_tipo(self, bloco: type[T], indice: int) -> T | None: """ Obtém um gerador de blocos de um tipo, se houver algum no arquivo. @@ -41,15 +42,13 @@ def _bloco_por_tipo(self, bloco: Type[T], indice: int) -> Optional[T]: """ try: return next( - b - for i, b in enumerate(self.data.of_type(bloco)) - if i == indice + b for i, b in enumerate(self.data.of_type(bloco)) if i == indice ) except StopIteration: return None @property - def versao(self) -> Optional[str]: + def versao(self) -> str | None: """ A versão do modelo utilizada para executar o caso. @@ -64,7 +63,7 @@ def versao(self) -> Optional[str]: return b_libs.data return None - def _tabela(self) -> Optional[pd.DataFrame]: + def _tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/modelos/avl_cortesfpha_dec.py b/idecomp/decomp/modelos/avl_cortesfpha_dec.py index 7d4df9c..bb9995f 100644 --- a/idecomp/decomp/modelos/avl_cortesfpha_dec.py +++ b/idecomp/decomp/modelos/avl_cortesfpha_dec.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/blocos/tabelacsv.py b/idecomp/decomp/modelos/blocos/tabelacsv.py index b0f7b9f..cec2e8c 100644 --- a/idecomp/decomp/modelos/blocos/tabelacsv.py +++ b/idecomp/decomp/modelos/blocos/tabelacsv.py @@ -1,9 +1,9 @@ +from typing import IO, Any + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block from cfinterface.components.line import Line -from typing import IO, List, Dict -import pandas as pd # type: ignore - class TabelaCSV(Block): """ @@ -15,10 +15,10 @@ class TabelaCSV(Block): BEGIN_PATTERN = "-----;------;" LINE_MODEL = Line([]) - COLUMN_NAMES: List[str] = [] + COLUMN_NAMES: list[str] = [] END_PATTERN = "" - def _monta_df(self, dados: dict) -> pd.DataFrame: + def _monta_df(self, dados: dict[str, Any]) -> pd.DataFrame: return pd.DataFrame(data=dados, columns=self.__class__.COLUMN_NAMES) def __eq__(self, o: object) -> bool: @@ -32,7 +32,7 @@ def __eq__(self, o: object) -> bool: else: return self.data.equals(o.data) - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] if len(self.__class__.LINE_MODEL.fields) != len( self.__class__.COLUMN_NAMES ): @@ -51,7 +51,9 @@ def read(self, file: IO, *args, **kwargs): elif len(linha) < 3: return # Lê a tabela - dados: Dict[str, List] = {c: [] for c in self.__class__.COLUMN_NAMES} + dados: dict[str, list[Any]] = { + c: [] for c in self.__class__.COLUMN_NAMES + } while True: linha = file.readline() if len(linha) < 3: @@ -72,10 +74,10 @@ class TabelaCSVLibs(Block): BEGIN_PATTERN = "&IIIIIII;IIIIIII;" LINE_MODEL = Line([]) - COLUMN_NAMES: List[str] = [] + COLUMN_NAMES: list[str] = [] END_PATTERN = "" - def _monta_df(self, dados: dict) -> pd.DataFrame: + def _monta_df(self, dados: dict[str, Any]) -> pd.DataFrame: return pd.DataFrame(data=dados, columns=self.__class__.COLUMN_NAMES) def __eq__(self, o: object) -> bool: @@ -89,7 +91,7 @@ def __eq__(self, o: object) -> bool: else: return self.data.equals(o.data) - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] if len(self.__class__.LINE_MODEL.fields) != len( self.__class__.COLUMN_NAMES ): @@ -102,7 +104,9 @@ def read(self, file: IO, *args, **kwargs): # Lê a tabela linha = file.readline() - dados: Dict[str, List] = {c: [] for c in self.__class__.COLUMN_NAMES} + dados: dict[str, list[Any]] = { + c: [] for c in self.__class__.COLUMN_NAMES + } while True: linha = file.readline() if len(linha) < 3: diff --git a/idecomp/decomp/modelos/blocos/versaomodelo.py b/idecomp/decomp/modelos/blocos/versaomodelo.py index c66988d..8fa1ccc 100644 --- a/idecomp/decomp/modelos/blocos/versaomodelo.py +++ b/idecomp/decomp/modelos/blocos/versaomodelo.py @@ -1,6 +1,6 @@ -from cfinterface.components.block import Block +from typing import IO, Any -from typing import IO +from cfinterface.components.block import Block class VersaoModelo(Block): @@ -22,9 +22,10 @@ def __eq__(self, o: object) -> bool: return False return self.data == o.data - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[str], *args: Any, **kwargs: Any) -> bool: linha = file.readline() self.data = linha.split("Versao")[1].strip().split("-")[0].strip() + return True class VersaoModeloLibs(Block): @@ -46,6 +47,7 @@ def __eq__(self, o: object) -> bool: return False return self.data == o.data - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[str], *args: Any, **kwargs: Any) -> bool: linha = file.readline() self.data = linha.split("Versão:")[1].strip() + return True diff --git a/idecomp/decomp/modelos/caso.py b/idecomp/decomp/modelos/caso.py index 8176fe5..5e56674 100644 --- a/idecomp/decomp/modelos/caso.py +++ b/idecomp/decomp/modelos/caso.py @@ -1,7 +1,8 @@ -from cfinterface.components.section import Section +from typing import IO, Any + from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from typing import IO +from cfinterface.components.section import Section class NomeCaso(Section): @@ -12,7 +13,9 @@ class NomeCaso(Section): __slots__ = ["__linha"] - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__linha = Line([LiteralField(80, 0)]) @@ -31,9 +34,9 @@ def __eq__(self, o: object) -> bool: return self.data == bloco.data # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] self.data = self.__linha.read(file.readline())[0] # Override - def write(self, file: IO, *args, **kwargs): + def write(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] file.write(self.__linha.write([self.data])) diff --git a/idecomp/decomp/modelos/cortdeco.py b/idecomp/decomp/modelos/cortdeco.py index ff6e164..d5dfd33 100644 --- a/idecomp/decomp/modelos/cortdeco.py +++ b/idecomp/decomp/modelos/cortdeco.py @@ -1,7 +1,8 @@ +from typing import IO, Any + +import numpy as np +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.section import Section -from typing import IO, List -import numpy as np # type: ignore -import pandas as pd # type: ignore class SecaoDadosCortdeco(Section): @@ -44,7 +45,7 @@ def __eq__(self, o: object) -> bool: else: return self.data.equals(bloco.data) - def __inicializa_variaveis(self, numero_total_cortes): + def __inicializa_variaveis(self, numero_total_cortes: int) -> None: self.__numero_coeficientes_rhs = 1 self.__numero_coeficientes_varm = int(len(self.__codigos_uhes)) @@ -77,8 +78,12 @@ def __inicializa_variaveis(self, numero_total_cortes): ) def __le_e_atribui_int( - self, file: IO, destino: np.ndarray, tamanho: int, indice: int - ): + self, + file: IO[Any], + destino: np.ndarray, + tamanho: int, + indice: int, + ) -> None: destino[indice, :] = np.frombuffer( file.read(tamanho * 4), dtype=np.int32, @@ -86,8 +91,8 @@ def __le_e_atribui_int( ) def __le_e_atribui_float( - self, file: IO, destino: np.ndarray, tamanho: int, indice: int - ): + self, file: IO[Any], destino: np.ndarray, tamanho: int, indice: int + ) -> None: destino[indice, :] = np.frombuffer( file.read(tamanho * 8), dtype=np.float64, @@ -96,7 +101,7 @@ def __le_e_atribui_float( def __le_registro( self, - file: IO, + file: IO[Any], offset: int, indice: int, ) -> int: @@ -109,7 +114,9 @@ def __le_registro( self.__tabela_int[indice, 0] = indice return indice_proximo_corte - def __converte_array_em_dataframe(self, no: int, estagio: int): + def __converte_array_em_dataframe( + self, no: int, estagio: int + ) -> pd.DataFrame: df_int = pd.DataFrame( self.__tabela_int[:, 0], columns=[ @@ -140,14 +147,14 @@ def __converte_array_em_dataframe(self, no: int, estagio: int): ) df = pd.concat([df_int, df_float], axis=1) - return df.sort_values( - by=["estagio", "no", "indice_corte"] - ).reset_index(drop=True) + return df.sort_values(by=["estagio", "no", "indice_corte"]).reset_index( + drop=True + ) def __le_arquivo( self, - file: IO, - ): + file: IO[Any], + ) -> None: df_cortes_completo = pd.DataFrame() # Realiza leitura para cada no que constroi corte for _, row in self.__registro_ultimo_corte_no.iterrows(): @@ -254,8 +261,8 @@ def __identifica_indices_registros( return df_cortes.sort_values(by=["indice_registro"]) def __atualiza_registros( - self, file: IO, df_registro_ultimo_corte_no: pd.DataFrame - ): + self, file: IO[Any], df_registro_ultimo_corte_no: pd.DataFrame + ) -> None: df_cortes = self.__identifica_indices_registros( df_registro_ultimo_corte_no ) @@ -280,21 +287,21 @@ def __atualiza_registros( ).tobytes() ) - def read( + def read( # type: ignore[override] self, - file: IO, + file: IO[Any], tamanho_registro: int, registro_ultimo_corte_no: pd.DataFrame, numero_total_cortes: int, numero_patamares_carga: int, numero_estagios: int, - codigos_uhes: List[int], - codigos_uhes_tempo_viagem: List[int], - codigos_submercados: List[int], + codigos_uhes: list[int], + codigos_uhes_tempo_viagem: list[int], + codigos_submercados: list[int], lag_maximo_tempo_viagem: int, - *args, - **kwargs, - ): + *args: Any, + **kwargs: Any, + ) -> None: # Atribui variáveis locais self.__tamanho_registro = tamanho_registro self.__numero_total_cortes = numero_total_cortes @@ -313,11 +320,11 @@ def read( # Ignore bytes restantes _ = file.read() - def write( + def write( # type: ignore[override] self, - file: IO, - *args, - **kwargs, - ): + file: IO[Any], + *args: Any, + **kwargs: Any, + ) -> None: df_registro_ultimo_corte_no = kwargs["df_registro_ultimo_corte_no"] self.__atualiza_registros(file, df_registro_ultimo_corte_no) diff --git a/idecomp/decomp/modelos/custos.py b/idecomp/decomp/modelos/custos.py index 0dd0fbf..9da65db 100644 --- a/idecomp/decomp/modelos/custos.py +++ b/idecomp/decomp/modelos/custos.py @@ -1,13 +1,14 @@ # Imports do próprio módulo # Imports de módulos externos +from typing import IO, Any + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField -import pandas as pd # type: ignore -from typing import IO, List class BlocoRelatorioCustos(Block): @@ -24,7 +25,9 @@ class BlocoRelatorioCustos(Block): BEGIN_PATTERN = r" RELATORIO DAS VARIAVEIS DUAIS NO ESTAGIO" END_PATTERN = "X----X-" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__scenario_line = Line([IntegerField(2, 44), IntegerField(4, 54)]) self.__dual_variables_line = Line( @@ -59,7 +62,7 @@ def __eq__(self, o: object) -> bool: ] ) - def __read_bloco_variaveis_duais(self, file: IO): + def __read_bloco_variaveis_duais(self, file: IO[Any]) -> None: def converte_tabela_para_df() -> pd.DataFrame: cols = [ "usina", @@ -83,8 +86,8 @@ def converte_tabela_para_df() -> pd.DataFrame: file.readline() # Variáveis auxiliares - usinas: List[str] = [] - pihs: List[float] = [] + usinas: list[str] = [] + pihs: list[float] = [] while True: linha: str = file.readline() # Verifica se acabou @@ -95,15 +98,13 @@ def converte_tabela_para_df() -> pd.DataFrame: usinas.append(dados[0]) pihs.append(dados[1]) - def __read_bloco_restricoes_fcf(self, file: IO): + def __read_bloco_restricoes_fcf(self, file: IO[Any]) -> None: def converte_tabela_para_df() -> pd.DataFrame: cols = [ "indice_corte", "parcela_pi", ] - df = pd.DataFrame( - data={"indice_corte": indices, "parcela_pi": pis} - ) + df = pd.DataFrame(data={"indice_corte": indices, "parcela_pi": pis}) cols_adic = [ "estagio", "cenario", @@ -121,8 +122,8 @@ def converte_tabela_para_df() -> pd.DataFrame: file.readline() # Variáveis auxiliares - indices: List[int] = [] - pis: List[float] = [] + indices: list[int] = [] + pis: list[float] = [] while True: linha: str = file.readline() # Verifica se acabou @@ -134,14 +135,14 @@ def converte_tabela_para_df() -> pd.DataFrame: pis.append(dados[1]) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] str_bloco_variaveis_duais = "Aproveitamento Bal.Hidr." str_bloco_restricoes_fcf = "Restricoes da FCF" # Extrai os dados de estágio e cenário self.dados_cenario = self.__scenario_line.read(file.readline()) - self.data: List[pd.DataFrame] = [] + self.data: list[pd.DataFrame] = [] while True: linha = file.readline() if str_bloco_variaveis_duais in linha: diff --git a/idecomp/decomp/modelos/dadger.py b/idecomp/decomp/modelos/dadger.py index 8fe92a9..2df5801 100644 --- a/idecomp/decomp/modelos/dadger.py +++ b/idecomp/decomp/modelos/dadger.py @@ -1,11 +1,11 @@ -from typing import IO, List, Optional +from typing import IO, Any from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField from cfinterface.components.register import Register -from numpy import abs # type: ignore +from numpy import abs class TE(Register): @@ -13,14 +13,14 @@ class TE(Register): Registro que contém a usina modificada. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "TE " IDENTIFIER_DIGITS = 4 LINE = Line([LiteralField(75, 4)]) @property - def titulo(self) -> Optional[str]: + def titulo(self) -> str | None: """ O único conteúdo do registro (título do estudo). @@ -30,7 +30,7 @@ def titulo(self) -> Optional[str]: return self.data[0] @titulo.setter - def titulo(self, t: str): + def titulo(self, t: str) -> None: self.data[0] = t @@ -39,14 +39,14 @@ class SB(Register): Registro que contém o cadastro dos submercados. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "SB " IDENTIFIER_DIGITS = 4 LINE = Line([IntegerField(2, 4), LiteralField(2, 9)]) @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código de cadastro do submercado. @@ -56,11 +56,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[0] @codigo_submercado.setter - def codigo_submercado(self, cod: int): + def codigo_submercado(self, cod: int) -> None: self.data[0] = cod @property - def nome_submercado(self) -> Optional[str]: + def nome_submercado(self) -> str | None: """ O nome de cadastro do submercado. @@ -70,7 +70,7 @@ def nome_submercado(self) -> Optional[str]: return self.data[1] @nome_submercado.setter - def nome_submercado(self, n: str): + def nome_submercado(self, n: str) -> None: self.data[1] = n @@ -80,7 +80,7 @@ class UH(Register): iniciais no estudo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "UH " IDENTIFIER_DIGITS = 4 @@ -100,7 +100,7 @@ class UH(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código de cadastro da UHE. @@ -110,11 +110,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, cod: int): + def codigo_usina(self, cod: int) -> None: self.data[0] = cod @property - def codigo_ree(self) -> Optional[int]: + def codigo_ree(self) -> int | None: """ O REE de cadastro da UHE. @@ -124,11 +124,11 @@ def codigo_ree(self) -> Optional[int]: return self.data[1] @codigo_ree.setter - def codigo_ree(self, n: int): + def codigo_ree(self, n: int) -> None: self.data[1] = n @property - def volume_inicial(self) -> Optional[float]: + def volume_inicial(self) -> float | None: """ O volume inicial da UHE para o estudo. @@ -138,11 +138,11 @@ def volume_inicial(self) -> Optional[float]: return self.data[2] @volume_inicial.setter - def volume_inicial(self, v: float): + def volume_inicial(self, v: float) -> None: self.data[2] = v @property - def vazao_defluente_minima(self) -> Optional[float]: + def vazao_defluente_minima(self) -> float | None: """ O vazão defluente mínima da usina em m3/s. @@ -152,11 +152,11 @@ def vazao_defluente_minima(self) -> Optional[float]: return self.data[3] @vazao_defluente_minima.setter - def vazao_defluente_minima(self, v: float): + def vazao_defluente_minima(self, v: float) -> None: self.data[3] = v @property - def evaporacao(self) -> Optional[int]: + def evaporacao(self) -> int | None: """ A consideração ou não de evaporação para a UHE. @@ -166,11 +166,11 @@ def evaporacao(self) -> Optional[int]: return self.data[4] @evaporacao.setter - def evaporacao(self, e: int): + def evaporacao(self, e: int) -> None: self.data[4] = e @property - def estagio_inicio_producao(self) -> Optional[int]: + def estagio_inicio_producao(self) -> int | None: """ Estágio a partir da qual a usina começa a produzir energia. @@ -180,11 +180,11 @@ def estagio_inicio_producao(self) -> Optional[int]: return self.data[5] @estagio_inicio_producao.setter - def estagio_inicio_producao(self, e: int): + def estagio_inicio_producao(self, e: int) -> None: self.data[5] = e @property - def volume_morto_inicial(self) -> Optional[float]: + def volume_morto_inicial(self) -> float | None: """ Volume morto inicial da usina. @@ -194,11 +194,11 @@ def volume_morto_inicial(self) -> Optional[float]: return self.data[6] @volume_morto_inicial.setter - def volume_morto_inicial(self, e: float): + def volume_morto_inicial(self, e: float) -> None: self.data[6] = e @property - def limite_superior_vertimento(self) -> Optional[float]: + def limite_superior_vertimento(self) -> float | None: """ O limite superior para vertimento da usina. @@ -208,11 +208,11 @@ def limite_superior_vertimento(self) -> Optional[float]: return self.data[7] @limite_superior_vertimento.setter - def limite_superior_vertimento(self, e: float): + def limite_superior_vertimento(self, e: float) -> None: self.data[7] = e @property - def balanco_hidrico_patamar(self) -> Optional[int]: + def balanco_hidrico_patamar(self) -> int | None: """ Consideração do balanço hídrico por patamar. @@ -222,11 +222,11 @@ def balanco_hidrico_patamar(self) -> Optional[int]: return self.data[8] @balanco_hidrico_patamar.setter - def balanco_hidrico_patamar(self, e: int): + def balanco_hidrico_patamar(self, e: int) -> None: self.data[8] = e @property - def configuracao_newave(self) -> Optional[str]: + def configuracao_newave(self) -> str | None: """ Sinaliza a existência da usina na configuração do NEWAVE, para compatibilização. @@ -237,7 +237,7 @@ def configuracao_newave(self) -> Optional[str]: return self.data[9] @configuracao_newave.setter - def configuracao_newave(self, e: str): + def configuracao_newave(self, e: str) -> None: self.data[9] = e @@ -248,7 +248,7 @@ class CT(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CT " IDENTIFIER_DIGITS = 4 @@ -278,10 +278,10 @@ class CT(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -292,7 +292,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código de cadastro da UTE. @@ -302,11 +302,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, codigo: int): + def codigo_usina(self, codigo: int) -> None: self.data[0] = codigo @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O submercado de cadastro da UTE. @@ -316,11 +316,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, submercado: int): + def codigo_submercado(self, submercado: int) -> None: self.data[1] = submercado @property - def nome_usina(self) -> Optional[str]: + def nome_usina(self) -> str | None: """ O nome de cadastro da UTE. @@ -330,11 +330,11 @@ def nome_usina(self) -> Optional[str]: return self.data[2] @nome_usina.setter - def nome_usina(self, nome: str): + def nome_usina(self, nome: str) -> None: self.data[2] = nome @property - def estagio(self) -> Optional[str]: + def estagio(self) -> str | None: """ O estágio associado às propriedades cadastradas. @@ -344,11 +344,11 @@ def estagio(self) -> Optional[str]: return self.data[3] @estagio.setter - def estagio(self, estagio: int): + def estagio(self, estagio: int) -> None: self.data[3] = estagio @property - def inflexibilidade(self) -> Optional[List[float]]: + def inflexibilidade(self) -> list[float] | None: """ A inflexibilidade da UTE por patamar. @@ -358,11 +358,11 @@ def inflexibilidade(self) -> Optional[List[float]]: return [v for v in self.data[4::3] if v is not None] @inflexibilidade.setter - def inflexibilidade(self, inflex: List[float]): + def inflexibilidade(self, inflex: list[float]) -> None: self.__atualiza_dados_lista(inflex, 4, 3) @property - def disponibilidade(self) -> Optional[List[float]]: + def disponibilidade(self) -> list[float] | None: """ A disponibilidade da UTE por patamar. @@ -372,11 +372,11 @@ def disponibilidade(self) -> Optional[List[float]]: return [v for v in self.data[5::3] if v is not None] @disponibilidade.setter - def disponibilidade(self, disp: List[float]): + def disponibilidade(self, disp: list[float]) -> None: self.__atualiza_dados_lista(disp, 5, 3) @property - def cvu(self) -> Optional[List[float]]: + def cvu(self) -> list[float] | None: """ O CVU da UTE por patamar. @@ -386,7 +386,7 @@ def cvu(self) -> Optional[List[float]]: return [v for v in self.data[6::3] if v is not None] @cvu.setter - def cvu(self, cvu: List[float]): + def cvu(self, cvu: list[float]) -> None: self.__atualiza_dados_lista(cvu, 6, 3) @@ -396,7 +396,7 @@ class UE(Register): (usinas elevatórias). """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "UE " IDENTIFIER_DIGITS = 4 @@ -414,7 +414,7 @@ class UE(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código de cadastro da UE. @@ -424,11 +424,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, cod: int): + def codigo_usina(self, cod: int) -> None: self.data[0] = cod @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O submercado de cadastro da UE, conforme registro SB. @@ -438,11 +438,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, n: int): + def codigo_submercado(self, n: int) -> None: self.data[1] = n @property - def nome_usina(self) -> Optional[str]: + def nome_usina(self) -> str | None: """ O nome da estação de bombeamento. @@ -452,11 +452,11 @@ def nome_usina(self) -> Optional[str]: return self.data[2] @nome_usina.setter - def nome_usina(self, v: str): + def nome_usina(self, v: str) -> None: self.data[2] = v @property - def codigo_usina_montante(self) -> Optional[int]: + def codigo_usina_montante(self) -> int | None: """ O código da UHE a montante, conforme registro UH. @@ -466,11 +466,11 @@ def codigo_usina_montante(self) -> Optional[int]: return self.data[3] @codigo_usina_montante.setter - def codigo_usina_montante(self, v: int): + def codigo_usina_montante(self, v: int) -> None: self.data[3] = v @property - def codigo_usina_jusante(self) -> Optional[int]: + def codigo_usina_jusante(self) -> int | None: """ O código da UHE a jusante, conforme registro UH. @@ -480,11 +480,11 @@ def codigo_usina_jusante(self) -> Optional[int]: return self.data[4] @codigo_usina_jusante.setter - def codigo_usina_jusante(self, e: int): + def codigo_usina_jusante(self, e: int) -> None: self.data[4] = e @property - def vazao_minima_bombeavel(self) -> Optional[float]: + def vazao_minima_bombeavel(self) -> float | None: """ A vazão mínima bombeável. @@ -494,11 +494,11 @@ def vazao_minima_bombeavel(self) -> Optional[float]: return self.data[5] @vazao_minima_bombeavel.setter - def vazao_minima_bombeavel(self, e: float): + def vazao_minima_bombeavel(self, e: float) -> None: self.data[5] = e @property - def vazao_maxima_bombeavel(self) -> Optional[float]: + def vazao_maxima_bombeavel(self) -> float | None: """ A vazão mínima bombeável. @@ -508,11 +508,11 @@ def vazao_maxima_bombeavel(self) -> Optional[float]: return self.data[6] @vazao_maxima_bombeavel.setter - def vazao_maxima_bombeavel(self, e: float): + def vazao_maxima_bombeavel(self, e: float) -> None: self.data[6] = e @property - def taxa_consumo(self) -> Optional[float]: + def taxa_consumo(self) -> float | None: """ A taxa de consumo. @@ -522,7 +522,7 @@ def taxa_consumo(self) -> Optional[float]: return self.data[7] @taxa_consumo.setter - def taxa_consumo(self, e: float): + def taxa_consumo(self, e: float) -> None: self.data[7] = e @@ -532,7 +532,7 @@ class DP(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "DP " IDENTIFIER_DIGITS = 4 @@ -556,10 +556,10 @@ class DP(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -570,7 +570,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado às durações especificadas. @@ -580,11 +580,11 @@ def estagio(self) -> Optional[int]: return self.data[0] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[0] = e @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O submercado associado às durações especificadas. @@ -594,11 +594,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, sub: int): + def codigo_submercado(self, sub: int) -> None: self.data[1] = sub @property - def numero_patamares(self) -> Optional[int]: + def numero_patamares(self) -> int | None: """ O número de patamares. @@ -608,11 +608,11 @@ def numero_patamares(self) -> Optional[int]: return self.data[2] @numero_patamares.setter - def numero_patamares(self, n: int): + def numero_patamares(self, n: int) -> None: self.data[2] = n @property - def carga(self) -> Optional[List[float]]: + def carga(self) -> list[float] | None: """ A carga em Mwmed pata cada patamar de carga @@ -622,11 +622,11 @@ def carga(self) -> Optional[List[float]]: return [v for v in self.data[3::2] if v is not None] @carga.setter - def carga(self, c: List[float]): + def carga(self, c: list[float]) -> None: self.__atualiza_dados_lista(c, 3, 2) @property - def duracao(self) -> Optional[List[float]]: + def duracao(self) -> list[float] | None: """ A duração de cada patamar de carga em horas @@ -636,7 +636,7 @@ def duracao(self) -> Optional[List[float]]: return [v for v in self.data[4::2] if v is not None] @duracao.setter - def duracao(self, d: List[float]): + def duracao(self, d: list[float]) -> None: self.__atualiza_dados_lista(d, 4, 2) @@ -646,7 +646,7 @@ class PQ(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "PQ " IDENTIFIER_DIGITS = 4 @@ -665,10 +665,10 @@ class PQ(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -679,7 +679,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def nome(self) -> Optional[str]: + def nome(self) -> str | None: """ O nome da geração. @@ -689,11 +689,11 @@ def nome(self) -> Optional[str]: return self.data[0] @nome.setter - def nome(self, nome: str): + def nome(self, nome: str) -> None: self.data[0] = nome @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O submercado associado à geração. @@ -703,11 +703,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, sub: int): + def codigo_submercado(self, sub: int) -> None: self.data[1] = sub @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado à geração. @@ -717,11 +717,11 @@ def estagio(self) -> Optional[int]: return self.data[2] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[2] = e @property - def geracao(self) -> Optional[List[float]]: + def geracao(self) -> list[float] | None: """ A geração em Mwmed para cada patamar de carga. @@ -731,7 +731,7 @@ def geracao(self) -> Optional[List[float]]: return [v for v in self.data[3:8] if v is not None] @geracao.setter - def geracao(self, c: List[float]): + def geracao(self, c: list[float]) -> None: self.__atualiza_dados_lista(c, 3, 1) @@ -741,7 +741,7 @@ class CD(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CD " IDENTIFIER_DIGITS = 4 @@ -766,10 +766,10 @@ class CD(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -780,7 +780,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_curva(self) -> Optional[int]: + def codigo_curva(self) -> int | None: """ O código da curva de déficit. @@ -790,11 +790,11 @@ def codigo_curva(self) -> Optional[int]: return self.data[0] @codigo_curva.setter - def codigo_curva(self, n: int): + def codigo_curva(self, n: int) -> None: self.data[0] = n @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado associado. @@ -804,11 +804,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, s: int): + def codigo_submercado(self, s: int) -> None: self.data[1] = s @property - def nome_curva(self) -> Optional[str]: + def nome_curva(self) -> str | None: """ O nome da curva de déficit @@ -818,11 +818,11 @@ def nome_curva(self) -> Optional[str]: return self.data[2] @nome_curva.setter - def nome_curva(self, n: str): + def nome_curva(self, n: str) -> None: self.data[2] = n @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio de vigência do custo de déficit @@ -832,11 +832,11 @@ def estagio(self) -> Optional[int]: return self.data[3] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[3] = e @property - def limite_superior(self) -> Optional[List[float]]: + def limite_superior(self) -> list[float] | None: """ O limite superior para consideração dos custos. @@ -846,11 +846,11 @@ def limite_superior(self) -> Optional[List[float]]: return [v for v in self.data[4::2] if v is not None] @limite_superior.setter - def limite_superior(self, lim: List[float]): + def limite_superior(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 4, 2) @property - def custo(self) -> Optional[List[float]]: + def custo(self) -> list[float] | None: """ O custo de déficit. @@ -860,7 +860,7 @@ def custo(self) -> Optional[List[float]]: return [v for v in self.data[5::2] if v is not None] @custo.setter - def custo(self, cus: List[float]): + def custo(self, cus: list[float]) -> None: self.__atualiza_dados_lista(cus, 5, 2) @@ -870,7 +870,7 @@ class RI(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "RI " IDENTIFIER_DIGITS = 4 @@ -909,10 +909,10 @@ class RI(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -923,7 +923,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da usina de Itaipu. @@ -933,11 +933,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, codigo: int): + def codigo_usina(self, codigo: int) -> None: self.data[0] = codigo @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O índice do estágio. @@ -947,11 +947,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, estagio: int): + def estagio(self, estagio: int) -> None: self.data[1] = estagio @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O submercado que representa o Sudeste. @@ -961,11 +961,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[2] @codigo_submercado.setter - def codigo_submercado(self, submercado: int): + def codigo_submercado(self, submercado: int) -> None: self.data[2] = submercado @property - def geracao_minima_60_hz(self) -> Optional[List[float]]: + def geracao_minima_60_hz(self) -> list[float] | None: """ A geração mínima de Itaipu 60 Hz. @@ -975,11 +975,11 @@ def geracao_minima_60_hz(self) -> Optional[List[float]]: return self.data[3::5] @geracao_minima_60_hz.setter - def geracao_minima_60_hz(self, lim: List[float]): + def geracao_minima_60_hz(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 3, 5) @property - def geracao_maxima_60_hz(self) -> Optional[List[float]]: + def geracao_maxima_60_hz(self) -> list[float] | None: """ A geração máxima de Itaipu 60 Hz. @@ -989,11 +989,11 @@ def geracao_maxima_60_hz(self) -> Optional[List[float]]: return self.data[4::5] @geracao_maxima_60_hz.setter - def geracao_maxima_60_hz(self, lim: List[float]): + def geracao_maxima_60_hz(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 4, 5) @property - def geracao_minima_50_hz(self) -> Optional[List[float]]: + def geracao_minima_50_hz(self) -> list[float] | None: """ A geração mínima de Itaipu 50 Hz. @@ -1003,11 +1003,11 @@ def geracao_minima_50_hz(self) -> Optional[List[float]]: return self.data[5::5] @geracao_minima_50_hz.setter - def geracao_minima_50_hz(self, lim: List[float]): + def geracao_minima_50_hz(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 5, 5) @property - def geracao_maxima_50_hz(self) -> Optional[List[float]]: + def geracao_maxima_50_hz(self) -> list[float] | None: """ A geração máxima de Itaipu 50 Hz. @@ -1017,11 +1017,11 @@ def geracao_maxima_50_hz(self) -> Optional[List[float]]: return self.data[6::5] @geracao_maxima_50_hz.setter - def geracao_maxima_50_hz(self, lim: List[float]): + def geracao_maxima_50_hz(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 6, 5) @property - def carga_ande(self) -> Optional[List[float]]: + def carga_ande(self) -> list[float] | None: """ A carga da ANDE. @@ -1031,7 +1031,7 @@ def carga_ande(self) -> Optional[List[float]]: return self.data[7::5] @carga_ande.setter - def carga_ande(self, lim: List[float]): + def carga_ande(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 7, 5) @@ -1041,7 +1041,7 @@ class IA(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "IA " IDENTIFIER_DIGITS = 4 @@ -1065,7 +1065,7 @@ class IA(Register): ) @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O índice do estágio. @@ -1075,11 +1075,11 @@ def estagio(self) -> Optional[int]: return self.data[0] @estagio.setter - def estagio(self, t: int): + def estagio(self, t: int) -> None: self.data[0] = t @property - def nome_submercado_de(self) -> Optional[str]: + def nome_submercado_de(self) -> str | None: """ O nome do submercado de origem. @@ -1089,11 +1089,11 @@ def nome_submercado_de(self) -> Optional[str]: return self.data[1] @nome_submercado_de.setter - def nome_submercado_de(self, t: str): + def nome_submercado_de(self, t: str) -> None: self.data[1] = t @property - def nome_submercado_para(self) -> Optional[str]: + def nome_submercado_para(self) -> str | None: """ O nome do submercado de destino. @@ -1103,7 +1103,7 @@ def nome_submercado_para(self) -> Optional[str]: return self.data[2] @nome_submercado_para.setter - def nome_submercado_para(self, t: str): + def nome_submercado_para(self, t: str) -> None: self.data[2] = t @@ -1112,7 +1112,7 @@ class TX(Register): Registro que contém a taxa de desconto anual do modelo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "TX " IDENTIFIER_DIGITS = 4 @@ -1123,7 +1123,7 @@ class TX(Register): ) @property - def taxa(self) -> Optional[float]: + def taxa(self) -> float | None: """ A taxa de desconto em % utilizada no estudo. @@ -1133,7 +1133,7 @@ def taxa(self) -> Optional[float]: return self.data[0] @taxa.setter - def taxa(self, t: float): + def taxa(self, t: float) -> None: self.data[0] = t @@ -1142,7 +1142,7 @@ class GP(Register): Registro que contém o gap de tolerância para convergência. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "GP " IDENTIFIER_DIGITS = 4 @@ -1153,7 +1153,7 @@ class GP(Register): ) @property - def gap(self) -> Optional[float]: + def gap(self) -> float | None: """ O gap considerado para convergência no estudo @@ -1163,7 +1163,7 @@ def gap(self) -> Optional[float]: return self.data[0] @gap.setter - def gap(self, g: float): + def gap(self, g: float) -> None: self.data[0] = g @@ -1172,14 +1172,14 @@ class NI(Register): Registro que contém o número máximo de iterações do modelo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "NI " IDENTIFIER_DIGITS = 4 LINE = Line([IntegerField(3, 4), IntegerField(1, 8)]) @property - def iteracoes(self) -> Optional[int]: + def iteracoes(self) -> int | None: """ O número máximo de iterações do modelo no estudo @@ -1189,11 +1189,11 @@ def iteracoes(self) -> Optional[int]: return self.data[0] @iteracoes.setter - def iteracoes(self, i: int): + def iteracoes(self, i: int) -> None: self.data[0] = i @property - def tipo_limite(self) -> Optional[int]: + def tipo_limite(self) -> int | None: """ Se o número de interações fornecido é mínimo ou máximo. @@ -1203,7 +1203,7 @@ def tipo_limite(self) -> Optional[int]: return self.data[1] @tipo_limite.setter - def tipo_limite(self, i: int): + def tipo_limite(self, i: int) -> None: self.data[1] = i @@ -1212,7 +1212,7 @@ class DT(Register): Registro que contém a data de referência do estudo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "DT " IDENTIFIER_DIGITS = 4 @@ -1225,7 +1225,7 @@ class DT(Register): ) @property - def dia(self) -> Optional[int]: + def dia(self) -> int | None: """ O dia de referência para realização do estudo @@ -1235,11 +1235,11 @@ def dia(self) -> Optional[int]: return self.data[0] @dia.setter - def dia(self, d: int): + def dia(self, d: int) -> None: self.data[0] = d @property - def mes(self) -> Optional[int]: + def mes(self) -> int | None: """ O mês de referência para realização do estudo @@ -1249,11 +1249,11 @@ def mes(self) -> Optional[int]: return self.data[1] @mes.setter - def mes(self, m: int): + def mes(self, m: int) -> None: self.data[1] = m @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: """ O ano de referência para realização do estudo @@ -1263,7 +1263,7 @@ def ano(self) -> Optional[int]: return self.data[2] @ano.setter - def ano(self, a: int): + def ano(self, a: int) -> None: self.data[2] = a @@ -1273,7 +1273,7 @@ class MP(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "MP " IDENTIFIER_DIGITS = 4 @@ -1310,10 +1310,10 @@ class MP(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -1324,7 +1324,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UHE associada às manutenções programadas. @@ -1333,11 +1333,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def frequencia(self) -> Optional[int]: + def frequencia(self) -> int | None: """ A frequência quando se trata da UHE Itaipu. @@ -1346,11 +1346,11 @@ def frequencia(self) -> Optional[int]: return self.data[1] @frequencia.setter - def frequencia(self, c: int): + def frequencia(self, c: int) -> None: self.data[1] = c @property - def manutencao(self) -> Optional[List[float]]: + def manutencao(self) -> list[float] | None: """ Os fatores de manutenção programada por estágio do estudo. A posição na lista indica a qual estágio @@ -1362,7 +1362,7 @@ def manutencao(self) -> Optional[List[float]]: return [v for v in self.data[2::] if v is not None] @manutencao.setter - def manutencao(self, m: List[float]): + def manutencao(self, m: list[float]) -> None: self.__atualiza_dados_lista(m, 2, 1) @@ -1372,7 +1372,7 @@ class MT(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "MT " IDENTIFIER_DIGITS = 4 @@ -1409,10 +1409,10 @@ class MT(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -1423,7 +1423,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UTE associada às manutenções programadas. @@ -1432,11 +1432,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado ao qual a UTE pertence. @@ -1445,11 +1445,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, c: int): + def codigo_submercado(self, c: int) -> None: self.data[1] = c @property - def manutencao(self) -> Optional[List[float]]: + def manutencao(self) -> list[float] | None: """ Os fatores de manutenção programada por estágio do estudo. A posição na lista indica a qual estágio @@ -1461,7 +1461,7 @@ def manutencao(self) -> Optional[List[float]]: return [v for v in self.data[2::] if v is not None] @manutencao.setter - def manutencao(self, m: List[float]): + def manutencao(self, m: list[float]) -> None: self.__atualiza_dados_lista(m, 2, 1) @@ -1471,7 +1471,7 @@ class FD(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FD " IDENTIFIER_DIGITS = 4 @@ -1508,10 +1508,10 @@ class FD(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -1522,7 +1522,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UHE associada aos fatores de disponibilidade. @@ -1531,11 +1531,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def frequencia(self) -> Optional[int]: + def frequencia(self) -> int | None: """ A frequência quando se trata da UHE Itaipu. @@ -1544,11 +1544,11 @@ def frequencia(self) -> Optional[int]: return self.data[1] @frequencia.setter - def frequencia(self, c: int): + def frequencia(self, c: int) -> None: self.data[1] = c @property - def fator(self) -> Optional[List[float]]: + def fator(self) -> list[float] | None: """ Os fatores de disponibilidade por estágio do estudo. A posição na lista indica a qual estágio @@ -1560,7 +1560,7 @@ def fator(self) -> Optional[List[float]]: return [v for v in self.data[2::] if v is not None] @fator.setter - def fator(self, m: List[float]): + def fator(self, m: list[float]) -> None: self.__atualiza_dados_lista(m, 2, 1) @@ -1570,7 +1570,7 @@ class VE(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "VE " IDENTIFIER_DIGITS = 4 @@ -1606,10 +1606,10 @@ class VE(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -1620,7 +1620,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da usina associada ao volume. @@ -1630,11 +1630,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def volume(self) -> Optional[List[float]]: + def volume(self) -> list[float] | None: """ O volume de espera por estagio. @@ -1644,7 +1644,7 @@ def volume(self) -> Optional[List[float]]: return [v for v in self.data[1:] if v is not None] @volume.setter - def volume(self, cus: List[float]): + def volume(self, cus: list[float]) -> None: self.__atualiza_dados_lista(cus, 1, 1) @@ -1653,7 +1653,7 @@ class RE(Register): Registro que contém os cadastros de restrições elétricas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "RE " IDENTIFIER_DIGITS = 4 @@ -1666,7 +1666,7 @@ class RE(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código de cadastro para a restrição @@ -1676,11 +1676,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicial(self) -> Optional[int]: + def estagio_inicial(self) -> int | None: """ O estágio inicial para consideração da restrição @@ -1690,11 +1690,11 @@ def estagio_inicial(self) -> Optional[int]: return self.data[1] @estagio_inicial.setter - def estagio_inicial(self, e: int): + def estagio_inicial(self, e: int) -> None: self.data[1] = e @property - def estagio_final(self) -> Optional[int]: + def estagio_final(self) -> int | None: """ O estágio final para consideração da restrição @@ -1704,7 +1704,7 @@ def estagio_final(self) -> Optional[int]: return self.data[2] @estagio_final.setter - def estagio_final(self, e: int): + def estagio_final(self, e: int) -> None: self.data[2] = e @@ -1714,7 +1714,7 @@ class LU(Register): """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "LU " IDENTIFIER_DIGITS = 4 @@ -1737,10 +1737,10 @@ class LU(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -1751,7 +1751,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição RE associada aos limites @@ -1761,11 +1761,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio inicial para consideração dos limites, até que sejam especificados novos limites. @@ -1776,11 +1776,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[1] = e @property - def limite_inferior(self) -> Optional[List[float]]: + def limite_inferior(self) -> list[float] | None: """ O limite inferior por patamar para a restrição elétrica @@ -1790,11 +1790,11 @@ def limite_inferior(self) -> Optional[List[float]]: return self.data[2::2] @limite_inferior.setter - def limite_inferior(self, lim: List[float]): + def limite_inferior(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 2, 2) @property - def limite_superior(self) -> Optional[List[float]]: + def limite_superior(self) -> list[float] | None: """ O limite superior por patamar para a restrição elétrica @@ -1804,7 +1804,7 @@ def limite_superior(self) -> Optional[List[float]]: return self.data[3::2] @limite_superior.setter - def limite_superior(self, lim: List[float]): + def limite_superior(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 3, 2) @@ -1814,7 +1814,7 @@ class FU(Register): nas restrições elétricas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FU " IDENTIFIER_DIGITS = 4 @@ -1829,7 +1829,7 @@ class FU(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição elétrica, segundo registro RE. @@ -1838,11 +1838,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado. @@ -1851,11 +1851,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, c: int): + def estagio(self, c: int) -> None: self.data[1] = c @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O número da UHE conforme registro UH. @@ -1864,11 +1864,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[2] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[2] = c @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: """ O coeficiente de participação da usina na restrição. @@ -1877,11 +1877,11 @@ def coeficiente(self) -> Optional[float]: return self.data[3] @coeficiente.setter - def coeficiente(self, f: float): + def coeficiente(self, f: float) -> None: self.data[3] = f @property - def frequencia(self) -> Optional[int]: + def frequencia(self) -> int | None: """ A frequência para restrição elétrica, valendo 50 ou 60 para Itaipu e 0 para as demais. @@ -1891,7 +1891,7 @@ def frequencia(self) -> Optional[int]: return self.data[4] @frequencia.setter - def frequencia(self, c: int): + def frequencia(self, c: int) -> None: self.data[4] = c @@ -1901,7 +1901,7 @@ class FT(Register): nas restrições elétricas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FT " IDENTIFIER_DIGITS = 4 @@ -1916,7 +1916,7 @@ class FT(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição elétrica, segundo registro RE. @@ -1925,11 +1925,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado. @@ -1938,11 +1938,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, c: int): + def estagio(self, c: int) -> None: self.data[1] = c @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O número da UTE conforme registro CT. @@ -1951,11 +1951,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[2] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[2] = c @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado conforme registros CT ou TG. @@ -1964,11 +1964,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[3] @codigo_submercado.setter - def codigo_submercado(self, s: int): + def codigo_submercado(self, s: int) -> None: self.data[3] = s @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: """ O coeficiente de participação da usina na restrição. @@ -1977,7 +1977,7 @@ def coeficiente(self) -> Optional[float]: return self.data[4] @coeficiente.setter - def coeficiente(self, f: float): + def coeficiente(self, f: float) -> None: self.data[4] = f @@ -1987,7 +1987,7 @@ class FI(Register): entre os subsistemas associados à restrição elétrica. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FI " IDENTIFIER_DIGITS = 4 @@ -2002,7 +2002,7 @@ class FI(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição elétrica, segundo registro RE. @@ -2011,11 +2011,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado. @@ -2024,11 +2024,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, c: int): + def estagio(self, c: int) -> None: self.data[1] = c @property - def codigo_submercado_de(self) -> Optional[str]: + def codigo_submercado_de(self) -> str | None: """ O submercado de origem conforme registros SB. @@ -2037,11 +2037,11 @@ def codigo_submercado_de(self) -> Optional[str]: return self.data[2] @codigo_submercado_de.setter - def codigo_submercado_de(self, s: str): + def codigo_submercado_de(self, s: str) -> None: self.data[2] = s @property - def codigo_submercado_para(self) -> Optional[str]: + def codigo_submercado_para(self) -> str | None: """ O submercado de destino conforme registros SB. @@ -2050,11 +2050,11 @@ def codigo_submercado_para(self) -> Optional[str]: return self.data[3] @codigo_submercado_para.setter - def codigo_submercado_para(self, s: str): + def codigo_submercado_para(self, s: str) -> None: self.data[3] = s @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: """ O coeficiente de participação da interligação. @@ -2063,7 +2063,7 @@ def coeficiente(self) -> Optional[float]: return self.data[4] @coeficiente.setter - def coeficiente(self, f: float): + def coeficiente(self, f: float) -> None: self.data[4] = f @@ -2072,7 +2072,7 @@ class VI(Register): Registro que contém os tempos de viagem da água entre usinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "VI " IDENTIFIER_DIGITS = 4 @@ -2094,10 +2094,10 @@ class VI(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -2108,7 +2108,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UHE a partir do qual é contabilizado o tempo de viagem. @@ -2119,11 +2119,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def duracao(self) -> Optional[int]: + def duracao(self) -> int | None: """ A duração da viagem da água (em horas) entre a UHE do código informado e sua usina à jusante segundo o hidr. @@ -2134,11 +2134,11 @@ def duracao(self) -> Optional[int]: return self.data[1] @duracao.setter - def duracao(self, d: int): + def duracao(self, d: int) -> None: self.data[1] = d @property - def vazao(self) -> Optional[List[float]]: + def vazao(self) -> list[float] | None: """ As vazões defluentes das semanas passadas para a usina do código informado. A posição da vazão na lista indica @@ -2150,7 +2150,7 @@ def vazao(self) -> Optional[List[float]]: return [v for v in self.data[2::] if v is not None] @vazao.setter - def vazao(self, v: List[float]): + def vazao(self, v: list[float]) -> None: self.__atualiza_dados_lista(v, 2, 1) @@ -2160,7 +2160,7 @@ class IR(Register): geração de relatórios de saída. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "IR " IDENTIFIER_DIGITS = 4 @@ -2174,7 +2174,7 @@ class IR(Register): ) @property - def tipo(self) -> Optional[str]: + def tipo(self) -> str | None: """ Mnemônico que contém o tipo de relatório de saída escolhido. @@ -2185,7 +2185,7 @@ def tipo(self) -> Optional[str]: return self.data[0] @tipo.setter - def tipo(self, t: str): + def tipo(self, t: str) -> None: self.data[0] = t @@ -2194,7 +2194,7 @@ class CI(Register): Registro que define contratos de importação de energia. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CI " IDENTIFIER_DIGITS = 4 @@ -2223,7 +2223,7 @@ class CE(Register): Registro que define contratos de exportação de energia. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CE " IDENTIFIER_DIGITS = 4 @@ -2253,7 +2253,7 @@ class FC(Register): pelo NEWAVE. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FC " IDENTIFIER_DIGITS = 4 @@ -2265,7 +2265,7 @@ class FC(Register): ) @property - def tipo(self) -> Optional[str]: + def tipo(self) -> str | None: """ O tipo de arquivo da FCF na forma dos mnemônicos aceitos pelo DECOMP. @@ -2276,7 +2276,7 @@ def tipo(self) -> Optional[str]: return self.data[0] @property - def caminho(self) -> Optional[str]: + def caminho(self) -> str | None: """ O caminho relativo ou completo para o arquivo da FCF. @@ -2287,7 +2287,7 @@ def caminho(self) -> Optional[str]: return self.data[1] @caminho.setter - def caminho(self, c: str): + def caminho(self, c: str) -> None: self.data[1] = c @@ -2296,7 +2296,7 @@ class EA(Register): Registro que a ENA dos meses que antecedem o estudo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "EA " IDENTIFIER_DIGITS = 4 @@ -2319,10 +2319,10 @@ class EA(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -2333,7 +2333,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_ree(self) -> Optional[int]: + def codigo_ree(self) -> int | None: """ O código do REE @@ -2343,11 +2343,11 @@ def codigo_ree(self) -> Optional[int]: return self.data[0] @codigo_ree.setter - def codigo_ree(self, c: int): + def codigo_ree(self, c: int) -> None: self.data[0] = c @property - def ena(self) -> Optional[List[float]]: + def ena(self) -> list[float] | None: """ A ENA passada para o REE. @@ -2357,7 +2357,7 @@ def ena(self) -> Optional[List[float]]: return [v for v in self.data[1::] if v is not None] @ena.setter - def ena(self, c: List[float]): + def ena(self, c: list[float]) -> None: self.__atualiza_dados_lista(c, 1, 1) self.data[1:] = c @@ -2367,7 +2367,7 @@ class ES(Register): Registro que define a ENA das semanas que antecedem o estudo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "ES " IDENTIFIER_DIGITS = 4 @@ -2385,10 +2385,10 @@ class ES(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -2399,7 +2399,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_ree(self) -> Optional[int]: + def codigo_ree(self) -> int | None: """ O código do REE @@ -2409,11 +2409,11 @@ def codigo_ree(self) -> Optional[int]: return self.data[0] @codigo_ree.setter - def codigo_ree(self, c: int): + def codigo_ree(self, c: int) -> None: self.data[0] = c @property - def numero_semanas_mes_anterior(self) -> Optional[int]: + def numero_semanas_mes_anterior(self) -> int | None: """ O número de semanas do mês anterior @@ -2423,11 +2423,11 @@ def numero_semanas_mes_anterior(self) -> Optional[int]: return self.data[1] @numero_semanas_mes_anterior.setter - def numero_semanas_mes_anterior(self, c: int): + def numero_semanas_mes_anterior(self, c: int) -> None: self.data[1] = c @property - def ena(self) -> Optional[List[float]]: + def ena(self) -> list[float] | None: """ A ENA passada para o REE. @@ -2437,7 +2437,7 @@ def ena(self) -> Optional[List[float]]: return [v for v in self.data[2::] if v is not None] @ena.setter - def ena(self, c: List[float]): + def ena(self, c: list[float]) -> None: self.__atualiza_dados_lista(c, 1, 1) self.data[2:] = c @@ -2447,7 +2447,7 @@ class QI(Register): Registro que define o tempo de viagem para o cálculo da ENA. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "QI " IDENTIFIER_DIGITS = 4 @@ -2464,10 +2464,10 @@ class QI(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -2478,7 +2478,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UHE. @@ -2488,11 +2488,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def vazao(self) -> Optional[List[float]]: + def vazao(self) -> list[float] | None: """ As vazões incrementais para cálculo da ENA. @@ -2502,7 +2502,7 @@ def vazao(self) -> Optional[List[float]]: return [v for v in self.data[1::] if v is not None] @vazao.setter - def vazao(self, c: List[float]): + def vazao(self, c: list[float]) -> None: self.__atualiza_dados_lista(c, 1, 1) self.data[1:] = c @@ -2513,7 +2513,7 @@ class RT(Register): vertedouro e de canais de desvio. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "RT " IDENTIFIER_DIGITS = 4 @@ -2524,7 +2524,7 @@ class RT(Register): ) @property - def restricao(self) -> Optional[str]: + def restricao(self) -> str | None: """ O mnemônico da restrição removida. @@ -2534,7 +2534,7 @@ def restricao(self) -> Optional[str]: return self.data[0] @restricao.setter - def restricao(self, m: str): + def restricao(self, m: str) -> None: self.data[0] = m @@ -2577,10 +2577,10 @@ class TI(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -2591,7 +2591,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UHE associada às taxas de irrigação @@ -2600,11 +2600,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def taxa(self) -> Optional[List[float]]: + def taxa(self) -> list[float] | None: """ As taxas de irrigação por estágio do estudo. A posição da taxa na lista indica a qual estágio @@ -2616,7 +2616,7 @@ def taxa(self) -> Optional[List[float]]: return [v for v in self.data[1::] if v is not None] @taxa.setter - def taxa(self, tx: List[float]): + def taxa(self, tx: list[float]) -> None: self.__atualiza_dados_lista(tx, 1, 1) @@ -2626,7 +2626,7 @@ class DA(Register): (desvios de água) por UHE. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "DA " IDENTIFIER_DIGITS = 4 @@ -2642,7 +2642,7 @@ class DA(Register): ) @property - def codigo_usina_retirada(self) -> Optional[int]: + def codigo_usina_retirada(self) -> int | None: """ O código da UHE a montante da qual será feita a retirada. @@ -2652,11 +2652,11 @@ def codigo_usina_retirada(self) -> Optional[int]: return self.data[0] @codigo_usina_retirada.setter - def codigo_usina_retirada(self, c: int): + def codigo_usina_retirada(self, c: int) -> None: self.data[0] = c @property - def codigo_usina_retorno(self) -> Optional[int]: + def codigo_usina_retorno(self) -> int | None: """ O código da UHE a montante da qual se derá o retorno. @@ -2666,11 +2666,11 @@ def codigo_usina_retorno(self) -> Optional[int]: return self.data[1] @codigo_usina_retorno.setter - def codigo_usina_retorno(self, c: int): + def codigo_usina_retorno(self, c: int) -> None: self.data[1] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado à restrição DA. @@ -2680,11 +2680,11 @@ def estagio(self) -> Optional[int]: return self.data[2] @estagio.setter - def estagio(self, c: int): + def estagio(self, c: int) -> None: self.data[2] = c @property - def vazao_desviada(self) -> Optional[float]: + def vazao_desviada(self) -> float | None: """ A vazão a ser desviada em m³/s. @@ -2694,11 +2694,11 @@ def vazao_desviada(self) -> Optional[float]: return self.data[3] @vazao_desviada.setter - def vazao_desviada(self, c: float): + def vazao_desviada(self, c: float) -> None: self.data[3] = c @property - def retorno_percentual(self) -> Optional[float]: + def retorno_percentual(self) -> float | None: """ O retorno em percentual da vazão desviada. @@ -2708,11 +2708,11 @@ def retorno_percentual(self) -> Optional[float]: return self.data[4] @retorno_percentual.setter - def retorno_percentual(self, c: float): + def retorno_percentual(self, c: float) -> None: self.data[4] = c @property - def custo(self) -> Optional[float]: + def custo(self) -> float | None: """ O custo de não antedimento do desvio em $/hm³. @@ -2722,7 +2722,7 @@ def custo(self) -> Optional[float]: return self.data[5] @custo.setter - def custo(self, c: float): + def custo(self, c: float) -> None: self.data[5] = c @@ -2732,7 +2732,7 @@ class FP(Register): função de produção das usinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FP " IDENTIFIER_DIGITS = 4 @@ -2755,7 +2755,7 @@ class FP(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UHE associada à restrição FP. @@ -2765,11 +2765,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado à restrição FP. @@ -2779,11 +2779,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[1] = e @property - def tipo_entrada_janela_turbinamento(self) -> Optional[int]: + def tipo_entrada_janela_turbinamento(self) -> int | None: """ O tipo de entrada da janela de turbinamento fornecido na restrição FP. 0 para limites em percentual da vazão turbinada @@ -2795,11 +2795,11 @@ def tipo_entrada_janela_turbinamento(self) -> Optional[int]: return self.data[2] @tipo_entrada_janela_turbinamento.setter - def tipo_entrada_janela_turbinamento(self, t: int): + def tipo_entrada_janela_turbinamento(self, t: int) -> None: self.data[2] = t @property - def numero_pontos_turbinamento(self) -> Optional[int]: + def numero_pontos_turbinamento(self) -> int | None: """ O número de pontos para discretização da janela de turbinamento. Máximo permitido de 1000 pontos. @@ -2810,11 +2810,11 @@ def numero_pontos_turbinamento(self) -> Optional[int]: return self.data[3] @numero_pontos_turbinamento.setter - def numero_pontos_turbinamento(self, n: int): + def numero_pontos_turbinamento(self, n: int) -> None: self.data[3] = n @property - def limite_inferior_janela_turbinamento(self) -> Optional[float]: + def limite_inferior_janela_turbinamento(self) -> float | None: """ O limite inferior da janela de turbinamento. @@ -2824,11 +2824,11 @@ def limite_inferior_janela_turbinamento(self) -> Optional[float]: return self.data[4] @limite_inferior_janela_turbinamento.setter - def limite_inferior_janela_turbinamento(self, lim: float): + def limite_inferior_janela_turbinamento(self, lim: float) -> None: self.data[4] = lim @property - def limite_superior_janela_turbinamento(self) -> Optional[float]: + def limite_superior_janela_turbinamento(self) -> float | None: """ O limite superior da janela de turbinamento. @@ -2838,11 +2838,11 @@ def limite_superior_janela_turbinamento(self) -> Optional[float]: return self.data[5] @limite_superior_janela_turbinamento.setter - def limite_superior_janela_turbinamento(self, lim: float): + def limite_superior_janela_turbinamento(self, lim: float) -> None: self.data[5] = lim @property - def tipo_entrada_janela_volume(self) -> Optional[int]: + def tipo_entrada_janela_volume(self) -> int | None: """ O tipo de entrada da janela de volume fornecido na restrição FP. 0 para limites em percentual do volume útil @@ -2854,11 +2854,11 @@ def tipo_entrada_janela_volume(self) -> Optional[int]: return self.data[6] @tipo_entrada_janela_volume.setter - def tipo_entrada_janela_volume(self, t: int): + def tipo_entrada_janela_volume(self, t: int) -> None: self.data[6] = t @property - def numero_pontos_volume(self) -> Optional[int]: + def numero_pontos_volume(self) -> int | None: """ O número de pontos para discretização da janela de volume. Máximo permitido de 1000 pontos. @@ -2869,11 +2869,11 @@ def numero_pontos_volume(self) -> Optional[int]: return self.data[7] @numero_pontos_volume.setter - def numero_pontos_volume(self, n: int): + def numero_pontos_volume(self, n: int) -> None: self.data[7] = n @property - def limite_inferior_janela_volume(self) -> Optional[float]: + def limite_inferior_janela_volume(self) -> float | None: """ A redução aplicada ao volume útil da usina, para ser utilizado como limite inferior da janela de volume. @@ -2884,11 +2884,11 @@ def limite_inferior_janela_volume(self) -> Optional[float]: return self.data[8] @limite_inferior_janela_volume.setter - def limite_inferior_janela_volume(self, lim: float): + def limite_inferior_janela_volume(self, lim: float) -> None: self.data[8] = lim @property - def limite_superior_janela_volume(self) -> Optional[float]: + def limite_superior_janela_volume(self) -> float | None: """ O acréscimo aplicado ao volume útil da usina, para ser utilizado como limite superior da janela de volume. @@ -2899,7 +2899,7 @@ def limite_superior_janela_volume(self) -> Optional[float]: return self.data[9] @limite_superior_janela_volume.setter - def limite_superior_janela_volume(self, lim: float): + def limite_superior_janela_volume(self, lim: float) -> None: self.data[9] = lim @@ -2909,7 +2909,7 @@ class RQ(Register): mínima histórica para cada REE. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "RQ " IDENTIFIER_DIGITS = 4 @@ -2929,10 +2929,10 @@ class RQ(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -2943,7 +2943,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_ree(self) -> Optional[int]: + def codigo_ree(self) -> int | None: """ O código do REE associado às vazões mínimas. @@ -2953,11 +2953,11 @@ def codigo_ree(self) -> Optional[int]: return self.data[0] @codigo_ree.setter - def codigo_ree(self, r: int): + def codigo_ree(self, r: int) -> None: self.data[0] = r @property - def vazao(self) -> Optional[List[float]]: + def vazao(self) -> list[float] | None: """ As vazões defluentes mínimas (percentuais) para o REE, por estágio [e1, e2, e3, ...]. @@ -2968,7 +2968,7 @@ def vazao(self) -> Optional[List[float]]: return [v for v in self.data[1:] if v is not None] @vazao.setter - def vazao(self, v: List[float]): + def vazao(self, v: list[float]) -> None: self.__atualiza_dados_lista(v, 1, 1) self.data[1:] = v @@ -2979,7 +2979,7 @@ class EZ(Register): volume útil para acoplamento. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "EZ " IDENTIFIER_DIGITS = 4 @@ -2991,7 +2991,7 @@ class EZ(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ Código da UHE associada, conforme registro UH. @@ -3001,11 +3001,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def volume(self) -> Optional[float]: + def volume(self) -> float | None: """ O volume útil considerado para cálculo. @@ -3015,7 +3015,7 @@ def volume(self) -> Optional[float]: return self.data[1] @volume.setter - def volume(self, u: float): + def volume(self, u: float) -> None: self.data[1] = u @@ -3024,7 +3024,7 @@ class HV(Register): Registro que contém os cadastros de restrições de volume armazenado. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "HV " IDENTIFIER_DIGITS = 4 @@ -3037,7 +3037,7 @@ class HV(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição HV. @@ -3047,11 +3047,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicial(self) -> Optional[int]: + def estagio_inicial(self) -> int | None: """ O estágio inicial de consideração da restrição HV. @@ -3061,11 +3061,11 @@ def estagio_inicial(self) -> Optional[int]: return self.data[1] @estagio_inicial.setter - def estagio_inicial(self, e: int): + def estagio_inicial(self, e: int) -> None: self.data[1] = e @property - def estagio_final(self) -> Optional[int]: + def estagio_final(self) -> int | None: """ O estágio final de consideração da restrição HV. @@ -3075,7 +3075,7 @@ def estagio_final(self) -> Optional[int]: return self.data[2] @estagio_final.setter - def estagio_final(self, e: int): + def estagio_final(self, e: int) -> None: self.data[2] = e @@ -3085,7 +3085,7 @@ class LV(Register): restrições de volume armazenado. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "LV " IDENTIFIER_DIGITS = 4 @@ -3099,7 +3099,7 @@ class LV(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição HV associada aos limites @@ -3109,11 +3109,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """' O estágio de consideração dos limites. @@ -3123,11 +3123,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[1] = e @property - def limite_inferior(self) -> Optional[float]: + def limite_inferior(self) -> float | None: """ O limite inferior para o armazenamento. @@ -3137,11 +3137,11 @@ def limite_inferior(self) -> Optional[float]: return self.data[2] @limite_inferior.setter - def limite_inferior(self, lim: float): + def limite_inferior(self, lim: float) -> None: self.data[2] = lim @property - def limite_superior(self) -> Optional[float]: + def limite_superior(self) -> float | None: """ O limite superior para o armazenamento. @@ -3151,7 +3151,7 @@ def limite_superior(self) -> Optional[float]: return self.data[3] @limite_superior.setter - def limite_superior(self, lim: float): + def limite_superior(self, lim: float) -> None: self.data[3] = lim @@ -3161,7 +3161,7 @@ class CV(Register): nas restrições de volume armazenado. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CV " IDENTIFIER_DIGITS = 4 @@ -3176,7 +3176,7 @@ class CV(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição de volume, segundo registro HV. @@ -3185,11 +3185,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado. @@ -3198,11 +3198,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, c: int): + def estagio(self, c: int) -> None: self.data[1] = c @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O número da UHE ou estação de bombeamento conforme registros UH ou UE. @@ -3211,11 +3211,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[2] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[2] = c @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: """ O coeficiente da variável na restrição. @@ -3224,11 +3224,11 @@ def coeficiente(self) -> Optional[float]: return self.data[3] @coeficiente.setter - def coeficiente(self, f: float): + def coeficiente(self, f: float) -> None: self.data[3] = f @property - def tipo(self) -> Optional[str]: + def tipo(self) -> str | None: """ O mnemônico de tipo da restrição. @@ -3237,7 +3237,7 @@ def tipo(self) -> Optional[str]: return self.data[4] @tipo.setter - def tipo(self, t: str): + def tipo(self, t: str) -> None: self.data[4] = t @@ -3246,7 +3246,7 @@ class HQ(Register): Registro que contém os cadastros de restrições de vazões. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "HQ " IDENTIFIER_DIGITS = 4 @@ -3259,7 +3259,7 @@ class HQ(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição HQ. @@ -3269,11 +3269,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicial(self) -> Optional[int]: + def estagio_inicial(self) -> int | None: """ O estágio inicial de consideração da restrição HQ. @@ -3283,11 +3283,11 @@ def estagio_inicial(self) -> Optional[int]: return self.data[1] @estagio_inicial.setter - def estagio_inicial(self, e: int): + def estagio_inicial(self, e: int) -> None: self.data[1] = e @property - def estagio_final(self) -> Optional[int]: + def estagio_final(self) -> int | None: """ O estágio final de consideração da restrição HQ. @@ -3297,7 +3297,7 @@ def estagio_final(self) -> Optional[int]: return self.data[2] @estagio_final.setter - def estagio_final(self, e: int): + def estagio_final(self, e: int) -> None: self.data[2] = e @@ -3307,7 +3307,7 @@ class LQ(Register): restrições de vazão. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "LQ " IDENTIFIER_DIGITS = 4 @@ -3330,10 +3330,10 @@ class LQ(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -3344,7 +3344,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição HQ associada aos limites @@ -3354,11 +3354,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio de consideração dos limites. @@ -3368,11 +3368,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[1] = e @property - def limite_inferior(self) -> Optional[List[float]]: + def limite_inferior(self) -> list[float] | None: """ O limite inferior por patamar para a vazão. @@ -3382,11 +3382,11 @@ def limite_inferior(self) -> Optional[List[float]]: return self.data[2::2] @limite_inferior.setter - def limite_inferior(self, lim: List[float]): + def limite_inferior(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 2, 2) @property - def limite_superior(self) -> Optional[List[float]]: + def limite_superior(self) -> list[float] | None: """ O limite superior por patamar para a vazão. @@ -3396,7 +3396,7 @@ def limite_superior(self) -> Optional[List[float]]: return self.data[3::2] @limite_superior.setter - def limite_superior(self, lim: List[float]): + def limite_superior(self, lim: list[float]) -> None: self.__atualiza_dados_lista(lim, 3, 2) @@ -3406,7 +3406,7 @@ class CQ(Register): nas restrições de vazão. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CQ " IDENTIFIER_DIGITS = 4 @@ -3421,7 +3421,7 @@ class CQ(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição de vazão, segundo registro HQ. @@ -3430,11 +3430,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio associado. @@ -3443,11 +3443,11 @@ def estagio(self) -> Optional[int]: return self.data[1] @estagio.setter - def estagio(self, c: int): + def estagio(self, c: int) -> None: self.data[1] = c @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O número da UHE conforme registro UH. @@ -3456,11 +3456,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[2] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[2] = c @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: """ O coeficiente da variável na restrição. @@ -3469,11 +3469,11 @@ def coeficiente(self) -> Optional[float]: return self.data[3] @coeficiente.setter - def coeficiente(self, f: float): + def coeficiente(self, f: float) -> None: self.data[3] = f @property - def tipo(self) -> Optional[str]: + def tipo(self) -> str | None: """ O mnemônico de tipo da restrição. @@ -3482,7 +3482,7 @@ def tipo(self) -> Optional[str]: return self.data[4] @tipo.setter - def tipo(self, t: str): + def tipo(self, t: str) -> None: self.data[4] = t @@ -3491,7 +3491,7 @@ class AR(Register): Registro que contém as configurações de aversão a risco. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "AR " IDENTIFIER_DIGITS = 4 @@ -3504,7 +3504,7 @@ class AR(Register): ) @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio inicial de aplicação do CVaR. @@ -3514,11 +3514,11 @@ def estagio(self) -> Optional[int]: return self.data[0] @estagio.setter - def estagio(self, p: int): + def estagio(self, p: int) -> None: self.data[0] = p @property - def lamb(self) -> Optional[float]: + def lamb(self) -> float | None: """ O valor de lambda utilizado no CVaR. @@ -3528,11 +3528,11 @@ def lamb(self) -> Optional[float]: return self.data[1] @lamb.setter - def lamb(self, v: float): + def lamb(self, v: float) -> None: self.data[1] = v @property - def alfa(self) -> Optional[float]: + def alfa(self) -> float | None: """ O valor de alfa utilizado no CVaR. @@ -3542,7 +3542,7 @@ def alfa(self) -> Optional[float]: return self.data[2] @alfa.setter - def alfa(self, v: float): + def alfa(self, v: float) -> None: self.data[2] = v @@ -3552,7 +3552,7 @@ class EV(Register): da evaporação. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "EV " IDENTIFIER_DIGITS = 4 @@ -3564,7 +3564,7 @@ class EV(Register): ) @property - def modelo(self) -> Optional[int]: + def modelo(self) -> int | None: """ O modelo de evaporação considerado @@ -3574,11 +3574,11 @@ def modelo(self) -> Optional[int]: return self.data[0] @modelo.setter - def modelo(self, m: int): + def modelo(self, m: int) -> None: self.data[0] = m @property - def volume_referencia(self) -> Optional[str]: + def volume_referencia(self) -> str | None: """ O mnemônico para o volume considerado @@ -3588,7 +3588,7 @@ def volume_referencia(self) -> Optional[str]: return self.data[1] @volume_referencia.setter - def volume_referencia(self, v: str): + def volume_referencia(self, v: str) -> None: self.data[1] = v @@ -3597,7 +3597,7 @@ class FJ(Register): Registro que contém o arquivo de polinômios de jusante. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FJ " IDENTIFIER_DIGITS = 4 @@ -3618,7 +3618,7 @@ def arquivo(self) -> str: return self.data[0] @arquivo.setter - def arquivo(self, a: str): + def arquivo(self, a: str) -> None: self.data[0] = a @@ -3628,7 +3628,7 @@ class HE(Register): armazenada. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "HE " IDENTIFIER_DIGITS = 4 @@ -3647,7 +3647,7 @@ class HE(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código de cadastro da restrição HE @@ -3657,11 +3657,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def tipo_limite(self) -> Optional[int]: + def tipo_limite(self) -> int | None: """ O tipo de limite especificado na restrição HE, em valor absoluto ou percentual. @@ -3672,11 +3672,11 @@ def tipo_limite(self) -> Optional[int]: return self.data[1] @tipo_limite.setter - def tipo_limite(self, t: int): + def tipo_limite(self, t: int) -> None: self.data[1] = t @property - def limite(self) -> Optional[float]: + def limite(self) -> float | None: """ O limite para a energia armazenada associada ao registro HE. @@ -3687,11 +3687,11 @@ def limite(self) -> Optional[float]: return self.data[2] @limite.setter - def limite(self, lim: float): + def limite(self, lim: float) -> None: self.data[2] = lim @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio para consideração da restrição. @@ -3701,11 +3701,11 @@ def estagio(self) -> Optional[int]: return self.data[3] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[3] = e @property - def valor_penalidade(self) -> Optional[float]: + def valor_penalidade(self) -> float | None: """ O valor da penalidade para a violação da restrição. @@ -3715,11 +3715,11 @@ def valor_penalidade(self) -> Optional[float]: return self.data[4] @valor_penalidade.setter - def valor_penalidade(self, p: float): + def valor_penalidade(self, p: float) -> None: self.data[4] = p @property - def forma_calculo_produtibilidades(self) -> Optional[int]: + def forma_calculo_produtibilidades(self) -> int | None: """ Flag para indicar a forma de cálculo das produtividades das usinas usadas nas restrição. @@ -3730,11 +3730,11 @@ def forma_calculo_produtibilidades(self) -> Optional[int]: return self.data[5] @forma_calculo_produtibilidades.setter - def forma_calculo_produtibilidades(self, t: int): + def forma_calculo_produtibilidades(self, t: int) -> None: self.data[5] = t @property - def tipo_valores_produtibilidades(self) -> Optional[int]: + def tipo_valores_produtibilidades(self) -> int | None: """ Flag para indicar o tipo dos valores das produtividades das usinas usadas nas restrição. @@ -3745,11 +3745,11 @@ def tipo_valores_produtibilidades(self) -> Optional[int]: return self.data[6] @tipo_valores_produtibilidades.setter - def tipo_valores_produtibilidades(self, t: int): + def tipo_valores_produtibilidades(self, t: int) -> None: self.data[6] = t @property - def tipo_penalidade(self) -> Optional[int]: + def tipo_penalidade(self) -> int | None: """ O tipo de penalidade a ser considerada ao violar a restrição (inviabilidade ou penalização). @@ -3760,11 +3760,11 @@ def tipo_penalidade(self) -> Optional[int]: return self.data[7] @tipo_penalidade.setter - def tipo_penalidade(self, t: int): + def tipo_penalidade(self, t: int) -> None: self.data[7] = t @property - def arquivo_produtibilidades(self) -> Optional[str]: + def arquivo_produtibilidades(self) -> str | None: """ O arquivo com as definições das produtibilidades usadas para o cálculo da restrição RHE. @@ -3775,7 +3775,7 @@ def arquivo_produtibilidades(self) -> Optional[str]: return self.data[8] @arquivo_produtibilidades.setter - def arquivo_produtibilidades(self, t: str): + def arquivo_produtibilidades(self, t: str) -> None: self.data[8] = t @@ -3784,7 +3784,7 @@ class CM(Register): Registro que contém os coeficientes de uma restrição RHE. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CM " IDENTIFIER_DIGITS = 4 @@ -3801,7 +3801,7 @@ def codigo_restricao(self) -> int: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property @@ -3815,7 +3815,7 @@ def codigo_ree(self) -> int: return self.data[1] @codigo_ree.setter - def codigo_ree(self, e: int): + def codigo_ree(self, e: int) -> None: self.data[1] = e @property @@ -3829,7 +3829,7 @@ def coeficiente(self) -> float: return self.data[2] @coeficiente.setter - def coeficiente(self, c: float): + def coeficiente(self, c: float) -> None: self.data[2] = c @@ -3839,7 +3839,7 @@ class PD(Register): resolução do PL. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "PD " IDENTIFIER_DIGITS = 4 @@ -3850,7 +3850,7 @@ class PD(Register): ) @property - def algoritmo(self) -> Optional[str]: + def algoritmo(self) -> str | None: """ O algoritmo considerado. @@ -3860,7 +3860,7 @@ def algoritmo(self) -> Optional[str]: return self.data[0] @algoritmo.setter - def algoritmo(self, m: str): + def algoritmo(self, m: str) -> None: self.data[0] = m @@ -3870,7 +3870,7 @@ class PU(Register): via PL único. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "PU " IDENTIFIER_DIGITS = 4 @@ -3881,7 +3881,7 @@ class PU(Register): ) @property - def pl(self) -> Optional[int]: + def pl(self) -> int | None: """ O tipo de pl considerado. @@ -3891,7 +3891,7 @@ def pl(self) -> Optional[int]: return self.data[0] @pl.setter - def pl(self, m: int): + def pl(self, m: int) -> None: self.data[0] = m @@ -3900,7 +3900,7 @@ class RC(Register): Registro que inclui restrições do tipo escada. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "RC " IDENTIFIER_DIGITS = 4 @@ -3911,7 +3911,7 @@ class RC(Register): ) @property - def mnemonico(self) -> Optional[str]: + def mnemonico(self) -> str | None: """ O tipo de mnemonico considerado. @@ -3921,7 +3921,7 @@ def mnemonico(self) -> Optional[str]: return self.data[0] @mnemonico.setter - def mnemonico(self, m: str): + def mnemonico(self, m: str) -> None: self.data[0] = m @@ -3931,7 +3931,7 @@ class PE(Register): intercâmbio e desvios. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "PE " IDENTIFIER_DIGITS = 4 @@ -3944,7 +3944,7 @@ class PE(Register): ) @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado considerado @@ -3954,11 +3954,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[0] @codigo_submercado.setter - def codigo_submercado(self, m: int): + def codigo_submercado(self, m: int) -> None: self.data[0] = m @property - def tipo(self) -> Optional[int]: + def tipo(self) -> int | None: """ O tipo de penalidade a ser modificado @@ -3968,11 +3968,11 @@ def tipo(self) -> Optional[int]: return self.data[1] @tipo.setter - def tipo(self, m: int): + def tipo(self, m: int) -> None: self.data[1] = m @property - def penalidade(self) -> Optional[float]: + def penalidade(self) -> float | None: """ O novo valor de penalidade @@ -3982,7 +3982,7 @@ def penalidade(self) -> Optional[float]: return self.data[2] @penalidade.setter - def penalidade(self, m: float): + def penalidade(self, m: float) -> None: self.data[2] = m @@ -3991,7 +3991,7 @@ class TS(Register): Registro que altera as tolerâncias do solver. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "TS " IDENTIFIER_DIGITS = 4 @@ -4005,7 +4005,7 @@ class TS(Register): ) @property - def tolerancia_primaria(self) -> Optional[float]: + def tolerancia_primaria(self) -> float | None: """ A nova tolerância primária do solver. @@ -4015,11 +4015,11 @@ def tolerancia_primaria(self) -> Optional[float]: return self.data[0] @tolerancia_primaria.setter - def tolerancia_primaria(self, m: float): + def tolerancia_primaria(self, m: float) -> None: self.data[0] = m @property - def tolerancia_secundaria(self) -> Optional[float]: + def tolerancia_secundaria(self) -> float | None: """ A nova tolerância secundária do solver. @@ -4029,11 +4029,11 @@ def tolerancia_secundaria(self) -> Optional[float]: return self.data[1] @tolerancia_secundaria.setter - def tolerancia_secundaria(self, m: float): + def tolerancia_secundaria(self, m: float) -> None: self.data[1] = m @property - def zera_coeficientes(self) -> Optional[int]: + def zera_coeficientes(self) -> int | None: """ Habilita ou não a funcionalidade de zerar coeficientes em casos de cortes não ótimos. @@ -4044,11 +4044,11 @@ def zera_coeficientes(self) -> Optional[int]: return self.data[2] @zera_coeficientes.setter - def zera_coeficientes(self, m: int): + def zera_coeficientes(self, m: int) -> None: self.data[2] = m @property - def tolerancia_teste_otimalidade(self) -> Optional[float]: + def tolerancia_teste_otimalidade(self) -> float | None: """ A nova tolerância usada no teste de otimalidade da solução do PL. @@ -4059,7 +4059,7 @@ def tolerancia_teste_otimalidade(self) -> Optional[float]: return self.data[3] @tolerancia_teste_otimalidade.setter - def tolerancia_teste_otimalidade(self, m: float): + def tolerancia_teste_otimalidade(self, m: float) -> None: self.data[3] = m @@ -4069,7 +4069,7 @@ class PV(Register): do problema e as tolerâncias para a viabilidade das restrições. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "PV " IDENTIFIER_DIGITS = 4 @@ -4085,7 +4085,7 @@ class PV(Register): ) @property - def penalidade_variaveis_folga(self) -> Optional[float]: + def penalidade_variaveis_folga(self) -> float | None: """ A nova penalidade para as variáveis de folga (R$/MWh). @@ -4095,11 +4095,11 @@ def penalidade_variaveis_folga(self) -> Optional[float]: return self.data[0] @penalidade_variaveis_folga.setter - def penalidade_variaveis_folga(self, m: float): + def penalidade_variaveis_folga(self, m: float) -> None: self.data[0] = m @property - def tolerancia_viabilidade_restricoes(self) -> Optional[float]: + def tolerancia_viabilidade_restricoes(self) -> float | None: """ A nova tolerância para a viabilidade das restrições. @@ -4109,11 +4109,11 @@ def tolerancia_viabilidade_restricoes(self) -> Optional[float]: return self.data[1] @tolerancia_viabilidade_restricoes.setter - def tolerancia_viabilidade_restricoes(self, m: float): + def tolerancia_viabilidade_restricoes(self, m: float) -> None: self.data[1] = m @property - def iteracoes_atualizacao_penalidade(self) -> Optional[int]: + def iteracoes_atualizacao_penalidade(self) -> int | None: """ O número de iterações para atualização da penalidade variável iterativa para as folgas. @@ -4124,11 +4124,11 @@ def iteracoes_atualizacao_penalidade(self) -> Optional[int]: return self.data[2] @iteracoes_atualizacao_penalidade.setter - def iteracoes_atualizacao_penalidade(self, m: int): + def iteracoes_atualizacao_penalidade(self, m: int) -> None: self.data[2] = m @property - def fator_multiplicacao_folga(self) -> Optional[float]: + def fator_multiplicacao_folga(self) -> float | None: """ O fator para multiplicação da folga ao longo das restrições. @@ -4138,11 +4138,11 @@ def fator_multiplicacao_folga(self) -> Optional[float]: return self.data[3] @fator_multiplicacao_folga.setter - def fator_multiplicacao_folga(self, m: float): + def fator_multiplicacao_folga(self, m: float) -> None: self.data[3] = m @property - def valor_inicial_variaveis_folga(self) -> Optional[float]: + def valor_inicial_variaveis_folga(self) -> float | None: """ O valor inicial ou mínimo para as variáveis de folga. @@ -4152,11 +4152,11 @@ def valor_inicial_variaveis_folga(self) -> Optional[float]: return self.data[4] @valor_inicial_variaveis_folga.setter - def valor_inicial_variaveis_folga(self, m: float): + def valor_inicial_variaveis_folga(self, m: float) -> None: self.data[4] = m @property - def valor_final_variaveis_folga(self) -> Optional[float]: + def valor_final_variaveis_folga(self) -> float | None: """ O valor final ou máximo para as variáveis de folga. @@ -4166,7 +4166,7 @@ def valor_final_variaveis_folga(self) -> Optional[float]: return self.data[5] @valor_final_variaveis_folga.setter - def valor_final_variaveis_folga(self, m: float): + def valor_final_variaveis_folga(self, m: float) -> None: self.data[5] = m @@ -4176,7 +4176,7 @@ class CX(Register): complexos no NEWAVE com o DECOMP. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CX " IDENTIFIER_DIGITS = 4 @@ -4188,7 +4188,7 @@ class CX(Register): ) @property - def codigo_newave(self) -> Optional[int]: + def codigo_newave(self) -> int | None: """ O código da usina no NEWAVE @@ -4198,11 +4198,11 @@ def codigo_newave(self) -> Optional[int]: return self.data[0] @codigo_newave.setter - def codigo_newave(self, m: int): + def codigo_newave(self, m: int) -> None: self.data[0] = m @property - def codigo_decomp(self) -> Optional[int]: + def codigo_decomp(self) -> int | None: """ O código da usina no DECOMP @@ -4212,7 +4212,7 @@ def codigo_decomp(self) -> Optional[int]: return self.data[1] @codigo_decomp.setter - def codigo_decomp(self, m: int): + def codigo_decomp(self, m: int) -> None: self.data[1] = m @@ -4221,7 +4221,7 @@ class FA(Register): Registro que indica o nome do arquivo índice CSV. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "FA " IDENTIFIER_DIGITS = 4 @@ -4232,7 +4232,7 @@ class FA(Register): ) @property - def arquivo(self) -> Optional[str]: + def arquivo(self) -> str | None: """ O nome do arquivo índice CSV. @@ -4242,7 +4242,7 @@ def arquivo(self) -> Optional[str]: return self.data[0] @arquivo.setter - def arquivo(self, m: str): + def arquivo(self, m: str) -> None: self.data[0] = m @@ -4251,7 +4251,7 @@ class VT(Register): Registro que indica o nome do arquivo com cenários de vento. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "VT " IDENTIFIER_DIGITS = 4 @@ -4262,7 +4262,7 @@ class VT(Register): ) @property - def arquivo(self) -> Optional[str]: + def arquivo(self) -> str | None: """ O nome do arquivo com cenários de vento. @@ -4272,7 +4272,7 @@ def arquivo(self) -> Optional[str]: return self.data[0] @arquivo.setter - def arquivo(self, m: str): + def arquivo(self, m: str) -> None: self.data[0] = m @@ -4281,7 +4281,7 @@ class CS(Register): Registro que habilita a consistência de dados. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "CS " IDENTIFIER_DIGITS = 4 @@ -4292,7 +4292,7 @@ class CS(Register): ) @property - def consistencia(self) -> Optional[int]: + def consistencia(self) -> int | None: """ Habilita ou não a consistência de dados. @@ -4302,7 +4302,7 @@ def consistencia(self) -> Optional[int]: return self.data[0] @consistencia.setter - def consistencia(self, m: int): + def consistencia(self, m: int) -> None: self.data[0] = m @@ -4311,7 +4311,7 @@ class ACNUMPOS(Register): Registro AC específico para alteração no número do posto. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) NUMPOS" IDENTIFIER_DIGITS = 15 @@ -4326,7 +4326,9 @@ class ACNUMPOS(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4338,43 +4340,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def codigo_posto(self) -> Optional[int]: + def codigo_posto(self) -> int | None: return self.data[1] @codigo_posto.setter - def codigo_posto(self, u: int): + def codigo_posto(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4383,7 +4385,7 @@ class ACNUMJUS(Register): Registro AC específico para alteração na usina de jusante. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) NUMJUS" IDENTIFIER_DIGITS = 15 @@ -4398,7 +4400,9 @@ class ACNUMJUS(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4410,7 +4414,7 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O número da UHE conforme registro UH. @@ -4419,11 +4423,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def codigo_usina_jusante(self) -> Optional[int]: + def codigo_usina_jusante(self) -> int | None: """ O número da UHE de jusante conforme registro UH. @@ -4432,11 +4436,11 @@ def codigo_usina_jusante(self) -> Optional[int]: return self.data[1] @codigo_usina_jusante.setter - def codigo_usina_jusante(self, u: int): + def codigo_usina_jusante(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: """ O mês de validade da alteração de cadastro. @@ -4445,11 +4449,11 @@ def mes(self) -> Optional[str]: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: """ A semana de validade da alteração de cadastro. @@ -4458,11 +4462,11 @@ def semana(self) -> Optional[int]: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: """ O ano de validade da alteração de cadastro. @@ -4471,7 +4475,7 @@ def ano(self) -> Optional[int]: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4481,7 +4485,7 @@ class ACDESVIO(Register): para canal de desvio e limite da vazão no canal. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) DESVIO" IDENTIFIER_DIGITS = 15 @@ -4497,7 +4501,9 @@ class ACDESVIO(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4509,51 +4515,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def codigo_usina_jusante(self) -> Optional[int]: + def codigo_usina_jusante(self) -> int | None: return self.data[1] @codigo_usina_jusante.setter - def codigo_usina_jusante(self, u: int): + def codigo_usina_jusante(self, u: int) -> None: self.data[1] = u @property - def limite_vazao(self) -> Optional[float]: + def limite_vazao(self) -> float | None: return self.data[2] @limite_vazao.setter - def limite_vazao(self, u: float): + def limite_vazao(self, u: float) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4562,7 +4568,7 @@ class ACVOLMIN(Register): Registro AC específico para alteração de volume mínimo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VOLMIN" IDENTIFIER_DIGITS = 15 @@ -4577,7 +4583,9 @@ class ACVOLMIN(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4589,43 +4597,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def volume(self) -> Optional[float]: + def volume(self) -> float | None: return self.data[1] @volume.setter - def volume(self, u: float): + def volume(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4634,7 +4642,7 @@ class ACVOLMAX(Register): Registro AC específico para alteração de volume máximo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VOLMAX" IDENTIFIER_DIGITS = 15 @@ -4649,7 +4657,9 @@ class ACVOLMAX(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4661,43 +4671,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def volume(self) -> Optional[float]: + def volume(self) -> float | None: return self.data[1] @volume.setter - def volume(self, u: float): + def volume(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4707,7 +4717,7 @@ class ACCOTVOL(Register): polinômio cota-volume. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) COTVOL" IDENTIFIER_DIGITS = 15 @@ -4723,7 +4733,9 @@ class ACCOTVOL(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4735,51 +4747,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def ordem(self) -> Optional[int]: + def ordem(self) -> int | None: return self.data[1] @ordem.setter - def ordem(self, u: int): + def ordem(self, u: int) -> None: self.data[1] = u @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: return self.data[2] @coeficiente.setter - def coeficiente(self, u: float): + def coeficiente(self, u: float) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4789,7 +4801,7 @@ class ACCOTARE(Register): polinômio cota-área. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) COTARE" IDENTIFIER_DIGITS = 15 @@ -4797,7 +4809,7 @@ class ACCOTARE(Register): [ IntegerField(3, 4), IntegerField(5, 19), - FloatField(15, 24, 8, 'D'), + FloatField(15, 24, 8, "D"), LiteralField(3, 69), IntegerField(2, 73), IntegerField(4, 76), @@ -4805,7 +4817,9 @@ class ACCOTARE(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4817,51 +4831,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def ordem(self) -> Optional[int]: + def ordem(self) -> int | None: return self.data[1] @ordem.setter - def ordem(self, u: int): + def ordem(self, u: int) -> None: self.data[1] = u @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: return self.data[2] @coeficiente.setter - def coeficiente(self, u: float): + def coeficiente(self, u: float) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4871,7 +4885,7 @@ class ACPROESP(Register): hidráulicas em função da queda bruta (%,m,k). """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) PROESP" IDENTIFIER_DIGITS = 15 @@ -4886,7 +4900,9 @@ class ACPROESP(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4898,43 +4914,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def produtibilidade(self) -> Optional[float]: + def produtibilidade(self) -> float | None: return self.data[1] @produtibilidade.setter - def produtibilidade(self, u: float): + def produtibilidade(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -4944,7 +4960,7 @@ class ACPERHID(Register): hidráulicas em função da queda bruta (%,m,k). """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) PERHID" IDENTIFIER_DIGITS = 15 @@ -4959,7 +4975,9 @@ class ACPERHID(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -4971,43 +4989,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: return self.data[1] @coeficiente.setter - def coeficiente(self, u: float): + def coeficiente(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5017,7 +5035,7 @@ class ACNCHAVE(Register): (cota-vazão) e nível de jusante da faixa associada (m). """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) NCHAVE" IDENTIFIER_DIGITS = 15 @@ -5033,7 +5051,9 @@ class ACNCHAVE(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5045,51 +5065,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def numero_curva(self) -> Optional[int]: + def numero_curva(self) -> int | None: return self.data[1] @numero_curva.setter - def numero_curva(self, u: int): + def numero_curva(self, u: int) -> None: self.data[1] = u @property - def nivel(self) -> Optional[float]: + def nivel(self) -> float | None: return self.data[2] @nivel.setter - def nivel(self, u: float): + def nivel(self, u: float) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5099,7 +5119,7 @@ class ACCOTVAZ(Register): polinômio cota-vazão. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) COTVAZ" IDENTIFIER_DIGITS = 15 @@ -5116,7 +5136,9 @@ class ACCOTVAZ(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5128,59 +5150,59 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def indice_polimonio(self) -> Optional[int]: + def indice_polimonio(self) -> int | None: return self.data[1] @indice_polimonio.setter - def indice_polimonio(self, u: int): + def indice_polimonio(self, u: int) -> None: self.data[1] = u @property - def ordem(self) -> Optional[int]: + def ordem(self) -> int | None: return self.data[2] @ordem.setter - def ordem(self, u: int): + def ordem(self, u: int) -> None: self.data[2] = u @property - def coeficiente(self) -> Optional[float]: + def coeficiente(self) -> float | None: return self.data[3] @coeficiente.setter - def coeficiente(self, u: float): + def coeficiente(self, u: float) -> None: self.data[3] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5190,7 +5212,7 @@ class ACCOFEVA(Register): mensal para cada mês. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) COFEVA" IDENTIFIER_DIGITS = 15 @@ -5206,7 +5228,9 @@ class ACCOFEVA(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5218,51 +5242,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def mes_coeficiente(self) -> Optional[int]: + def mes_coeficiente(self) -> int | None: return self.data[1] @mes_coeficiente.setter - def mes_coeficiente(self, u: int): + def mes_coeficiente(self, u: int) -> None: self.data[1] = u @property - def coeficiente(self) -> Optional[int]: + def coeficiente(self) -> int | None: return self.data[2] @coeficiente.setter - def coeficiente(self, u: int): + def coeficiente(self, u: int) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5272,7 +5296,7 @@ class ACNUMCON(Register): de máquinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) NUMCON" IDENTIFIER_DIGITS = 15 @@ -5287,7 +5311,9 @@ class ACNUMCON(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5299,43 +5325,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def numero_conjuntos(self) -> Optional[int]: + def numero_conjuntos(self) -> int | None: return self.data[1] @numero_conjuntos.setter - def numero_conjuntos(self, u: int): + def numero_conjuntos(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5345,7 +5371,7 @@ class ACNUMMAQ(Register): em cada conjunto de máquinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) NUMMAQ" IDENTIFIER_DIGITS = 15 @@ -5361,7 +5387,9 @@ class ACNUMMAQ(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5373,51 +5401,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def indice_conjunto(self) -> Optional[int]: + def indice_conjunto(self) -> int | None: return self.data[1] @indice_conjunto.setter - def indice_conjunto(self, u: int): + def indice_conjunto(self, u: int) -> None: self.data[1] = u @property - def numero_maquinas(self) -> Optional[int]: + def numero_maquinas(self) -> int | None: return self.data[2] @numero_maquinas.setter - def numero_maquinas(self, u: int): + def numero_maquinas(self, u: int) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5427,7 +5455,7 @@ class ACPOTEFE(Register): por unidade geradora em um conjunto de máquinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) POTEFE" IDENTIFIER_DIGITS = 15 @@ -5443,7 +5471,9 @@ class ACPOTEFE(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5455,51 +5485,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def indice_conjunto(self) -> Optional[int]: + def indice_conjunto(self) -> int | None: return self.data[1] @indice_conjunto.setter - def indice_conjunto(self, u: int): + def indice_conjunto(self, u: int) -> None: self.data[1] = u @property - def potencia(self) -> Optional[float]: + def potencia(self) -> float | None: return self.data[2] @potencia.setter - def potencia(self, u: float): + def potencia(self, u: float) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5509,7 +5539,7 @@ class ACALTEFE(Register): de queda para um conjunto de máquinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) ALTEFE" IDENTIFIER_DIGITS = 15 @@ -5525,7 +5555,9 @@ class ACALTEFE(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5537,35 +5569,35 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5575,7 +5607,7 @@ class ACVAZEFE(Register): para um conjunto de máquinas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VAZEFE" IDENTIFIER_DIGITS = 15 @@ -5591,7 +5623,9 @@ class ACVAZEFE(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5603,51 +5637,51 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def indice_conjunto(self) -> Optional[int]: + def indice_conjunto(self) -> int | None: return self.data[1] @indice_conjunto.setter - def indice_conjunto(self, u: int): + def indice_conjunto(self, u: int) -> None: self.data[1] = u @property - def vazao(self) -> Optional[int]: + def vazao(self) -> int | None: return self.data[2] @vazao.setter - def vazao(self, u: int): + def vazao(self, u: int) -> None: self.data[2] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5657,7 +5691,7 @@ class ACJUSMED(Register): de fuga em metros. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) JUSMED" IDENTIFIER_DIGITS = 15 @@ -5672,7 +5706,9 @@ class ACJUSMED(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5684,43 +5720,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def cota(self) -> Optional[float]: + def cota(self) -> float | None: return self.data[1] @cota.setter - def cota(self, u: float): + def cota(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5730,7 +5766,7 @@ class ACVERTJU(Register): no canal de fuga. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VERTJU" IDENTIFIER_DIGITS = 15 @@ -5745,7 +5781,9 @@ class ACVERTJU(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5757,43 +5795,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def considera_influencia(self) -> Optional[int]: + def considera_influencia(self) -> int | None: return self.data[1] @considera_influencia.setter - def considera_influencia(self, u: int): + def considera_influencia(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5802,7 +5840,7 @@ class ACVAZMIN(Register): Registro AC específico para alteração da vazão mínima. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VAZMIN" IDENTIFIER_DIGITS = 15 @@ -5817,7 +5855,9 @@ class ACVAZMIN(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5829,43 +5869,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def vazao(self) -> Optional[int]: + def vazao(self) -> int | None: return self.data[1] @vazao.setter - def vazao(self, u: int): + def vazao(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5874,7 +5914,7 @@ class ACTIPERH(Register): Registro AC específico para alteração do tipo de perdas hidráulicas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) TIPERH" IDENTIFIER_DIGITS = 15 @@ -5889,7 +5929,9 @@ class ACTIPERH(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5901,43 +5943,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def tipo_perda(self) -> Optional[int]: + def tipo_perda(self) -> int | None: return self.data[1] @tipo_perda.setter - def tipo_perda(self, u: int): + def tipo_perda(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -5948,7 +5990,7 @@ class ACJUSENA(Register): armazenada e afluente. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) JUSENA" IDENTIFIER_DIGITS = 15 @@ -5963,7 +6005,9 @@ class ACJUSENA(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -5975,43 +6019,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def codigo_usina_jusante(self) -> Optional[int]: + def codigo_usina_jusante(self) -> int | None: return self.data[1] @codigo_usina_jusante.setter - def codigo_usina_jusante(self, u: int): + def codigo_usina_jusante(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -6021,7 +6065,7 @@ class ACVSVERT(Register): do vertedor. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VSVERT" IDENTIFIER_DIGITS = 15 @@ -6036,7 +6080,9 @@ class ACVSVERT(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -6048,43 +6094,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def volume(self) -> Optional[float]: + def volume(self) -> float | None: return self.data[1] @volume.setter - def volume(self, u: float): + def volume(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -6094,7 +6140,7 @@ class ACVMDESV(Register): do canal de desvio. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) VMDESV" IDENTIFIER_DIGITS = 15 @@ -6109,7 +6155,9 @@ class ACVMDESV(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -6121,43 +6169,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def volume(self) -> Optional[float]: + def volume(self) -> float | None: return self.data[1] @volume.setter - def volume(self, u: float): + def volume(self, u: float) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -6167,7 +6215,7 @@ class ACNPOSNW(Register): com o NEWAVE. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = r"AC ([\d ]{1,3}) NPOSNW" IDENTIFIER_DIGITS = 15 @@ -6182,7 +6230,9 @@ class ACNPOSNW(Register): ) # Override - def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: + def write( + self, file: IO[Any], storage: str = "", *args: Any, **kwargs: Any + ) -> bool: line = self.__class__.LINE.write(self.data) line = ( self.__class__.IDENTIFIER[:2] # type: ignore @@ -6194,43 +6244,43 @@ def write(self, file: IO, storage: str = "", *args, **kwargs) -> bool: return True @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: return self.data[0] @codigo_usina.setter - def codigo_usina(self, u: int): + def codigo_usina(self, u: int) -> None: self.data[0] = u @property - def codigo_posto(self) -> Optional[int]: + def codigo_posto(self) -> int | None: return self.data[1] @codigo_posto.setter - def codigo_posto(self, u: int): + def codigo_posto(self, u: int) -> None: self.data[1] = u @property - def mes(self) -> Optional[str]: + def mes(self) -> str | None: return self.data[-3] @mes.setter - def mes(self, m: str): + def mes(self, m: str) -> None: self.data[-3] = m @property - def semana(self) -> Optional[int]: + def semana(self) -> int | None: return self.data[-2] @semana.setter - def semana(self, s: int): + def semana(self, s: int) -> None: self.data[-2] = s @property - def ano(self) -> Optional[int]: + def ano(self) -> int | None: return self.data[-1] @ano.setter - def ano(self, m: int): + def ano(self, m: int) -> None: self.data[-1] = m @@ -6240,7 +6290,7 @@ class VL(Register): na cota de jusante. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "VL " IDENTIFIER_DIGITS = 4 @@ -6267,7 +6317,7 @@ class VL(Register): ) @property - def codigo_usina_influenciada(self) -> Optional[int]: + def codigo_usina_influenciada(self) -> int | None: """ O código da UHE influenciada. @@ -6277,11 +6327,11 @@ def codigo_usina_influenciada(self) -> Optional[int]: return self.data[0] @codigo_usina_influenciada.setter - def codigo_usina_influenciada(self, c: int): + def codigo_usina_influenciada(self, c: int) -> None: self.data[0] = c @property - def fator_impacto_defluencia(self) -> Optional[float]: + def fator_impacto_defluencia(self) -> float | None: """ O fator de impacto referente a defluência da usina influenciada. @@ -6291,11 +6341,11 @@ def fator_impacto_defluencia(self) -> Optional[float]: return self.data[1] @fator_impacto_defluencia.setter - def fator_impacto_defluencia(self, c: float): + def fator_impacto_defluencia(self, c: float) -> None: self.data[1] = c @property - def coeficiente_a0(self) -> Optional[float]: + def coeficiente_a0(self) -> float | None: """ O coeficiente de grau 0 do polinômio de jusante. @@ -6305,11 +6355,11 @@ def coeficiente_a0(self) -> Optional[float]: return self.data[2] @coeficiente_a0.setter - def coeficiente_a0(self, c: float): + def coeficiente_a0(self, c: float) -> None: self.data[2] = c @property - def coeficiente_a1(self) -> Optional[float]: + def coeficiente_a1(self) -> float | None: """ O coeficiente de grau 1 do polinômio de jusante. @@ -6319,11 +6369,11 @@ def coeficiente_a1(self) -> Optional[float]: return self.data[3] @coeficiente_a1.setter - def coeficiente_a1(self, c: float): + def coeficiente_a1(self, c: float) -> None: self.data[3] = c @property - def coeficiente_a2(self) -> Optional[float]: + def coeficiente_a2(self) -> float | None: """ O coeficiente de grau 2 do polinômio de jusante. @@ -6333,11 +6383,11 @@ def coeficiente_a2(self) -> Optional[float]: return self.data[4] @coeficiente_a2.setter - def coeficiente_a2(self, c: float): + def coeficiente_a2(self, c: float) -> None: self.data[4] = c @property - def coeficiente_a3(self) -> Optional[float]: + def coeficiente_a3(self) -> float | None: """ O coeficiente de grau 3 do polinômio de jusante. @@ -6347,11 +6397,11 @@ def coeficiente_a3(self) -> Optional[float]: return self.data[5] @coeficiente_a3.setter - def coeficiente_a3(self, c: float): + def coeficiente_a3(self, c: float) -> None: self.data[5] = c @property - def coeficiente_a4(self) -> Optional[float]: + def coeficiente_a4(self) -> float | None: """ O coeficiente de grau 4 do polinômio de jusante. @@ -6361,7 +6411,7 @@ def coeficiente_a4(self) -> Optional[float]: return self.data[6] @coeficiente_a4.setter - def coeficiente_a4(self, c: float): + def coeficiente_a4(self, c: float) -> None: self.data[6] = c @@ -6371,7 +6421,7 @@ class VU(Register): jusante da primeira. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "VU " IDENTIFIER_DIGITS = 4 @@ -6384,7 +6434,7 @@ class VU(Register): ) @property - def codigo_usina_influenciada(self) -> Optional[int]: + def codigo_usina_influenciada(self) -> int | None: """ O código da UHE influenciada. @@ -6394,11 +6444,11 @@ def codigo_usina_influenciada(self) -> Optional[int]: return self.data[0] @codigo_usina_influenciada.setter - def codigo_usina_influenciada(self, c: int): + def codigo_usina_influenciada(self, c: int) -> None: self.data[0] = c @property - def codigo_usina_influenciadora(self) -> Optional[int]: + def codigo_usina_influenciadora(self) -> int | None: """ O código da UHE cuja vazão defluente influencia lateralmente. @@ -6409,11 +6459,11 @@ def codigo_usina_influenciadora(self) -> Optional[int]: return self.data[1] @codigo_usina_influenciadora.setter - def codigo_usina_influenciadora(self, c: int): + def codigo_usina_influenciadora(self, c: int) -> None: self.data[1] = c @property - def fator_impacto_defluencia(self) -> Optional[float]: + def fator_impacto_defluencia(self) -> float | None: """ O fator de impacto referente a defluência da usina influenciadora. @@ -6423,7 +6473,7 @@ def fator_impacto_defluencia(self) -> Optional[float]: return self.data[2] @fator_impacto_defluencia.setter - def fator_impacto_defluencia(self, c: float): + def fator_impacto_defluencia(self, c: float) -> None: self.data[2] = c @@ -6433,7 +6483,7 @@ class VA(Register): jusante da primeira. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "VA " IDENTIFIER_DIGITS = 4 @@ -6446,7 +6496,7 @@ class VA(Register): ) @property - def codigo_usina_influenciada(self) -> Optional[int]: + def codigo_usina_influenciada(self) -> int | None: """ O código da UHE influenciada. @@ -6456,11 +6506,11 @@ def codigo_usina_influenciada(self) -> Optional[int]: return self.data[0] @codigo_usina_influenciada.setter - def codigo_usina_influenciada(self, c: int): + def codigo_usina_influenciada(self, c: int) -> None: self.data[0] = c @property - def codigo_posto_influenciador(self) -> Optional[int]: + def codigo_posto_influenciador(self) -> int | None: """ O código do posto cuja vazão incremental influencia lateralmente. @@ -6471,11 +6521,11 @@ def codigo_posto_influenciador(self) -> Optional[int]: return self.data[1] @codigo_posto_influenciador.setter - def codigo_posto_influenciador(self, c: int): + def codigo_posto_influenciador(self, c: int) -> None: self.data[1] = c @property - def fator_impacto_incremental(self) -> Optional[float]: + def fator_impacto_incremental(self) -> float | None: """ O fator de impacto referente a incremental do posto influenciador. @@ -6485,5 +6535,5 @@ def fator_impacto_incremental(self) -> Optional[float]: return self.data[2] @fator_impacto_incremental.setter - def fator_impacto_incremental(self, c: float): + def fator_impacto_incremental(self, c: float) -> None: self.data[2] = c diff --git a/idecomp/decomp/modelos/dadgnl.py b/idecomp/decomp/modelos/dadgnl.py index 318ff2e..6724797 100644 --- a/idecomp/decomp/modelos/dadgnl.py +++ b/idecomp/decomp/modelos/dadgnl.py @@ -1,10 +1,10 @@ -from cfinterface.components.register import Register -from cfinterface.components.line import Line +from typing import Any + +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - -from typing import List, Optional +from cfinterface.components.register import Register class TG(Register): @@ -12,7 +12,7 @@ class TG(Register): Registro que contém o cadastro das térmicas a GNL """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "TG " IDENTIFIER_DIGITS = 4 @@ -42,10 +42,10 @@ class TG(Register): def __atualiza_dados_lista( self, - novos_dados: list, + novos_dados: list[Any], indice_inicial: int, espacamento: int, - ): + ) -> None: atuais = len(self.data) ultimo_indice = indice_inicial + espacamento * len(novos_dados) diferenca = (ultimo_indice - atuais) // espacamento @@ -56,7 +56,7 @@ def __atualiza_dados_lista( self.data[indice_inicial::espacamento] = novos_dados @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código de cadastro da UTE. @@ -66,11 +66,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado de cadastro da UTE. @@ -79,11 +79,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, c: int): + def codigo_submercado(self, c: int) -> None: self.data[1] = c @property - def nome(self) -> Optional[str]: + def nome(self) -> str | None: """ O nome de cadastro da UTE. @@ -93,11 +93,11 @@ def nome(self) -> Optional[str]: return self.data[2] @nome.setter - def nome(self, nome: str): + def nome(self, nome: str) -> None: self.data[2] = nome @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio do despacho da UTE. @@ -107,11 +107,11 @@ def estagio(self) -> Optional[int]: return self.data[3] @estagio.setter - def estagio(self, estagio: int): + def estagio(self, estagio: int) -> None: self.data[3] = estagio @property - def inflexibilidade(self) -> List[float]: + def inflexibilidade(self) -> list[float]: """ A inflexibilidade da UTE por patamar. @@ -121,11 +121,11 @@ def inflexibilidade(self) -> List[float]: return [v for v in self.data[4::3] if v is not None] @inflexibilidade.setter - def inflexibilidade(self, inflex: List[float]): + def inflexibilidade(self, inflex: list[float]) -> None: self.__atualiza_dados_lista(inflex, 4, 3) @property - def disponibilidade(self) -> List[float]: + def disponibilidade(self) -> list[float]: """ A disponibilidade da UTE por patamar. @@ -135,11 +135,11 @@ def disponibilidade(self) -> List[float]: return [v for v in self.data[5::3] if v is not None] @disponibilidade.setter - def disponibilidade(self, disp: List[float]): + def disponibilidade(self, disp: list[float]) -> None: self.__atualiza_dados_lista(disp, 5, 3) @property - def cvu(self) -> List[float]: + def cvu(self) -> list[float]: """ Os CVUs da UTE por patamar. @@ -149,7 +149,7 @@ def cvu(self) -> List[float]: return [v for v in self.data[6::3] if v is not None] @cvu.setter - def cvu(self, cvu: List[float]): + def cvu(self, cvu: list[float]) -> None: self.__atualiza_dados_lista(cvu, 6, 3) @@ -159,7 +159,7 @@ class GS(Register): no estudo. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "GS " IDENTIFIER_DIGITS = 4 @@ -171,7 +171,7 @@ class GS(Register): ) @property - def mes(self) -> Optional[int]: + def mes(self) -> int | None: """ O índice do mês associado ao registro GS @@ -181,11 +181,11 @@ def mes(self) -> Optional[int]: return self.data[0] @mes.setter - def mes(self, m: int): + def mes(self, m: int) -> None: self.data[0] = m @property - def semanas(self) -> Optional[int]: + def semanas(self) -> int | None: """ O número de semanas do mês associado ao registro GS @@ -195,7 +195,7 @@ def semanas(self) -> Optional[int]: return self.data[1] @semanas.setter - def semanas(self, s: int): + def semanas(self, s: int) -> None: self.data[1] = s @@ -205,7 +205,7 @@ class NL(Register): de despacho antecipado em cada subsistema. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "NL " IDENTIFIER_DIGITS = 4 @@ -218,7 +218,7 @@ class NL(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UTE associada ao registro NL @@ -228,11 +228,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado de despacho da UTE @@ -242,11 +242,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, s: int): + def codigo_submercado(self, s: int) -> None: self.data[1] = s @property - def lag(self) -> Optional[int]: + def lag(self) -> int | None: """ O lag de despacho da UTE @@ -256,7 +256,7 @@ def lag(self) -> Optional[int]: return self.data[2] @lag.setter - def lag(self, lag: int): + def lag(self, lag: int) -> None: self.data[2] = lag @@ -265,7 +265,7 @@ class GL(Register): Registro que contém os cadastros de restrições elétricas. """ - __slots__ = [] + __slots__: list[str] = [] IDENTIFIER = "GL " IDENTIFIER_DIGITS = 4 @@ -285,7 +285,7 @@ class GL(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da UTE despachada no registro GL @@ -295,11 +295,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def codigo_submercado(self) -> Optional[int]: + def codigo_submercado(self) -> int | None: """ O código do submercado de despacho da UTE @@ -309,11 +309,11 @@ def codigo_submercado(self) -> Optional[int]: return self.data[1] @codigo_submercado.setter - def codigo_submercado(self, e: int): + def codigo_submercado(self, e: int) -> None: self.data[1] = e @property - def estagio(self) -> Optional[int]: + def estagio(self) -> int | None: """ O estágio de despacho da UTE @@ -323,11 +323,11 @@ def estagio(self) -> Optional[int]: return self.data[2] @estagio.setter - def estagio(self, e: int): + def estagio(self, e: int) -> None: self.data[2] = e @property - def geracao(self) -> List[float]: + def geracao(self) -> list[float]: """ Os valores de geração por patamar para o despacho da UTE @@ -337,7 +337,7 @@ def geracao(self) -> List[float]: return [v for v in self.data[3:8:2] if v is not None] @geracao.setter - def geracao(self, gers: List[float]): + def geracao(self, gers: list[float]) -> None: novos = len(gers) atuais = len(self.geracao) if novos != atuais: @@ -348,7 +348,7 @@ def geracao(self, gers: List[float]): self.data[3:9:2] = gers @property - def duracao(self) -> List[float]: + def duracao(self) -> list[float]: """ As durações de cada patamar para o despacho da UTE @@ -358,7 +358,7 @@ def duracao(self) -> List[float]: return [v for v in self.data[4:9:2] if v is not None] @duracao.setter - def duracao(self, durs: List[float]): + def duracao(self, durs: list[float]) -> None: novos = len(durs) atuais = len(self.duracao) if novos != atuais: @@ -369,7 +369,7 @@ def duracao(self, durs: List[float]): self.data[4:9:2] = durs @property - def data_inicio(self) -> Optional[str]: + def data_inicio(self) -> str | None: """ A data de despacho da UTE @@ -379,5 +379,5 @@ def data_inicio(self) -> Optional[str]: return self.data[9] @data_inicio.setter - def data_inicio(self, d: str): + def data_inicio(self, d: str) -> None: self.data[9] = d diff --git a/idecomp/decomp/modelos/dec_avl_evap.py b/idecomp/decomp/modelos/dec_avl_evap.py index 80adece..62763ad 100644 --- a/idecomp/decomp/modelos/dec_avl_evap.py +++ b/idecomp/decomp/modelos/dec_avl_evap.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_cortes_evap.py b/idecomp/decomp/modelos/dec_cortes_evap.py index 473ad65..403267f 100644 --- a/idecomp/decomp/modelos/dec_cortes_evap.py +++ b/idecomp/decomp/modelos/dec_cortes_evap.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_desvfpha.py b/idecomp/decomp/modelos/dec_desvfpha.py index 9d4daee..5b65d85 100644 --- a/idecomp/decomp/modelos/dec_desvfpha.py +++ b/idecomp/decomp/modelos/dec_desvfpha.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_eco_cotajus.py b/idecomp/decomp/modelos/dec_eco_cotajus.py index 51e2238..83ed7f4 100644 --- a/idecomp/decomp/modelos/dec_eco_cotajus.py +++ b/idecomp/decomp/modelos/dec_eco_cotajus.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_eco_discr.py b/idecomp/decomp/modelos/dec_eco_discr.py index b8ebfaa..2060bc2 100644 --- a/idecomp/decomp/modelos/dec_eco_discr.py +++ b/idecomp/decomp/modelos/dec_eco_discr.py @@ -1,8 +1,7 @@ # Imports de módulos externos -from cfinterface.components.line import Line -from cfinterface.components.integerfield import IntegerField from cfinterface.components.floatfield import FloatField - +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_eco_evap.py b/idecomp/decomp/modelos/dec_eco_evap.py index ee93517..5459a55 100644 --- a/idecomp/decomp/modelos/dec_eco_evap.py +++ b/idecomp/decomp/modelos/dec_eco_evap.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_eco_qlat.py b/idecomp/decomp/modelos/dec_eco_qlat.py index aa4a376..7dff510 100644 --- a/idecomp/decomp/modelos/dec_eco_qlat.py +++ b/idecomp/decomp/modelos/dec_eco_qlat.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_estatevap.py b/idecomp/decomp/modelos/dec_estatevap.py index b1dfa0c..f24503c 100644 --- a/idecomp/decomp/modelos/dec_estatevap.py +++ b/idecomp/decomp/modelos/dec_estatevap.py @@ -1,8 +1,7 @@ # Imports de módulos externos -from cfinterface.components.line import Line -from cfinterface.components.integerfield import IntegerField from cfinterface.components.floatfield import FloatField - +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_estatfpha.py b/idecomp/decomp/modelos/dec_estatfpha.py index 3bfab3f..da23088 100644 --- a/idecomp/decomp/modelos/dec_estatfpha.py +++ b/idecomp/decomp/modelos/dec_estatfpha.py @@ -1,6 +1,7 @@ +from typing import IO, Any + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block -from typing import List, IO -import pandas as pd # type: ignore class BlocoDesvios(Block): @@ -27,13 +28,13 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(data={"variavel": variavel, "valor": valores}) return df - variavel: List[str] = [] - valores: List[float] = [] + variavel: list[str] = [] + valores: list[float] = [] while True: linha = file.readline() diff --git a/idecomp/decomp/modelos/dec_fcf_cortes.py b/idecomp/decomp/modelos/dec_fcf_cortes.py index 272e32b..fe5c693 100644 --- a/idecomp/decomp/modelos/dec_fcf_cortes.py +++ b/idecomp/decomp/modelos/dec_fcf_cortes.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_evap.py b/idecomp/decomp/modelos/dec_oper_evap.py index 1279082..a913f68 100644 --- a/idecomp/decomp/modelos/dec_oper_evap.py +++ b/idecomp/decomp/modelos/dec_oper_evap.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_gnl.py b/idecomp/decomp/modelos/dec_oper_gnl.py index 9938153..316338d 100644 --- a/idecomp/decomp/modelos/dec_oper_gnl.py +++ b/idecomp/decomp/modelos/dec_oper_gnl.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_interc.py b/idecomp/decomp/modelos/dec_oper_interc.py index c636ec0..0257793 100644 --- a/idecomp/decomp/modelos/dec_oper_interc.py +++ b/idecomp/decomp/modelos/dec_oper_interc.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_ree.py b/idecomp/decomp/modelos/dec_oper_ree.py index bb0b671..1cbda16 100644 --- a/idecomp/decomp/modelos/dec_oper_ree.py +++ b/idecomp/decomp/modelos/dec_oper_ree.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_rhesoft.py b/idecomp/decomp/modelos/dec_oper_rhesoft.py index 703ca22..e4e7f2c 100644 --- a/idecomp/decomp/modelos/dec_oper_rhesoft.py +++ b/idecomp/decomp/modelos/dec_oper_rhesoft.py @@ -1,8 +1,7 @@ # Imports de módulos externos -from cfinterface.components.line import Line -from cfinterface.components.integerfield import IntegerField from cfinterface.components.floatfield import FloatField - +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_sist.py b/idecomp/decomp/modelos/dec_oper_sist.py index e0f5862..9b852f7 100644 --- a/idecomp/decomp/modelos/dec_oper_sist.py +++ b/idecomp/decomp/modelos/dec_oper_sist.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_usie.py b/idecomp/decomp/modelos/dec_oper_usie.py index 26251a0..d0adc8a 100644 --- a/idecomp/decomp/modelos/dec_oper_usie.py +++ b/idecomp/decomp/modelos/dec_oper_usie.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_usih.py b/idecomp/decomp/modelos/dec_oper_usih.py index a14af4d..71704d6 100644 --- a/idecomp/decomp/modelos/dec_oper_usih.py +++ b/idecomp/decomp/modelos/dec_oper_usih.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/dec_oper_usit.py b/idecomp/decomp/modelos/dec_oper_usit.py index 391a2f6..f312b03 100644 --- a/idecomp/decomp/modelos/dec_oper_usit.py +++ b/idecomp/decomp/modelos/dec_oper_usit.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/decomptim.py b/idecomp/decomp/modelos/decomptim.py index cdcd8ad..ba34eb1 100644 --- a/idecomp/decomp/modelos/decomptim.py +++ b/idecomp/decomp/modelos/decomptim.py @@ -1,10 +1,11 @@ # Imports de módulos externos +from datetime import timedelta +from typing import IO, Any + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block -from cfinterface.components.line import Line from cfinterface.components.integerfield import IntegerField -import pandas as pd # type: ignore -from typing import IO -from datetime import timedelta +from cfinterface.components.line import Line class BlocoTemposEtapas(Block): @@ -18,7 +19,9 @@ class BlocoTemposEtapas(Block): BEGIN_PATTERN = "RELATORIO DE CONVERGENCIA DO PROCESSO ITERATIVO" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -47,7 +50,7 @@ def read_line_time(self, linha: str) -> timedelta: return timedelta(hours=data[0], minutes=data[1], seconds=data[2]) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame( diff --git a/idecomp/decomp/modelos/eco_fpha.py b/idecomp/decomp/modelos/eco_fpha.py index 8f0c1b3..a7d4a1d 100644 --- a/idecomp/decomp/modelos/eco_fpha.py +++ b/idecomp/decomp/modelos/eco_fpha.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSV diff --git a/idecomp/decomp/modelos/fcfnw.py b/idecomp/decomp/modelos/fcfnw.py index 2b67b3e..4b599c3 100644 --- a/idecomp/decomp/modelos/fcfnw.py +++ b/idecomp/decomp/modelos/fcfnw.py @@ -1,13 +1,14 @@ # Imports do próprio módulo # Imports de módulos externos +from typing import IO, Any + +import numpy as np +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block -from cfinterface.components.line import Line -from cfinterface.components.integerfield import IntegerField from cfinterface.components.floatfield import FloatField -import numpy as np # type: ignore -import pandas as pd # type: ignore -from typing import IO +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line class BlocoCortesFCF(Block): @@ -36,7 +37,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: if ree: colunas = ["corte", "RHS", "REE", "coef_earm"] + [ @@ -124,7 +125,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if len(linha) < 5: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break diff --git a/idecomp/decomp/modelos/hidr.py b/idecomp/decomp/modelos/hidr.py index 4f196c6..9f55617 100644 --- a/idecomp/decomp/modelos/hidr.py +++ b/idecomp/decomp/modelos/hidr.py @@ -1,9 +1,8 @@ -from cfinterface.components.register import Register +from cfinterface.components.floatfield import FloatField +from cfinterface.components.integerfield import IntegerField from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.integerfield import IntegerField -from cfinterface.components.floatfield import FloatField -from typing import List +from cfinterface.components.register import Register class RegistroUHEHidr(Register): @@ -12,7 +11,7 @@ class RegistroUHEHidr(Register): arquivo HIDR. """ - __slots__ = [] + __slots__: list[str] = [] LINE = Line( [ @@ -146,7 +145,7 @@ def nome(self) -> str: return self.data[0] @nome.setter - def nome(self, v: str): + def nome(self, v: str) -> None: self.data[0] = v @property @@ -154,7 +153,7 @@ def posto(self) -> int: return self.data[1] @posto.setter - def posto(self, v: int): + def posto(self, v: int) -> None: self.data[1] = v @property @@ -162,7 +161,7 @@ def subsistema(self) -> int: return self.data[3] @subsistema.setter - def subsistema(self, v: int): + def subsistema(self, v: int) -> None: self.data[3] = v @property @@ -170,7 +169,7 @@ def empresa(self) -> int: return self.data[4] @empresa.setter - def empresa(self, v: int): + def empresa(self, v: int) -> None: self.data[4] = v @property @@ -178,7 +177,7 @@ def jusante(self) -> int: return self.data[5] @jusante.setter - def jusante(self, v: int): + def jusante(self, v: int) -> None: self.data[5] = v @property @@ -186,7 +185,7 @@ def desvio(self) -> int: return self.data[6] @desvio.setter - def desvio(self, v: int): + def desvio(self, v: int) -> None: self.data[6] = v @property @@ -194,7 +193,7 @@ def volume_minimo(self) -> float: return self.data[7] @volume_minimo.setter - def volume_minimo(self, v: float): + def volume_minimo(self, v: float) -> None: self.data[7] = v @property @@ -202,7 +201,7 @@ def volume_maximo(self) -> float: return self.data[8] @volume_maximo.setter - def volume_maximo(self, v: float): + def volume_maximo(self, v: float) -> None: self.data[8] = v @property @@ -210,7 +209,7 @@ def volume_vertedouro(self) -> float: return self.data[9] @volume_vertedouro.setter - def volume_vertedouro(self, v: float): + def volume_vertedouro(self, v: float) -> None: self.data[9] = v @property @@ -218,7 +217,7 @@ def volume_desvio(self) -> float: return self.data[10] @volume_desvio.setter - def volume_desvio(self, v: float): + def volume_desvio(self, v: float) -> None: self.data[10] = v @property @@ -226,7 +225,7 @@ def cota_minima(self) -> float: return self.data[11] @cota_minima.setter - def cota_minima(self, v: float): + def cota_minima(self, v: float) -> None: self.data[11] = v @property @@ -234,31 +233,31 @@ def cota_maxima(self) -> float: return self.data[12] @cota_maxima.setter - def cota_maxima(self, v: float): + def cota_maxima(self, v: float) -> None: self.data[12] = v @property - def polinomio_volume_cota(self) -> List[float]: + def polinomio_volume_cota(self) -> list[float]: return self.data[13:18] @polinomio_volume_cota.setter - def polinomio_volume_cota(self, v: List[float]): + def polinomio_volume_cota(self, v: list[float]) -> None: self.data[13:18] = v @property - def polinomio_cota_area(self) -> List[float]: + def polinomio_cota_area(self) -> list[float]: return self.data[18:23] @polinomio_cota_area.setter - def polinomio_cota_area(self, v: List[float]): + def polinomio_cota_area(self, v: list[float]) -> None: self.data[18:23] = v @property - def evaporacao(self) -> List[float]: + def evaporacao(self) -> list[float]: return self.data[23:35] @evaporacao.setter - def evaporacao(self, v: List[float]): + def evaporacao(self, v: list[float]) -> None: self.data[23:35] = v @property @@ -266,39 +265,39 @@ def numero_conjuntos_maquinas(self) -> int: return self.data[35] @numero_conjuntos_maquinas.setter - def numero_conjuntos_maquinas(self, v: int): + def numero_conjuntos_maquinas(self, v: int) -> None: self.data[35] = v @property - def numero_maquinas_conjunto(self) -> List[int]: + def numero_maquinas_conjunto(self) -> list[int]: return self.data[36:41] @numero_maquinas_conjunto.setter - def numero_maquinas_conjunto(self, v: List[int]): + def numero_maquinas_conjunto(self, v: list[int]) -> None: self.data[36:41] = v @property - def potef_conjunto(self) -> List[float]: + def potef_conjunto(self) -> list[float]: return self.data[41:46] @potef_conjunto.setter - def potef_conjunto(self, v: List[float]): + def potef_conjunto(self, v: list[float]) -> None: self.data[41:46] = v @property - def hef_conjunto(self) -> List[float]: + def hef_conjunto(self) -> list[float]: return self.data[47:52] @hef_conjunto.setter - def hef_conjunto(self, v: List[float]): + def hef_conjunto(self, v: list[float]) -> None: self.data[47:52] = v @property - def qef_conjunto(self) -> List[float]: + def qef_conjunto(self) -> list[float]: return self.data[52:57] @qef_conjunto.setter - def qef_conjunto(self, v: List[float]): + def qef_conjunto(self, v: list[float]) -> None: self.data[52:57] = v @property @@ -306,7 +305,7 @@ def produtibilidade_especifica(self) -> float: return self.data[57] @produtibilidade_especifica.setter - def produtibilidade_especifica(self, v: float): + def produtibilidade_especifica(self, v: float) -> None: self.data[57] = v @property @@ -314,7 +313,7 @@ def perdas(self) -> float: return self.data[58] @perdas.setter - def perdas(self, v: float): + def perdas(self, v: float) -> None: self.data[58] = v @property @@ -322,15 +321,15 @@ def numero_polinomios_jusante(self) -> int: return self.data[59] @numero_polinomios_jusante.setter - def numero_polinomios_jusante(self, v: int): + def numero_polinomios_jusante(self, v: int) -> None: self.data[59] = v @property - def polinomios_jusante(self) -> List[float]: + def polinomios_jusante(self) -> list[float]: return self.data[60:96] @polinomios_jusante.setter - def polinomios_jusante(self, v: List[float]): + def polinomios_jusante(self, v: list[float]) -> None: self.data[60:96] = v @property @@ -338,7 +337,7 @@ def canal_fuga_medio(self) -> float: return self.data[96] @canal_fuga_medio.setter - def canal_fuga_medio(self, v: float): + def canal_fuga_medio(self, v: float) -> None: self.data[96] = v @property @@ -346,7 +345,7 @@ def influencia_vertimento_canal_fuga(self) -> int: return self.data[97] @influencia_vertimento_canal_fuga.setter - def influencia_vertimento_canal_fuga(self, v: int): + def influencia_vertimento_canal_fuga(self, v: int) -> None: self.data[97] = v @property @@ -354,7 +353,7 @@ def fator_carga_maximo(self) -> float: return self.data[98] @fator_carga_maximo.setter - def fator_carga_maximo(self, v: float): + def fator_carga_maximo(self, v: float) -> None: self.data[98] = v @property @@ -362,7 +361,7 @@ def fator_carga_minimo(self) -> float: return self.data[99] @fator_carga_minimo.setter - def fator_carga_minimo(self, v: float): + def fator_carga_minimo(self, v: float) -> None: self.data[99] = v @property @@ -370,7 +369,7 @@ def vazao_minima_historica(self) -> int: return self.data[100] @vazao_minima_historica.setter - def vazao_minima_historica(self, v: int): + def vazao_minima_historica(self, v: int) -> None: self.data[100] = v @property @@ -378,7 +377,7 @@ def numero_unidades_base(self) -> int: return self.data[101] @numero_unidades_base.setter - def numero_unidades_base(self, v: int): + def numero_unidades_base(self, v: int) -> None: self.data[101] = v @property @@ -386,7 +385,7 @@ def tipo_turbina(self) -> int: return self.data[102] @tipo_turbina.setter - def tipo_turbina(self, v: int): + def tipo_turbina(self, v: int) -> None: self.data[102] = v @property @@ -394,7 +393,7 @@ def representacao_conjunto(self) -> int: return self.data[103] @representacao_conjunto.setter - def representacao_conjunto(self, v: int): + def representacao_conjunto(self, v: int) -> None: self.data[103] = v @property @@ -402,7 +401,7 @@ def teif(self) -> float: return self.data[104] @teif.setter - def teif(self, v: float): + def teif(self, v: float) -> None: self.data[104] = v @property @@ -410,7 +409,7 @@ def ip(self) -> float: return self.data[105] @ip.setter - def ip(self, v: float): + def ip(self, v: float) -> None: self.data[105] = v @property @@ -418,7 +417,7 @@ def tipo_perda(self) -> int: return self.data[106] @tipo_perda.setter - def tipo_perda(self, v: int): + def tipo_perda(self, v: int) -> None: self.data[106] = v @property @@ -426,7 +425,7 @@ def data_referencia(self) -> str: return self.data[107] @data_referencia.setter - def data_referencia(self, v: str): + def data_referencia(self, v: str) -> None: self.data[107] = v @property @@ -434,7 +433,7 @@ def observacao(self) -> str: return self.data[108] @observacao.setter - def observacao(self, v: str): + def observacao(self, v: str) -> None: self.data[108] = v @property @@ -442,7 +441,7 @@ def volume_referencia(self) -> float: return self.data[109] @volume_referencia.setter - def volume_referencia(self, v: float): + def volume_referencia(self, v: float) -> None: self.data[109] = v @property @@ -450,5 +449,5 @@ def tipo_regulacao(self) -> str: return self.data[110] @tipo_regulacao.setter - def tipo_regulacao(self, v: str): + def tipo_regulacao(self, v: str) -> None: self.data[110] = v diff --git a/idecomp/decomp/modelos/inviabunic.py b/idecomp/decomp/modelos/inviabunic.py index ec5c153..4a5a8d6 100644 --- a/idecomp/decomp/modelos/inviabunic.py +++ b/idecomp/decomp/modelos/inviabunic.py @@ -1,10 +1,11 @@ +from typing import IO, Any + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block -from cfinterface.components.line import Line -from cfinterface.components.integerfield import IntegerField from cfinterface.components.floatfield import FloatField +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from typing import List, IO -import pandas as pd # type: ignore class BlocoInviabilidadesIteracoes(Block): @@ -18,7 +19,9 @@ class BlocoInviabilidadesIteracoes(Block): BEGIN_PATTERN = "RELATORIO DE VIOLACOES DAS RESTRICOES" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__linha = Line( [ @@ -47,7 +50,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame() df["iteracao"] = iteracoes @@ -62,13 +65,13 @@ def converte_tabela_em_df() -> pd.DataFrame: # Salta linhas de cabeçalho for _ in range(4): file.readline() - iteracoes: List[int] = [] - fwds_bwds: List[int] = [] - estagios: List[int] = [] - cenarios: List[int] = [] - restricoes: List[str] = [] - violacoes: List[float] = [] - unidades: List[str] = [] + iteracoes: list[int] = [] + fwds_bwds: list[int] = [] + estagios: list[int] = [] + cenarios: list[int] = [] + restricoes: list[str] = [] + violacoes: list[float] = [] + unidades: list[str] = [] while True: # Confere se a leitura não acabou linha = file.readline() @@ -97,7 +100,9 @@ class BlocoInviabilidadesSimFinal(Block): BEGIN_PATTERN = "SIMULACAO FINAL:" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__linha = Line( [ @@ -124,7 +129,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame() df["estagio"] = estagios @@ -138,11 +143,11 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(4): file.readline() - estagios: List[int] = [] - cenarios: List[int] = [] - restricoes: List[str] = [] - violacoes: List[float] = [] - unidades: List[str] = [] + estagios: list[int] = [] + cenarios: list[int] = [] + restricoes: list[str] = [] + violacoes: list[float] = [] + unidades: list[str] = [] while True: # Confere se a leitura não acabou linha = file.readline() diff --git a/idecomp/decomp/modelos/mapcut.py b/idecomp/decomp/modelos/mapcut.py index 8383e07..4e7e715 100644 --- a/idecomp/decomp/modelos/mapcut.py +++ b/idecomp/decomp/modelos/mapcut.py @@ -1,8 +1,9 @@ -from cfinterface.components.section import Section -from typing import IO, List -import numpy as np # type: ignore -import pandas as pd # type: ignore from datetime import datetime +from typing import IO, Any + +import numpy as np +import pandas as pd # type: ignore[import-untyped] +from cfinterface.components.section import Section class SecaoDadosMapcut(Section): @@ -15,7 +16,12 @@ class SecaoDadosMapcut(Section): REGISTER_SIZE = 48020 - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, + previous: Any = None, + next: Any = None, + data: Any = None, + ) -> None: super().__init__(previous, next, data) self.data = { "dados_gerais": [], # registro 1 @@ -44,7 +50,7 @@ def __eq__(self, o: object) -> bool: else: return self.data == bloco.data - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] # Leitura do primeiro registro (dados gerais) self.__le_primeiro_registro(file) # Leitura do segundo registro (dados do caso) @@ -62,7 +68,7 @@ def read(self, file: IO, *args, **kwargs): # Leitura do nono e décimo registro (dados de usinas gnl) self.__le_nono_decimo_registros(file) - def __le_primeiro_registro(self, file: IO): + def __le_primeiro_registro(self, file: IO[Any]) -> None: tamanho_primeiro_bloco = 5 dados_primeiro_bloco = np.frombuffer( file.read(tamanho_primeiro_bloco * 4), @@ -79,7 +85,7 @@ def __le_primeiro_registro(self, file: IO): dados = list(dados_primeiro_bloco) + list(dados_segundo_bloco) self.data["dados_gerais"] = list(dados) - def __le_segundo_registro(self, file: IO): + def __le_segundo_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho = 4 dados = np.frombuffer( @@ -90,7 +96,7 @@ def __le_segundo_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_caso"] = list(dados) - def __le_terceiro_registro(self, file: IO): + def __le_terceiro_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho = self.numero_uhes dados = np.frombuffer( @@ -101,7 +107,7 @@ def __le_terceiro_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_uhes"] = list(dados) - def __le_quarto_registro(self, file: IO): + def __le_quarto_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho = self.numero_uhes dados = np.frombuffer( @@ -112,7 +118,7 @@ def __le_quarto_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_uhes_topologia"] = list(dados) - def __le_quinto_registro(self, file: IO): + def __le_quinto_registro(self, file: IO[Any]) -> None: self.__contador_registro = self.__contador_registro + 14 file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho = self.numero_cenarios @@ -124,7 +130,7 @@ def __le_quinto_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_arvore"] = list(dados) - def __le_sexto_registro(self, file: IO): + def __le_sexto_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho_primeiro_bloco = 5 dados_primeiro_bloco = np.frombuffer( @@ -154,7 +160,7 @@ def __le_sexto_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_estagios"] = list(dados) - def __le_setimo_registro(self, file: IO): + def __le_setimo_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho_primeiro_bloco = 3 tamanho_segundo_bloco = 1 @@ -172,7 +178,7 @@ def __le_setimo_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_tempo_viagem"] += list(dados) - def __le_oitavo_registro(self, file: IO): + def __le_oitavo_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho = 1 dados_primeiro_bloco = np.frombuffer( @@ -189,7 +195,7 @@ def __le_oitavo_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_tempo_viagem"] += list(dados) - def __le_setimo_oitavo_registros(self, file: IO): + def __le_setimo_oitavo_registros(self, file: IO[Any]) -> None: for uhe in range(1, self.numero_uhes_tempo_viagem + 1, 1): for est in range(1, self.numero_estagios + 1, 1): lags = self.lag_tempo_viagem_por_uhe[(uhe * est) - 1] @@ -199,7 +205,7 @@ def __le_setimo_oitavo_registros(self, file: IO): else: self.__le_oitavo_registro(file) - def __le_nono_registro(self, file: IO): + def __le_nono_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho_primeiro_bloco = 1 dados_primeiro_bloco = np.frombuffer( @@ -228,7 +234,7 @@ def __le_nono_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_gnl"] += list(dados) - def __le_decimo_registro(self, file: IO): + def __le_decimo_registro(self, file: IO[Any]) -> None: file.seek(self.__contador_registro * self.REGISTER_SIZE) tamanho = 6 dados = np.frombuffer( @@ -239,7 +245,7 @@ def __le_decimo_registro(self, file: IO): self.__contador_registro = self.__contador_registro + 1 self.data["dados_custos"] += list(dados) - def __le_nono_decimo_registros(self, file: IO): + def __le_nono_decimo_registros(self, file: IO[Any]) -> None: for _ in range(self.numero_estagios): self.__le_nono_registro(file) self.__le_decimo_registro(file) @@ -266,7 +272,7 @@ def numero_cenarios(self) -> int: @property def registro_ultimo_corte_no(self) -> pd.DataFrame: - lista_estagios: List[int] = [] + lista_estagios: list[int] = [] estagio = 1 for no in range(1, self.numero_cenarios + 1): if no in self.indice_primeiro_no_estagio: @@ -297,15 +303,15 @@ def data_inicio(self) -> datetime: return datetime(year=ano, month=mes, day=dia) @property - def codigos_uhes(self) -> list: + def codigos_uhes(self) -> list[Any]: return self.data["dados_uhes"] @property - def codigos_uhes_jusante(self) -> list: + def codigos_uhes_jusante(self) -> list[Any]: return self.data["dados_uhes_topologia"] @property - def indice_no_arvore(self) -> list: + def indice_no_arvore(self) -> list[Any]: return self.data["dados_arvore"] @property @@ -325,14 +331,14 @@ def maximo_lag_tempo_viagem(self) -> int: return self.data["dados_estagios"][4] @property - def indice_primeiro_no_estagio(self) -> list: + def indice_primeiro_no_estagio(self) -> list[Any]: offset = 5 return self.data["dados_estagios"][ offset : (offset + self.numero_estagios) ] @property - def patamares_por_estagio(self) -> list: + def patamares_por_estagio(self) -> list[Any]: offset = 4 + self.numero_estagios + 1 patamares_estagio = self.data["dados_estagios"][ offset : (offset + self.numero_estagios) @@ -340,7 +346,7 @@ def patamares_por_estagio(self) -> list: return patamares_estagio @property - def lag_tempo_viagem_por_uhe(self) -> list: + def lag_tempo_viagem_por_uhe(self) -> list[Any]: return self.data["dados_estagios"][ -(self.numero_uhes_tempo_viagem * self.numero_estagios) : ] @@ -392,11 +398,11 @@ def dados_tempo_viagem(self) -> pd.DataFrame: return df_tempo_viagem.reset_index(drop=True) @property - def codigos_uhes_tempo_viagem(self) -> list: + def codigos_uhes_tempo_viagem(self) -> list[Any]: return self.dados_tempo_viagem["codigo_usina"].unique().tolist() @property - def codigos_submercados_gnl(self) -> list: + def codigos_submercados_gnl(self) -> list[Any]: codigos_submercados = ( self.dados_gnl["codigo_submercado"].unique().tolist() ) diff --git a/idecomp/decomp/modelos/oper_desvio_fpha.py b/idecomp/decomp/modelos/oper_desvio_fpha.py index 72321b0..b555091 100644 --- a/idecomp/decomp/modelos/oper_desvio_fpha.py +++ b/idecomp/decomp/modelos/oper_desvio_fpha.py @@ -1,9 +1,8 @@ # Imports de módulos externos -from cfinterface.components.line import Line +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField - from idecomp.decomp.modelos.blocos.tabelacsv import TabelaCSVLibs diff --git a/idecomp/decomp/modelos/postos.py b/idecomp/decomp/modelos/postos.py index fe56dc4..5366692 100644 --- a/idecomp/decomp/modelos/postos.py +++ b/idecomp/decomp/modelos/postos.py @@ -1,8 +1,7 @@ -from cfinterface.components.register import Register +from cfinterface.components.integerfield import IntegerField from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.integerfield import IntegerField -from typing import List +from cfinterface.components.register import Register class RegistroPostos(Register): @@ -24,7 +23,7 @@ class RegistroPostos(Register): ) @classmethod - def set_postos(cls, postos: int): + def set_postos(cls, postos: int) -> None: cls.POSTOS = postos cls.LINE = Line( [ @@ -36,9 +35,9 @@ def set_postos(cls, postos: int): ) @property - def postos(self) -> List[int]: + def postos(self) -> list[int]: return self.data @postos.setter - def postos(self, v: List[int]): + def postos(self, v: list[int]) -> None: self.data = v diff --git a/idecomp/decomp/modelos/relato.py b/idecomp/decomp/modelos/relato.py index 480d112..9f5496e 100644 --- a/idecomp/decomp/modelos/relato.py +++ b/idecomp/decomp/modelos/relato.py @@ -1,26 +1,28 @@ # Imports do próprio módulo +from typing import IO, Any + +import numpy as np +import pandas as pd # type: ignore[import-untyped] + +# Imports de módulos externos +from cfinterface.components.block import Block +from cfinterface.components.field import Field +from cfinterface.components.floatfield import FloatField +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line +from cfinterface.components.literalfield import LiteralField + from idecomp.config import ( MAX_CENARIOS, MAX_ESTAGIOS, MAX_PATAMARES, - MAX_SUBSISTEMAS, MAX_REES, + MAX_SUBSISTEMAS, MAX_UHES, MAX_UTES, MESES_DF, ) -# Imports de módulos externos -from cfinterface.components.block import Block -from cfinterface.components.line import Line -from cfinterface.components.field import Field -from cfinterface.components.integerfield import IntegerField -from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField -import numpy as np # type: ignore -import pandas as pd # type: ignore -from typing import IO, List, Tuple, Dict - class BlocoREEsSubsistemas(Block): """ @@ -33,7 +35,9 @@ class BlocoREEsSubsistemas(Block): BEGIN_PATTERN = "Relatorio dos dados da configuracao dos Reservatorios" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -60,7 +64,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame( data={ @@ -74,11 +78,11 @@ def converte_tabela_em_df() -> pd.DataFrame: return df comecou = False - numeros: List[int] = [] - nomes: List[str] = [] - numeros_subsistema: List[int] = [] - nomes_subsistema: List[str] = [] - nomes_newave: List[str] = [] + numeros: list[int] = [] + nomes: list[str] = [] + numeros_subsistema: list[int] = [] + nomes_subsistema: list[str] = [] + nomes_newave: list[str] = [] while True: # Confere se a leitura não acabou linha = file.readline() @@ -112,7 +116,9 @@ class BlocoUHEsREEsSubsistemas(Block): ) END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -141,7 +147,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame( data={ @@ -157,13 +163,13 @@ def converte_tabela_em_df() -> pd.DataFrame: return df comecou = False - numeros: List[int] = [] - nomes: List[str] = [] - numeros_rees: List[int] = [] - nomes_rees: List[str] = [] - numeros_subsistema: List[int] = [] - nomes_subsistema: List[str] = [] - nomes_newave: List[str] = [] + numeros: list[int] = [] + nomes: list[str] = [] + numeros_rees: list[int] = [] + nomes_rees: list[str] = [] + numeros_subsistema: list[int] = [] + nomes_subsistema: list[str] = [] + nomes_newave: list[str] = [] while True: # Confere se a leitura não acabou linha = file.readline() @@ -196,7 +202,9 @@ class BlocoConvergenciaRelato(Block): BEGIN_PATTERN = "RELATORIO DE CONVERGENCIA DO PROCESSO ITERATIVO" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -229,7 +237,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: colunas = [ "iteracao", @@ -271,7 +279,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if len(linha) < 5: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break if "----" in linha: @@ -300,7 +308,9 @@ class BlocoRelatorioOperacaoRelato(Block): BEGIN_PATTERN = r"RELATORIO DA OPERACAO " END_PATTERN = "X----X-" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__scenario_line = Line( [IntegerField(2, 34), IntegerField(4, 47), FloatField(8, 67, 6)] @@ -347,7 +357,7 @@ def __eq__(self, o: object) -> bool: ] ) - def __read_bloco_operacao_uhe(self, file: IO): + def __read_bloco_operacao_uhe(self, file: IO[Any]) -> None: def converte_tabela_para_df() -> pd.DataFrame: cols = [ "volume_inicial_percentual", @@ -399,12 +409,12 @@ def converte_tabela_para_df() -> pd.DataFrame: file.readline() # Variáveis auxiliares - numeros: List[int] = [] - usinas: List[str] = [] - evaporacao: List[bool] = [] - tv_afluencia: List[bool] = [] - cota_abaixo_crista: List[bool] = [] - def_minima_zero: List[bool] = [] + numeros: list[int] = [] + usinas: list[str] = [] + evaporacao: list[bool] = [] + tv_afluencia: list[bool] = [] + cota_abaixo_crista: list[bool] = [] + def_minima_zero: list[bool] = [] # Salta uma linha e extrai a semana tabela: np.ndarray = np.zeros((MAX_UHES, 15)) i = 0 @@ -426,7 +436,7 @@ def converte_tabela_para_df() -> pd.DataFrame: tabela[i, :] = dados[3:] i += 1 - def __read_bloco_operacao_geral(self, file: IO): + def __read_bloco_operacao_geral(self, file: IO[Any]) -> None: def converte_tabela_para_df() -> pd.DataFrame: df = pd.DataFrame( data={ @@ -456,28 +466,28 @@ def converte_tabela_para_df() -> pd.DataFrame: linha_custo_exp = Line([FloatField(14, 54, 2, format="E")]) linha_custo_subsis = Line([LiteralField(2, 44), FloatField(14, 54, 2)]) - custo_futuro: list = linha_custo.read(file.readline()) + custo_futuro: list[Any] = linha_custo.read(file.readline()) file.readline() file.readline() - custo_estagio: list = linha_custo.read(file.readline()) + custo_estagio: list[Any] = linha_custo.read(file.readline()) file.readline() file.readline() - custo_gt: list = linha_custo.read(file.readline()) + custo_gt: list[Any] = linha_custo.read(file.readline()) file.readline() file.readline() file.readline() file.readline() - custo_desvio: list = linha_custo.read(file.readline()) - custo_vert_reservatorio: list = linha_custo.read(file.readline()) - custo_vert_fio: list = linha_custo.read(file.readline()) - custo_turbinamento_reservatorio: list = linha_custo.read( + custo_desvio: list[Any] = linha_custo.read(file.readline()) + custo_vert_reservatorio: list[Any] = linha_custo.read(file.readline()) + custo_vert_fio: list[Any] = linha_custo.read(file.readline()) + custo_turbinamento_reservatorio: list[Any] = linha_custo.read( file.readline() ) - custo_turbinamento_fio: list = linha_custo.read(file.readline()) - custo_intercambio: list = linha_custo_exp.read(file.readline()) + custo_turbinamento_fio: list[Any] = linha_custo.read(file.readline()) + custo_intercambio: list[Any] = linha_custo_exp.read(file.readline()) file.readline() - cmos: Dict[str, float] = {} + cmos: dict[str, float] = {} while True: linha = file.readline() if len(linha.strip()) < 4: @@ -487,7 +497,7 @@ def converte_tabela_para_df() -> pd.DataFrame: cmos[dados_linha[0]] = dados_linha[1] # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] str_bloco_operacao_uhe = "# Aproveitamento(s) com evaporacao " str_bloco_operacao_geral = "Valor esperado do custo futuro:" # str_bloco_restricoes_rhe = ( @@ -534,7 +544,9 @@ class BlocoRelatorioOperacaoUTERelato(Block): BEGIN_PATTERN = r"RELATORIO DA OPERACAO TERMICA E CONTRATOS" END_PATTERN = "RELATORIO DO BALANCO ENERGETICO" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__scenario_line = Line( [IntegerField(2, 34), IntegerField(4, 47), FloatField(8, 67, 6)] @@ -566,7 +578,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_para_df() -> pd.DataFrame: cols = [f"geracao_patamar{i}" for i in range(1, n_pats + 1)] + [ "custo" @@ -617,7 +629,7 @@ def converte_tabela_para_df() -> pd.DataFrame: linha: str = file.readline() # Verifica se acabou if self.ends(linha): - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] file.seek(posicao_ultima_linha) self.data = converte_tabela_para_df() break @@ -654,7 +666,9 @@ class BlocoBalancoEnergeticoRelato(Block): BEGIN_PATTERN = "RELATORIO DO BALANCO ENERGETICO" END_PATTERN = r"RELATORIO\s+DA\s+OPERACAO" # noqa - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__linha_cenario = Line( [IntegerField(2, 34), IntegerField(4, 47), FloatField(8, 67, 6)] @@ -685,23 +699,23 @@ def __eq__(self, o: object) -> bool: else: return self.data.equals(bloco.data) - def __define_linha_balanco(self, cabecalho: str) -> Tuple[Line, List[str]]: + def __define_linha_balanco(self, cabecalho: str) -> tuple[Line, list[str]]: colunas = [c for c in cabecalho.split(" ") if len(c) > 2] colunas = [c.strip("\n") for c in colunas] if "Interligacao" not in colunas: return Line([]), [] indice_intercambio = colunas.index("Interligacao") - campo_patamar: List[Field] = [LiteralField(5, 4)] - campos_balanco: List[Field] = [ + campo_patamar: list[Field] = [LiteralField(5, 4)] + campos_balanco: list[Field] = [ FloatField(7, 10 + 8 * i, 1) for i in range(indice_intercambio) ] - campo_subsis: List[Field] = [ + campo_subsis: list[Field] = [ LiteralField(2, 10 + 8 * indice_intercambio + 1) ] - campo_interligacao: List[Field] = [ + campo_interligacao: list[Field] = [ FloatField(8, 10 + 8 * indice_intercambio + 4, 1) ] - campos_apos_interligacao: List[Field] = [ + campos_apos_interligacao: list[Field] = [ FloatField(7, 25 + 8 * indice_intercambio + 8 * i, 1) for i in range(2) ] @@ -714,7 +728,7 @@ def __define_linha_balanco(self, cabecalho: str) -> Tuple[Line, List[str]]: ), [c for c in colunas if c != "Interligacao"] # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_para_df() -> pd.DataFrame: tamanho_maior_linha = max([len(lin) for lin in linhas]) tabela = np.zeros((len(linhas), tamanho_maior_linha)) @@ -789,8 +803,8 @@ def converte_tabela_para_df() -> pd.DataFrame: estagio = dados[0] cenario = dados[1] probabilidade = dados[2] - linhas: List[List[float]] = [] - colunas_balanco: List[str] = [] + linhas: list[list[float]] = [] + colunas_balanco: list[str] = [] while True: pos = file.tell() linha = file.readline() @@ -832,10 +846,10 @@ def converte_tabela_para_df() -> pd.DataFrame: indice_subsis = dados.index( [d for d in dados if type(d) is str][1] ) - dados_antes_interligacao: List[float] = [ + dados_antes_interligacao: list[float] = [ d for d in dados[1:indice_subsis] if d is not None ] - dados_apos_interligacao: List[float] = [ + dados_apos_interligacao: list[float] = [ d for d in dados[indice_subsis + 2 :] if d is not None ] linhas.append( @@ -871,7 +885,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = [f"estagio_{s}" for s in range(1, num_estagios + 1)] @@ -897,13 +911,13 @@ def converte_tabela_em_df() -> pd.DataFrame: - 1 ) num_estagios - campo_subsis: List[Field] = [LiteralField(6, 4)] - campos_cmos: List[Field] = [ + campo_subsis: list[Field] = [LiteralField(6, 4)] + campos_cmos: list[Field] = [ FloatField(10, 11 + i * 11, 2) for i in range(num_estagios) ] self.__linha = Line(campo_subsis + campos_cmos) - subsistemas_distintos: list = [] - patamares_distintos: list = [] + subsistemas_distintos: list[Any] = [] + patamares_distintos: list[Any] = [] tabela = np.zeros((MAX_SUBSISTEMAS * MAX_PATAMARES, num_estagios)) # Salta outra linha file.readline() @@ -912,7 +926,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -957,7 +971,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = [f"estagio_{s}" for s in range(1, num_estagios + 1)] @@ -972,12 +986,12 @@ def converte_tabela_em_df() -> pd.DataFrame: num_estagios = len( [e for e in file.readline().strip().split(" ") if len(e) > 2] ) - campo_parcela: List[Field] = [LiteralField(6, 4)] - campos_custos: List[Field] = [ + campo_parcela: list[Field] = [LiteralField(6, 4)] + campos_custos: list[Field] = [ FloatField(12, 11 + i * 13, 1) for i in range(num_estagios) ] self.__linha = Line(campo_parcela + campos_custos) - parcelas: list = [] + parcelas: list[Any] = [] tabela = np.zeros((2, num_estagios)) # Salta outra linha file.readline() @@ -1015,7 +1029,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = [f"estagio_{s}" for s in range(1, num_estagios + 1)] @@ -1031,12 +1045,12 @@ def converte_tabela_em_df() -> pd.DataFrame: len([e for e in file.readline().strip().split(" ") if len(e) > 2]) - 1 ) - campo_subsis: List[Field] = [LiteralField(6, 4)] - campos_gt: List[Field] = [ + campo_subsis: list[Field] = [LiteralField(6, 4)] + campos_gt: list[Field] = [ FloatField(10, 11 + i * 11, 1) for i in range(num_estagios) ] self.__linha = Line(campo_subsis + campos_gt) - subsistemas: list = [] + subsistemas: list[Any] = [] tabela = np.zeros((MAX_SUBSISTEMAS * MAX_PATAMARES, num_estagios)) # Salta outra linha file.readline() @@ -1045,7 +1059,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1081,7 +1095,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["inicial"] + [ @@ -1100,17 +1114,17 @@ def converte_tabela_em_df() -> pd.DataFrame: len([e for e in file.readline().strip().split(" ") if len(e) > 2]) - 1 ) - campo_usi: List[Field] = [ + campo_usi: list[Field] = [ IntegerField(3, 4), LiteralField(12, 8), FloatField(7, 22, 1), ] - campos_vu: List[Field] = [ + campos_vu: list[Field] = [ FloatField(6, 30 + i * 7, 1) for i in range(num_estagios) ] self.__linha = Line(campo_usi + campos_vu) - numeros: List[int] = [] - usinas: List[str] = [] + numeros: list[int] = [] + usinas: list[str] = [] tabela = np.zeros((MAX_UHES, num_estagios + 1)) # Salta outra linha file.readline() @@ -1119,7 +1133,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1140,7 +1154,9 @@ class BlocoDadosTermicasRelato(Block): BEGIN_PATTERN = "Relatorio dos Dados de Usinas Termicas" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -1175,7 +1191,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: cols = [ "geracao_minima_patamar_1", @@ -1203,10 +1219,10 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - numeros: List[int] = [] - usinas: List[str] = [] - subsistemas: List[str] = [] - estagios: List[int] = [] + numeros: list[int] = [] + usinas: list[str] = [] + subsistemas: list[str] = [] + estagios: list[int] = [] tabela = np.zeros((MAX_ESTAGIOS * MAX_UTES, 9)) @@ -1218,7 +1234,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X---X----------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1263,7 +1279,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = [f"estagio_{s}" for s in range(1, num_estagios + 1)] @@ -1281,16 +1297,16 @@ def converte_tabela_em_df() -> pd.DataFrame: len([e for e in file.readline().strip().split(" ") if len(e) > 2]) - 2 ) - campo_usi: List[Field] = [ + campo_usi: list[Field] = [ IntegerField(3, 4), LiteralField(12, 8), ] - campos_vu: List[Field] = [ + campos_vu: list[Field] = [ FloatField(6, 21 + i * 7, 1) for i in range(num_estagios) ] self.__linha = Line(campo_usi + campos_vu) - numeros: List[int] = [] - usinas: List[str] = [] + numeros: list[int] = [] + usinas: list[str] = [] tabela = np.zeros((MAX_UTES, num_estagios)) # Salta outra linha file.readline() @@ -1299,7 +1315,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1321,7 +1337,9 @@ class BlocoDadosMercadoRelato(Block): BEGIN_PATTERN = "Relatorio dos Dados de Mercado" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -1351,7 +1369,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: cols = [ "patamar_1", @@ -1371,8 +1389,8 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - estagios: List[int] = [] - subsistemas: List[str] = [] + estagios: list[int] = [] + subsistemas: list[str] = [] tabela = np.zeros((MAX_ESTAGIOS * MAX_SUBSISTEMAS, 6)) @@ -1382,7 +1400,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X---------X------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1422,7 +1440,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def le_tabela(linha: str) -> np.ndarray: indice_ree = int(linha.split("REE: ")[1].split("-")[0].strip()) ree = linha.split("REE: ")[1].split("/")[0].split("-")[1].strip() @@ -1433,8 +1451,8 @@ def le_tabela(linha: str) -> np.ndarray: estagios = [s for s in linha.split(" ") if len(s) > 2] num_estagios = len(estagios) - 1 file.readline() - campo_cenario: List[Field] = [IntegerField(3, 4)] - campos_enas: List[Field] = [ + campo_cenario: list[Field] = [IntegerField(3, 4)] + campos_enas: list[Field] = [ FloatField(8, 8 + 9 * i, 1) for i in range(num_estagios) ] linha_tabela = Line(campo_cenario + campos_enas) @@ -1444,7 +1462,7 @@ def le_tabela(linha: str) -> np.ndarray: while True: linha = file.readline() if len(linha) < 4: - tab = tab[:i, :] # type: ignore + tab = tab[:i, :] break tab[i, :] = linha_tabela.read(linha) indices_rees.append(indice_ree) @@ -1470,9 +1488,9 @@ def converte_tabela_em_df() -> pd.DataFrame: df = df.astype({"cenario": np.int64}) return df - indices_rees: List[int] = [] - rees: List[str] = [] - subsistemas: List[str] = [] + indices_rees: list[int] = [] + rees: list[str] = [] + subsistemas: list[str] = [] tabela = None while True: # Confere se a leitura não acabou @@ -1516,7 +1534,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["inicial"] + [ @@ -1537,19 +1555,19 @@ def converte_tabela_em_df() -> pd.DataFrame: - 3 ) - campos_ree: List[Field] = [ + campos_ree: list[Field] = [ LiteralField(12, 4), IntegerField(4, 17), IntegerField(4, 22), FloatField(7, 27, 1), ] - campos_vu: List[Field] = [ + campos_vu: list[Field] = [ FloatField(6, 35 + i * 7, 1) for i in range(num_estagios) ] self.__linha = Line(campos_ree + campos_vu) - rees: List[str] = [] - numeros: List[int] = [] - subsistemas: List[int] = [] + rees: list[str] = [] + numeros: list[int] = [] + subsistemas: list[int] = [] tabela = np.zeros((MAX_REES, num_estagios + 1)) # Salta outra linha file.readline() @@ -1558,7 +1576,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X------------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1596,7 +1614,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["inicial"] + [ @@ -1616,17 +1634,17 @@ def converte_tabela_em_df() -> pd.DataFrame: - 2 ) - campos_ree: List[Field] = [ + campos_ree: list[Field] = [ LiteralField(12, 4), IntegerField(4, 17), FloatField(7, 22, 1), ] - campos_vu: List[Field] = [ + campos_vu: list[Field] = [ FloatField(6, 30 + i * 7, 1) for i in range(num_estagios) ] self.__linha = Line(campos_ree + campos_vu) - subsistemas: List[int] = [] - numeros: List[int] = [] + subsistemas: list[int] = [] + numeros: list[int] = [] tabela = np.zeros((MAX_REES, num_estagios + 1)) # Salta outra linha file.readline() @@ -1635,7 +1653,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X------------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1657,15 +1675,17 @@ class BlocoENAPreEstudoMensalREERelato(Block): BEGIN_PATTERN = r"ENERGIA NATURAL AFLUENTE POR REE \(MESES" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) - campos_ree: List[Field] = [ + campos_ree: list[Field] = [ LiteralField(14, 4), IntegerField(4, 19), IntegerField(4, 24), FloatField(8, 29, 1), ] - campos_ena: List[Field] = [ + campos_ena: list[Field] = [ FloatField(8, 38 + i * 9, 1) for i in range(len(MESES_DF) - 1) ] self.__line = Line(campos_ree + campos_ena) @@ -1685,7 +1705,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["energia_armazenada_maxima"] + [ @@ -1700,14 +1720,14 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - rees: List[str] = [] + rees: list[str] = [] tabela = np.zeros((MAX_REES, len(MESES_DF))) i = 0 while True: # Confere se a leitura não acabou linha = file.readline() if "X--------------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1730,14 +1750,16 @@ class BlocoENAPreEstudoMensalSubsistemaRelato(Block): BEGIN_PATTERN = r"ENERGIA NATURAL AFLUENTE POR SUBSISTEMA \(MESES" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) - campos_subsis: List[Field] = [ + campos_subsis: list[Field] = [ LiteralField(14, 4), IntegerField(4, 19), FloatField(8, 24, 1), ] - campos_ena: List[Field] = [ + campos_ena: list[Field] = [ FloatField(8, 33 + i * 9, 1) for i in range(len(MESES_DF) - 1) ] self.__line = Line(campos_subsis + campos_ena) @@ -1757,7 +1779,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["energia_armazenada_maxima"] + [ @@ -1772,14 +1794,14 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - subsistemas: List[str] = [] + subsistemas: list[str] = [] tabela = np.zeros((MAX_SUBSISTEMAS, len(MESES_DF))) i = 0 while True: # Confere se a leitura não acabou linha = file.readline() if "X--------------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1802,15 +1824,17 @@ class BlocoENAPreEstudoSemanalREERelato(Block): BEGIN_PATTERN = r"DADOS DE ENERGIA NATURAL AFLUENTE POR REE \(SEMANAS" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) - campos_ree: List[Field] = [ + campos_ree: list[Field] = [ LiteralField(14, 4), IntegerField(4, 19), IntegerField(4, 24), FloatField(8, 29, 1), ] - campos_ena: List[Field] = [ + campos_ena: list[Field] = [ FloatField(8, 38 + i * 9, 1) for i in range(5) ] self.__line = Line(campos_ree + campos_ena) @@ -1830,7 +1854,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["energia_armazenada_maxima"] + [ @@ -1849,14 +1873,14 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - rees: List[str] = [] + rees: list[str] = [] tabela = np.zeros((MAX_REES, 6)) i = 0 while True: # Confere se a leitura não acabou linha = file.readline() if "X--------------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1879,14 +1903,16 @@ class BlocoENAPreEstudoSemanalSubsistemaRelato(Block): BEGIN_PATTERN = r"NATURAL AFLUENTE POR SUBSISTEMA\(SEMANAS" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) - campos_subsis: List[Field] = [ + campos_subsis: list[Field] = [ LiteralField(14, 4), IntegerField(4, 19), FloatField(8, 24, 1), ] - campos_ena: List[Field] = [ + campos_ena: list[Field] = [ FloatField(8, 33 + i * 9, 1) for i in range(5) ] self.__line = Line(campos_subsis + campos_ena) @@ -1906,7 +1932,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = ["energia_armazenada_maxima"] + [ @@ -1925,14 +1951,14 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - subsistemas: List[str] = [] + subsistemas: list[str] = [] tabela = np.zeros((MAX_SUBSISTEMAS, 6)) i = 0 while True: # Confere se a leitura não acabou linha = file.readline() if "X--------------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -1955,7 +1981,9 @@ class BlocoDiasExcluidosSemanas(Block): BEGIN_PATTERN = " Mes inicial do periodo de estudos" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line([IntegerField(2, 53)]) @@ -1975,7 +2003,7 @@ def __eq__(self, o: object) -> bool: return self.data == bloco.data # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] file.readline() dias_semana_inicial = self.__line.read(file.readline())[0] dias_semana_final = self.__line.read(file.readline())[0] diff --git a/idecomp/decomp/modelos/relgnl.py b/idecomp/decomp/modelos/relgnl.py index c703d97..fb1a59f 100644 --- a/idecomp/decomp/modelos/relgnl.py +++ b/idecomp/decomp/modelos/relgnl.py @@ -1,16 +1,18 @@ # Imports do próprio módulo -from idecomp.config import MAX_ESTAGIOS, MAX_UTES +from typing import IO, Any + +import numpy as np +import pandas as pd # type: ignore[import-untyped] # Imports de módulos externos from cfinterface.components.block import Block -from cfinterface.components.line import Line from cfinterface.components.field import Field +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField -import numpy as np # type: ignore -import pandas as pd # type: ignore -from typing import IO, List + +from idecomp.config import MAX_ESTAGIOS, MAX_UTES class BlocoDadosUsinasRelgnl(Block): @@ -24,7 +26,9 @@ class BlocoDadosUsinasRelgnl(Block): BEGIN_PATTERN = "Relatorio dos Dados de Usinas Termicas GNL" END_PATTERN = "" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__line = Line( [ @@ -59,7 +63,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: cols = [ "geracao_minima_patamar_1", @@ -87,10 +91,10 @@ def converte_tabela_em_df() -> pd.DataFrame: for _ in range(5): file.readline() - numeros: List[int] = [] - usinas: List[str] = [] - subsistemas: List[str] = [] - estagios: List[int] = [] + numeros: list[int] = [] + usinas: list[str] = [] + subsistemas: list[str] = [] + estagios: list[int] = [] tabela = np.zeros((MAX_ESTAGIOS * MAX_UTES, 9)) @@ -102,7 +106,7 @@ def converte_tabela_em_df() -> pd.DataFrame: # Confere se a leitura não acabou linha = file.readline() if "X---X----------X" in linha: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break # Senão, lê mais uma linha @@ -147,7 +151,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: colunas = ["patamar_1", "patamar_2", "patamar_3"] df = pd.DataFrame(tabela, columns=colunas) @@ -175,28 +179,28 @@ def converte_tabela_em_df() -> pd.DataFrame: ) file.readline() file.readline() - campo_usi: List[Field] = [ + campo_usi: list[Field] = [ IntegerField(3, 3), LiteralField(11, 8), IntegerField(3, 20), LiteralField(6, 24), LiteralField(11, 31), ] - campos_cmos: List[Field] = [ + campos_cmos: list[Field] = [ FloatField(11, 43 + i * 11, 2) for i in range(num_estagios) ] self.__linha = Line(campo_usi + campos_cmos) - numeros: List[int] = [] - usinas: List[str] = [] - lags: List[int] = [] - subsistemas: List[str] = [] - semanas: List[str] = [] + numeros: list[int] = [] + usinas: list[str] = [] + lags: list[int] = [] + subsistemas: list[str] = [] + semanas: list[str] = [] tabela = np.zeros((MAX_UTES * MAX_ESTAGIOS, num_estagios)) i = 0 while True: linha: str = file.readline() if len(linha) < 3: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break dados = self.__linha.read(linha) @@ -235,7 +239,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_em_df() -> pd.DataFrame: colunas = ["patamar_1", "patamar_2", "patamar_3"] df = pd.DataFrame(tabela, columns=colunas) @@ -264,28 +268,28 @@ def converte_tabela_em_df() -> pd.DataFrame: ) file.readline() file.readline() - campo_usi: List[Field] = [ + campo_usi: list[Field] = [ IntegerField(3, 3), LiteralField(11, 8), IntegerField(3, 20), LiteralField(6, 24), IntegerField(9, 31), ] - campos_cmos: List[Field] = [ + campos_cmos: list[Field] = [ FloatField(11, 41 + i * 11, 2) for i in range(num_estagios) ] self.__linha = Line(campo_usi + campos_cmos) - numeros: List[int] = [] - usinas: List[str] = [] - lags: List[int] = [] - subsistemas: List[str] = [] - semanas: List[str] = [] + numeros: list[int] = [] + usinas: list[str] = [] + lags: list[int] = [] + subsistemas: list[str] = [] + semanas: list[str] = [] tabela = np.zeros((MAX_UTES * MAX_ESTAGIOS, num_estagios)) i = 0 while True: linha: str = file.readline() if len(linha) < 3: - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_em_df() break dados = self.__linha.read(linha) @@ -309,7 +313,9 @@ class BlocoRelatorioOperacaoRelgnl(Block): BEGIN_PATTERN = "RELATORIO DA OPERACAO TERMICA E CONTRATOS" END_PATTERN = "RELATORIO DA OPERACAO TERMICA E CONTRATOS" - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, previous: Any = None, next: Any = None, data: Any = None + ) -> None: super().__init__(previous, next, data) self.__linha_cenario = Line( [IntegerField(2, 34), IntegerField(4, 47), FloatField(8, 67, 6)] @@ -346,7 +352,7 @@ def __eq__(self, o: object) -> bool: return self.data.equals(bloco.data) # Override - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] def converte_tabela_para_df() -> pd.DataFrame: df = pd.DataFrame(tabela) cols = [ @@ -385,11 +391,11 @@ def converte_tabela_para_df() -> pd.DataFrame: # Variáveis auxiliares str_tabela = "Sinalizacao de Despacho antecipado em k meses" - subsistemas: List[str] = [] - usinas: List[str] = [] - lags: List[int] = [] - estagios: List[str] = [] - inicio_semanas: List[str] = [] + subsistemas: list[str] = [] + usinas: list[str] = [] + lags: list[int] = [] + estagios: list[str] = [] + inicio_semanas: list[str] = [] # Salta duas linhas e extrai o estágio / cenário file.readline() @@ -408,7 +414,7 @@ def converte_tabela_para_df() -> pd.DataFrame: # Verifica se acabou if self.ends(linha) or len(linha) == 0: file.seek(ultima_linha) - tabela = tabela[:i, :] # type: ignore + tabela = tabela[:i, :] self.data = converte_tabela_para_df() break # Senão, procura a linha que identifica o subsistema diff --git a/idecomp/decomp/modelos/vazoes.py b/idecomp/decomp/modelos/vazoes.py index 5502d9a..5305715 100644 --- a/idecomp/decomp/modelos/vazoes.py +++ b/idecomp/decomp/modelos/vazoes.py @@ -1,6 +1,6 @@ -from typing import IO, List +from typing import IO, Any -import numpy as np # type: ignore +import numpy as np from cfinterface.components.section import Section @@ -9,11 +9,16 @@ class SecaoVazoesPostos(Section): Registro com os dados associados às vazões dos postos. """ - __slots__ = [] + __slots__: list[str] = [] TAMANHO_REGISTRO = 320 - def __init__(self, previous=None, next=None, data=None) -> None: + def __init__( + self, + previous: Any = None, + next: Any = None, + data: Any = None, + ) -> None: super().__init__(previous, next, data) self.data = { "dados_gerais": [], @@ -32,15 +37,17 @@ def __eq__(self, o: object) -> bool: if not isinstance(o, SecaoVazoesPostos): return False bloco: SecaoVazoesPostos = o - if not all([ - isinstance(self.data, dict), - isinstance(o.data, dict), - ]): + if not all( + [ + isinstance(self.data, dict), + isinstance(o.data, dict), + ] + ): return False else: return self.data == bloco.data - def read(self, file: IO, *args, **kwargs): + def read(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] # Leitura do primeiro registro (dados gerais) self.__le_primeiro_registro(file) # Leitura do segundo registro (códigos UHEs) @@ -68,7 +75,7 @@ def read(self, file: IO, *args, **kwargs): # semanais observadas) self.__le_decimo_registro(file) - def __le_primeiro_registro(self, file: IO): + def __le_primeiro_registro(self, file: IO[Any]) -> None: dados = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), dtype=np.int32, @@ -76,7 +83,7 @@ def __le_primeiro_registro(self, file: IO): ) self.data["dados_gerais"] = list(dados) - def __le_segundo_registro(self, file: IO): + def __le_segundo_registro(self, file: IO[Any]) -> None: dados = np.frombuffer( file.read( SecaoVazoesPostos.TAMANHO_REGISTRO @@ -89,7 +96,7 @@ def __le_segundo_registro(self, file: IO): ) self.data["codigos_uhes"] = list(dados) - def __le_terceiro_registro(self, file: IO): + def __le_terceiro_registro(self, file: IO[Any]) -> None: dados_caso = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), dtype=np.int32, @@ -97,7 +104,7 @@ def __le_terceiro_registro(self, file: IO): ) self.data["dados_caso"] = list(dados_caso) - def __le_quarto_registro(self, file: IO): + def __le_quarto_registro(self, file: IO[Any]) -> None: dados_cenarios = np.frombuffer( file.read( SecaoVazoesPostos.TAMANHO_REGISTRO @@ -110,7 +117,7 @@ def __le_quarto_registro(self, file: IO): ) self.data["dados_cenarios"] = list(dados_cenarios) - def __le_quinto_registro(self, file: IO): + def __le_quinto_registro(self, file: IO[Any]) -> None: for _ in range(self.numero_semanas_completas): dados_cenarios = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), @@ -119,7 +126,7 @@ def __le_quinto_registro(self, file: IO): ) self.data["previsoes"] += list(dados_cenarios) - def __le_sexto_registro(self, file: IO): + def __le_sexto_registro(self, file: IO[Any]) -> None: for _ in range(self.numero_cenarios_estocasticos): dados_cenarios = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), @@ -128,7 +135,7 @@ def __le_sexto_registro(self, file: IO): ) self.data["cenarios_gerados"] += list(dados_cenarios) - def __le_setimo_registro(self, file: IO): + def __le_setimo_registro(self, file: IO[Any]) -> None: for _ in range(self.numero_semanas_completas): dados_cenarios = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), @@ -139,7 +146,7 @@ def __le_setimo_registro(self, file: IO): dados_cenarios ) - def __le_oitavo_registro(self, file: IO): + def __le_oitavo_registro(self, file: IO[Any]) -> None: for _ in range(self.numero_cenarios_estocasticos): dados_cenarios = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), @@ -150,7 +157,7 @@ def __le_oitavo_registro(self, file: IO): dados_cenarios ) - def __le_nono_registro(self, file: IO): + def __le_nono_registro(self, file: IO[Any]) -> None: for _ in range(self.numero_meses_observados): dados_cenarios = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), @@ -159,7 +166,7 @@ def __le_nono_registro(self, file: IO): ) self.data["observacoes_mensais"] += list(dados_cenarios) - def __le_decimo_registro(self, file: IO): + def __le_decimo_registro(self, file: IO[Any]) -> None: for _ in range(self.numero_semanas_observadas): dados_cenarios = np.frombuffer( file.read(SecaoVazoesPostos.TAMANHO_REGISTRO * 4), @@ -168,7 +175,7 @@ def __le_decimo_registro(self, file: IO): ) self.data["observacoes_semanais"] += list(dados_cenarios) - def write(self, file: IO, *args, **kwargs): + def write(self, file: IO[Any], *args: Any, **kwargs: Any) -> None: # type: ignore[override] # Escrita do primeiro registro (dados gerais) self.__escreve_primeiro_registro(file) # Escrita do segundo registro (códigos UHEs) @@ -196,20 +203,20 @@ def write(self, file: IO, *args, **kwargs): # semanais observadas) self.__escreve_decimo_registro(file) - def __escreve_primeiro_registro(self, file: IO): + def __escreve_primeiro_registro(self, file: IO[Any]) -> None: file.write( np.array(self.data["dados_gerais"], dtype=np.int32).tobytes() ) - def __escreve_segundo_registro(self, file: IO): + def __escreve_segundo_registro(self, file: IO[Any]) -> None: file.write( np.array(self.data["codigos_uhes"], dtype=np.int32).tobytes() ) - def __escreve_terceiro_registro(self, file: IO): + def __escreve_terceiro_registro(self, file: IO[Any]) -> None: file.write(np.array(self.data["dados_caso"], dtype=np.int32).tobytes()) - def __escreve_quarto_registro(self, file: IO): + def __escreve_quarto_registro(self, file: IO[Any]) -> None: num_entradas = len(self.data["dados_cenarios"]) num_valores = ( self.__numero_blocos_cenarios() * SecaoVazoesPostos.TAMANHO_REGISTRO @@ -220,22 +227,22 @@ def __escreve_quarto_registro(self, file: IO): np.array(self.data["dados_cenarios"], dtype=np.float32).tobytes() ) - def __escreve_quinto_registro(self, file: IO): + def __escreve_quinto_registro(self, file: IO[Any]) -> None: file.write(np.array(self.data["previsoes"], dtype=np.int32).tobytes()) - def __escreve_sexto_registro(self, file: IO): + def __escreve_sexto_registro(self, file: IO[Any]) -> None: file.write( np.array(self.data["cenarios_gerados"], dtype=np.int32).tobytes() ) - def __escreve_setimo_registro(self, file: IO): + def __escreve_setimo_registro(self, file: IO[Any]) -> None: file.write( np.array( self.data["previsoes_com_postos_artificiais"], dtype=np.int32 ).tobytes() ) - def __escreve_oitavo_registro(self, file: IO): + def __escreve_oitavo_registro(self, file: IO[Any]) -> None: file.write( np.array( self.data["cenarios_calculados_com_postos_artificiais"], @@ -243,12 +250,12 @@ def __escreve_oitavo_registro(self, file: IO): ).tobytes() ) - def __escreve_nono_registro(self, file: IO): + def __escreve_nono_registro(self, file: IO[Any]) -> None: file.write( np.array(self.data["observacoes_mensais"], dtype=np.int32).tobytes() ) - def __escreve_decimo_registro(self, file: IO): + def __escreve_decimo_registro(self, file: IO[Any]) -> None: file.write( np.array( self.data["observacoes_semanais"], dtype=np.int32 @@ -279,7 +286,7 @@ def numero_uhes(self) -> int: return self.data["dados_gerais"][0] @numero_uhes.setter - def numero_uhes(self, n: int): + def numero_uhes(self, n: int) -> None: self.data["dados_gerais"][0] = n @property @@ -287,15 +294,15 @@ def numero_estagios(self) -> int: return self.data["dados_gerais"][1] @numero_estagios.setter - def numero_estagios(self, n: int): + def numero_estagios(self, n: int) -> None: self.data["dados_gerais"][1] = n @property - def numero_aberturas_estagios(self) -> List[int]: + def numero_aberturas_estagios(self) -> list[int]: return self.data["dados_gerais"][2 : 2 + self.numero_estagios] @numero_aberturas_estagios.setter - def numero_aberturas_estagios(self, n: List[int]): + def numero_aberturas_estagios(self, n: list[int]) -> None: self.data["dados_gerais"][2 : 2 + self.numero_estagios] = n @property @@ -304,15 +311,15 @@ def numero_postos(self) -> int: return 320 if valor_arquivo == 0 else valor_arquivo @numero_postos.setter - def numero_postos(self, n: int): + def numero_postos(self, n: int) -> None: self.data["dados_gerais"][2 + self.numero_estagios] = n @property - def codigos_uhes(self) -> List[int]: + def codigos_uhes(self) -> list[int]: return self.data["codigos_uhes"][: self.numero_uhes] @codigos_uhes.setter - def codigos_uhes(self, n: List[int]): + def codigos_uhes(self, n: list[int]) -> None: self.data["codigos_uhes"][: self.numero_uhes] = n @property @@ -344,59 +351,61 @@ def versao_modelo(self) -> int: return self.data["dados_caso"][5] @property - def probabilidades_nos(self) -> List[float]: + def probabilidades_nos(self) -> list[float]: return self.data["dados_cenarios"][ : sum(self.numero_aberturas_estagios) ] @probabilidades_nos.setter - def probabilidades_nos(self, probs: List[float]): + def probabilidades_nos(self, probs: list[float]) -> None: self.data["dados_cenarios"] = probs @property - def previsoes(self) -> List[int]: + def previsoes(self) -> list[int]: return self.data["previsoes"] @previsoes.setter - def previsoes(self, prevs: List[int]): + def previsoes(self, prevs: list[int]) -> None: self.data["previsoes"] = prevs @property - def cenarios_gerados(self) -> List[int]: + def cenarios_gerados(self) -> list[int]: return self.data["cenarios_gerados"] @cenarios_gerados.setter - def cenarios_gerados(self, cens: List[int]): + def cenarios_gerados(self, cens: list[int]) -> None: self.data["cenarios_gerados"] = cens @property - def previsoes_com_postos_artificiais(self) -> List[int]: + def previsoes_com_postos_artificiais(self) -> list[int]: return self.data["previsoes_com_postos_artificiais"] @previsoes_com_postos_artificiais.setter - def previsoes_com_postos_artificiais(self, prevs: List[int]): + def previsoes_com_postos_artificiais(self, prevs: list[int]) -> None: self.data["previsoes_com_postos_artificiais"] = prevs @property - def cenarios_calculados_com_postos_artificiais(self) -> List[int]: + def cenarios_calculados_com_postos_artificiais(self) -> list[int]: return self.data["cenarios_calculados_com_postos_artificiais"] @cenarios_calculados_com_postos_artificiais.setter - def cenarios_calculados_com_postos_artificiais(self, cens: List[int]): + def cenarios_calculados_com_postos_artificiais( + self, cens: list[int] + ) -> None: self.data["cenarios_calculados_com_postos_artificiais"] = cens @property - def observacoes_mensais(self) -> List[int]: + def observacoes_mensais(self) -> list[int]: return self.data["observacoes_mensais"] @observacoes_mensais.setter - def observacoes_mensais(self, obs: List[int]): + def observacoes_mensais(self, obs: list[int]) -> None: self.data["observacoes_mensais"] = obs @property - def observacoes_semanais(self) -> List[int]: + def observacoes_semanais(self) -> list[int]: return self.data["observacoes_semanais"] @observacoes_semanais.setter - def observacoes_semanais(self, obs: List[int]): + def observacoes_semanais(self, obs: list[int]) -> None: self.data["observacoes_semanais"] = obs diff --git a/idecomp/decomp/oper_desvio_fpha.py b/idecomp/decomp/oper_desvio_fpha.py index 43a7b32..6d3807b 100644 --- a/idecomp/decomp/oper_desvio_fpha.py +++ b/idecomp/decomp/oper_desvio_fpha.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs -from idecomp.decomp.modelos.oper_desvio_fpha import TabelaOperDesvioFpha +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs +from idecomp.decomp.modelos.oper_desvio_fpha import TabelaOperDesvioFpha class OperDesvioFpha(ArquivoCSV): @@ -14,7 +13,7 @@ class OperDesvioFpha(ArquivoCSV): BLOCKS = [VersaoModeloLibs, TabelaOperDesvioFpha] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/oper_disp_usih.py b/idecomp/decomp/oper_disp_usih.py index 631c26f..51b4e8c 100644 --- a/idecomp/decomp/oper_disp_usih.py +++ b/idecomp/decomp/oper_disp_usih.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs -from idecomp.decomp.modelos.oper_disp_usih import TabelaOperDispUsih +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs +from idecomp.decomp.modelos.oper_disp_usih import TabelaOperDispUsih class OperDispUsih(ArquivoCSV): @@ -14,7 +13,7 @@ class OperDispUsih(ArquivoCSV): BLOCKS = [VersaoModeloLibs, TabelaOperDispUsih] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/oper_disp_usih_ree.py b/idecomp/decomp/oper_disp_usih_ree.py index c795ed9..4601343 100644 --- a/idecomp/decomp/oper_disp_usih_ree.py +++ b/idecomp/decomp/oper_disp_usih_ree.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs -from idecomp.decomp.modelos.oper_disp_usih_ree import TabelaOperDispUsihRee +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs +from idecomp.decomp.modelos.oper_disp_usih_ree import TabelaOperDispUsihRee class OperDispUsihRee(ArquivoCSV): @@ -15,7 +14,7 @@ class OperDispUsihRee(ArquivoCSV): BLOCKS = [VersaoModeloLibs, TabelaOperDispUsihRee] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/oper_disp_usih_subm.py b/idecomp/decomp/oper_disp_usih_subm.py index 29fc95b..be11154 100644 --- a/idecomp/decomp/oper_disp_usih_subm.py +++ b/idecomp/decomp/oper_disp_usih_subm.py @@ -1,9 +1,8 @@ -from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs -from idecomp.decomp.modelos.oper_disp_usih_subm import TabelaOperDispUsihSubm +import pandas as pd # type: ignore from idecomp.decomp.modelos.arquivoscsv.arquivocsv import ArquivoCSV -from typing import Optional -import pandas as pd # type: ignore +from idecomp.decomp.modelos.blocos.versaomodelo import VersaoModeloLibs +from idecomp.decomp.modelos.oper_disp_usih_subm import TabelaOperDispUsihSubm class OperDispUsihSubm(ArquivoCSV): @@ -15,7 +14,7 @@ class OperDispUsihSubm(ArquivoCSV): BLOCKS = [VersaoModeloLibs, TabelaOperDispUsihSubm] @property - def tabela(self) -> Optional[pd.DataFrame]: + def tabela(self) -> pd.DataFrame | None: """ A tabela de dados que está contida no arquivo. diff --git a/idecomp/decomp/postos.py b/idecomp/decomp/postos.py index 226bba2..9a0c6ec 100644 --- a/idecomp/decomp/postos.py +++ b/idecomp/decomp/postos.py @@ -1,9 +1,9 @@ -from cfinterface.files.registerfile import RegisterFile -from idecomp.decomp.modelos.postos import RegistroPostos -import pandas as pd # type: ignore +from typing import IO, Any, TypeVar +import pandas as pd # type: ignore[import-untyped] +from cfinterface.files.registerfile import RegisterFile -from typing import TypeVar, List, Optional, Union, IO +from idecomp.decomp.modelos.postos import RegistroPostos class Postos(RegisterFile): @@ -18,24 +18,27 @@ class Postos(RegisterFile): POSTOS = 320 STORAGE = "BINARY" - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) - self.__df: Optional[pd.DataFrame] = None + self.__df: pd.DataFrame | None = None RegistroPostos.set_postos(self.POSTOS) - def write(self, to: Union[str, IO], *args, **kwargs): + def write(self, to: str | IO[Any], *args: Any, **kwargs: Any) -> None: self.__atualiza_registros() super().write(to, *args, **kwargs) - def __monta_df_de_registros(self) -> Optional[pd.DataFrame]: - registros: List[RegistroPostos] = [ + def __monta_df_de_registros(self) -> pd.DataFrame | None: + registros: list[RegistroPostos] = [ r for r in self.data.of_type(RegistroPostos) ] if len(registros) == 0: return None df = pd.DataFrame( data={ - "nome": [r.data[0] if r.data[0] is not None else "" for r in registros], + "nome": [ + r.data[0] if r.data[0] is not None else "" + for r in registros + ], "ano_inicio_historico": [r.data[1] for r in registros], "ano_fim_historico": [r.data[2] for r in registros], } @@ -50,8 +53,8 @@ def __monta_df_de_registros(self) -> Optional[pd.DataFrame]: ) return df - def __atualiza_registros(self): - registros: List[RegistroPostos] = [r for r in self.data][1:] + def __atualiza_registros(self) -> None: + registros: list[RegistroPostos] = [r for r in self.data][1:] # type: ignore[assignment] n_registros = len(registros) n_meses = self.postos.shape[0] # Deleta os registros que sobraram @@ -82,5 +85,5 @@ def postos(self) -> pd.DataFrame: return self.__df @postos.setter - def postos(self, df: pd.DataFrame): + def postos(self, df: pd.DataFrame) -> None: self.__df = df diff --git a/idecomp/decomp/relato.py b/idecomp/decomp/relato.py index 014c4ab..a355321 100644 --- a/idecomp/decomp/relato.py +++ b/idecomp/decomp/relato.py @@ -1,35 +1,32 @@ -from idecomp.decomp.modelos.relato import BlocoREEsSubsistemas -from idecomp.decomp.modelos.relato import BlocoUHEsREEsSubsistemas -from idecomp.decomp.modelos.relato import BlocoConvergenciaRelato -from idecomp.decomp.modelos.relato import BlocoRelatorioOperacaoRelato -from idecomp.decomp.modelos.relato import BlocoRelatorioOperacaoUTERelato -from idecomp.decomp.modelos.relato import BlocoBalancoEnergeticoRelato -from idecomp.decomp.modelos.relato import BlocoCMORelato -from idecomp.decomp.modelos.relato import BlocoGeracaoTermicaSubsistemaRelato -from idecomp.decomp.modelos.relato import BlocoCustoOperacaoValorEsperadoRelato -from idecomp.decomp.modelos.relato import BlocoENAAcoplamentoREERelato -from idecomp.decomp.modelos.relato import BlocoVolumeUtilReservatorioRelato -from idecomp.decomp.modelos.relato import BlocoDadosTermicasRelato -from idecomp.decomp.modelos.relato import BlocoDisponibilidadesTermicasRelato -from idecomp.decomp.modelos.relato import BlocoDadosMercadoRelato -from idecomp.decomp.modelos.relato import BlocoEnergiaArmazenadaREERelato -from idecomp.decomp.modelos.relato import ( - BlocoEnergiaArmazenadaSubsistemaRelato, -) # noqa -from idecomp.decomp.modelos.relato import BlocoENAPreEstudoMensalREERelato -from idecomp.decomp.modelos.relato import ( - BlocoENAPreEstudoMensalSubsistemaRelato, -) # noqa -from idecomp.decomp.modelos.relato import BlocoENAPreEstudoSemanalREERelato -from idecomp.decomp.modelos.relato import ( - BlocoENAPreEstudoSemanalSubsistemaRelato, -) # noqa -from idecomp.decomp.modelos.relato import BlocoDiasExcluidosSemanas +from typing import Any, TypeVar +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block from cfinterface.files.blockfile import BlockFile -from typing import Union, List, TypeVar, Optional -import pandas as pd # type: ignore + +from idecomp.decomp.modelos.relato import ( + BlocoBalancoEnergeticoRelato, + BlocoCMORelato, + BlocoConvergenciaRelato, + BlocoCustoOperacaoValorEsperadoRelato, + BlocoDadosMercadoRelato, + BlocoDadosTermicasRelato, + BlocoDiasExcluidosSemanas, + BlocoDisponibilidadesTermicasRelato, + BlocoENAAcoplamentoREERelato, + BlocoENAPreEstudoMensalREERelato, + BlocoENAPreEstudoMensalSubsistemaRelato, + BlocoENAPreEstudoSemanalREERelato, + BlocoENAPreEstudoSemanalSubsistemaRelato, + BlocoEnergiaArmazenadaREERelato, + BlocoEnergiaArmazenadaSubsistemaRelato, + BlocoGeracaoTermicaSubsistemaRelato, + BlocoREEsSubsistemas, + BlocoRelatorioOperacaoRelato, + BlocoRelatorioOperacaoUTERelato, + BlocoUHEsREEsSubsistemas, + BlocoVolumeUtilReservatorioRelato, +) # noqa # noqa # noqa class Relato(BlockFile): @@ -69,7 +66,7 @@ class Relato(BlockFile): BlocoDiasExcluidosSemanas, ] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) self.__relatorios_operacao_ute = None self.__relatorios_operacao_uhe = None @@ -77,8 +74,8 @@ def __init__(self, data=...) -> None: self.__balanco_energetico = None def __concatena_blocos( - self, blocos: Union[T, List[T]], indice_data=None - ) -> Optional[pd.DataFrame]: + self, blocos: T | list[T], indice_data: int | None = None + ) -> pd.DataFrame | None: df = None if not isinstance(blocos, list): blocos = [blocos] @@ -93,7 +90,7 @@ def __concatena_blocos( return None @property - def rees_submercados(self) -> Optional[pd.DataFrame]: + def rees_submercados(self) -> pd.DataFrame | None: """ Obtém a tabela de relação entre os REEs e os submercados do DECOMP existente no :class:`Relato` @@ -113,7 +110,7 @@ def rees_submercados(self) -> Optional[pd.DataFrame]: return None @property - def uhes_rees_submercados(self) -> Optional[pd.DataFrame]: + def uhes_rees_submercados(self) -> pd.DataFrame | None: """ Obtém a tabela de relação entre as UHEs, REEs e os submercados do DECOMP existente no :class:`Relato` @@ -135,7 +132,7 @@ def uhes_rees_submercados(self) -> Optional[pd.DataFrame]: return None @property - def convergencia(self) -> Optional[pd.DataFrame]: + def convergencia(self) -> pd.DataFrame | None: """ Obtém a tabela de convergência do DECOMP existente no :class:`Relato` @@ -161,7 +158,7 @@ def convergencia(self) -> Optional[pd.DataFrame]: return None @property - def relatorio_operacao_custos(self) -> Optional[pd.DataFrame]: + def relatorio_operacao_custos(self) -> pd.DataFrame | None: """ Obtém a tabela de operação de cada UHE por estágio do DECOMP existente no :class:`Relato` @@ -186,7 +183,7 @@ def relatorio_operacao_custos(self) -> Optional[pd.DataFrame]: :rtype: pd.DataFrame | None """ if self.__relatorios_operacao_custos is None: - blocos_custos: List[BlocoRelatorioOperacaoRelato] = [] + blocos_custos: list[BlocoRelatorioOperacaoRelato] = [] for b in self.data.of_type(BlocoRelatorioOperacaoRelato): if b.data[0] == "GERAL": blocos_custos.append(b) @@ -196,7 +193,7 @@ def relatorio_operacao_custos(self) -> Optional[pd.DataFrame]: return self.__relatorios_operacao_custos @property - def relatorio_operacao_uhe(self) -> Optional[pd.DataFrame]: + def relatorio_operacao_uhe(self) -> pd.DataFrame | None: """ Obtém a tabela de operação de cada UHE por estágio do DECOMP existente no :class:`Relato` @@ -230,7 +227,7 @@ def relatorio_operacao_uhe(self) -> Optional[pd.DataFrame]: :rtype: pd.DataFrame | None """ if self.__relatorios_operacao_uhe is None: - blocos_uhe: List[BlocoRelatorioOperacaoRelato] = [] + blocos_uhe: list[BlocoRelatorioOperacaoRelato] = [] for b in self.data.of_type(BlocoRelatorioOperacaoRelato): if b.data[0] == "UHE": blocos_uhe.append(b) @@ -240,7 +237,7 @@ def relatorio_operacao_uhe(self) -> Optional[pd.DataFrame]: return self.__relatorios_operacao_uhe @property - def relatorio_operacao_ute(self) -> Optional[pd.DataFrame]: + def relatorio_operacao_ute(self) -> pd.DataFrame | None: """ Obtém a tabela de operação de cada UTE por estágio do DECOMP existente no :class:`Relato` @@ -268,7 +265,7 @@ def relatorio_operacao_ute(self) -> Optional[pd.DataFrame]: return self.__relatorios_operacao_ute @property - def balanco_energetico(self) -> Optional[pd.DataFrame]: + def balanco_energetico(self) -> pd.DataFrame | None: """ Obtém a tabela de balanço energético entre os patamares para cada estágio do DECOMP existente no :class:`Relato` @@ -307,7 +304,7 @@ def balanco_energetico(self) -> Optional[pd.DataFrame]: return self.__balanco_energetico @property - def cmo_medio_submercado(self) -> Optional[pd.DataFrame]: + def cmo_medio_submercado(self) -> pd.DataFrame | None: """ Obtém a tabela de CMO existente no :class:`Relato` @@ -327,7 +324,7 @@ def cmo_medio_submercado(self) -> Optional[pd.DataFrame]: return None @property - def geracao_termica_submercado(self) -> Optional[pd.DataFrame]: + def geracao_termica_submercado(self) -> pd.DataFrame | None: """ Obtém a tabela de Geração Térmica existente no :class:`Relato` @@ -346,7 +343,7 @@ def geracao_termica_submercado(self) -> Optional[pd.DataFrame]: return None @property - def custo_operacao_valor_esperado(self) -> Optional[pd.DataFrame]: + def custo_operacao_valor_esperado(self) -> pd.DataFrame | None: """ Obtém a tabela de Custo de Operação existente no :class:`Relato` @@ -365,7 +362,7 @@ def custo_operacao_valor_esperado(self) -> Optional[pd.DataFrame]: return None @property - def energia_armazenada_ree(self) -> Optional[pd.DataFrame]: + def energia_armazenada_ree(self) -> pd.DataFrame | None: """ Obtém a tabela de Energia Armazenada por REE (em %) existente no :class:`Relato` @@ -387,7 +384,7 @@ def energia_armazenada_ree(self) -> Optional[pd.DataFrame]: return None @property - def energia_armazenada_submercado(self) -> Optional[pd.DataFrame]: + def energia_armazenada_submercado(self) -> pd.DataFrame | None: """ Obtém a tabela de Energia Armazenada por Submercado (em %) existente no :class:`Relato` @@ -408,7 +405,7 @@ def energia_armazenada_submercado(self) -> Optional[pd.DataFrame]: return None @property - def volume_util_reservatorios(self) -> Optional[pd.DataFrame]: + def volume_util_reservatorios(self) -> pd.DataFrame | None: """ Obtém a tabela de Volumes Úteis por reservatório (em %) existente no :class:`Relato` @@ -430,7 +427,7 @@ def volume_util_reservatorios(self) -> Optional[pd.DataFrame]: return None @property - def dados_termicas(self) -> Optional[pd.DataFrame]: + def dados_termicas(self) -> pd.DataFrame | None: """ Obtém a tabela de dados cadastrais das usinas térmicas existente no :class:`Relato`. @@ -458,7 +455,7 @@ def dados_termicas(self) -> Optional[pd.DataFrame]: return None @property - def disponibilidades_termicas(self) -> Optional[pd.DataFrame]: + def disponibilidades_termicas(self) -> pd.DataFrame | None: """ Obtém a tabela de disponibilidades das usinas térmicas existente no :class:`Relato`. @@ -479,7 +476,7 @@ def disponibilidades_termicas(self) -> Optional[pd.DataFrame]: return None @property - def dados_mercado(self) -> Optional[pd.DataFrame]: + def dados_mercado(self) -> pd.DataFrame | None: """ Obtém a tabela de dados do mercado de energia existente no :class:`Relato`. @@ -501,7 +498,7 @@ def dados_mercado(self) -> Optional[pd.DataFrame]: return None @property - def ena_acoplamento_ree(self) -> Optional[pd.DataFrame]: + def ena_acoplamento_ree(self) -> pd.DataFrame | None: """ Obtém a tabela de ENA para acoplamento com o longo prazo (em MWmed) existente no :class:`Relato` @@ -523,7 +520,7 @@ def ena_acoplamento_ree(self) -> Optional[pd.DataFrame]: return None @property - def ena_pre_estudo_mensal_ree(self) -> Optional[pd.DataFrame]: + def ena_pre_estudo_mensal_ree(self) -> pd.DataFrame | None: """ Obtém a tabela de ENA Pré-Estudo Mensal por REE existente no :class:`Relato` @@ -545,7 +542,7 @@ def ena_pre_estudo_mensal_ree(self) -> Optional[pd.DataFrame]: return None @property - def ena_pre_estudo_mensal_submercado(self) -> Optional[pd.DataFrame]: + def ena_pre_estudo_mensal_submercado(self) -> pd.DataFrame | None: """ Obtém a tabela de ENA Pré-Estudo Mensal por Submercado existente no :class:`Relato` @@ -569,7 +566,7 @@ def ena_pre_estudo_mensal_submercado(self) -> Optional[pd.DataFrame]: return None @property - def ena_pre_estudo_semanal_ree(self) -> Optional[pd.DataFrame]: + def ena_pre_estudo_semanal_ree(self) -> pd.DataFrame | None: """ Obtém a tabela de ENA Pré-Estudo Semanal por REE existente no :class:`Relato` @@ -591,7 +588,7 @@ def ena_pre_estudo_semanal_ree(self) -> Optional[pd.DataFrame]: return None @property - def ena_pre_estudo_semanal_submercado(self) -> Optional[pd.DataFrame]: + def ena_pre_estudo_semanal_submercado(self) -> pd.DataFrame | None: """ Obtém a tabela de ENA Pré-Estudo Semanal por Submercado existente no :class:`Relato` @@ -615,7 +612,7 @@ def ena_pre_estudo_semanal_submercado(self) -> Optional[pd.DataFrame]: return None @property - def energia_armazenada_maxima_submercado(self) -> Optional[pd.DataFrame]: + def energia_armazenada_maxima_submercado(self) -> pd.DataFrame | None: """ Obtém a tabela de Energia Armazenada Máxima (EARMax) em MWmes por submercado existente no :class:`Relato` @@ -635,7 +632,7 @@ def energia_armazenada_maxima_submercado(self) -> Optional[pd.DataFrame]: return None @property - def dias_excluidos_semana_inicial(self) -> Optional[int]: + def dias_excluidos_semana_inicial(self) -> int | None: """ Obtém o número de dias excluídos da semana inicial. @@ -648,7 +645,7 @@ def dias_excluidos_semana_inicial(self) -> Optional[int]: return None @property - def dias_excluidos_semana_final(self) -> Optional[int]: + def dias_excluidos_semana_final(self) -> int | None: """ Obtém o número de dias excluídos da semana final. diff --git a/idecomp/decomp/relgnl.py b/idecomp/decomp/relgnl.py index d88a02b..f6ac71b 100644 --- a/idecomp/decomp/relgnl.py +++ b/idecomp/decomp/relgnl.py @@ -1,11 +1,15 @@ -from idecomp.decomp.modelos.relgnl import BlocoDadosUsinasRelgnl -from idecomp.decomp.modelos.relgnl import BlocoComandosUsinasAjustesTGRelgnl -from idecomp.decomp.modelos.relgnl import BlocoComandosUsinasAjustesRERelgnl -from idecomp.decomp.modelos.relgnl import BlocoRelatorioOperacaoRelgnl +from typing import Any, TypeVar + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.block import Block from cfinterface.files.blockfile import BlockFile -from typing import Type, List, TypeVar, Optional -import pandas as pd # type: ignore + +from idecomp.decomp.modelos.relgnl import ( + BlocoComandosUsinasAjustesRERelgnl, + BlocoComandosUsinasAjustesTGRelgnl, + BlocoDadosUsinasRelgnl, + BlocoRelatorioOperacaoRelgnl, +) class Relgnl(BlockFile): @@ -28,11 +32,11 @@ class Relgnl(BlockFile): BlocoRelatorioOperacaoRelgnl, ] - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) self.__relatorio_operacao_gnl = None - def __concatena_blocos(self, bloco: Type[T]) -> Optional[pd.DataFrame]: + def __concatena_blocos(self, bloco: type[T]) -> pd.DataFrame | None: """ Adiciona uma coluna com os estágios de cada amostra e outra com o estágio de cada uma, assumindo @@ -43,7 +47,7 @@ def __concatena_blocos(self, bloco: Type[T]) -> Optional[pd.DataFrame]: :rtype: pd.DataFrame """ - col_estagio: List[int] = [] + col_estagio: list[int] = [] df = None for i, b in enumerate(self.data.of_type(bloco)): if not isinstance(b, Block): @@ -61,7 +65,7 @@ def __concatena_blocos(self, bloco: Type[T]) -> Optional[pd.DataFrame]: return None @property - def usinas_termicas(self) -> Optional[pd.DataFrame]: + def usinas_termicas(self) -> pd.DataFrame | None: """ Tabela de informações das usinas térmicas GNL. @@ -88,7 +92,7 @@ def usinas_termicas(self) -> Optional[pd.DataFrame]: return None @property - def comandos_usinas_registros_tg(self) -> Optional[pd.DataFrame]: + def comandos_usinas_registros_tg(self) -> pd.DataFrame | None: """ Tabela de comandos das usinas térmicas GNL com ajustes devido aos registros TG. @@ -111,7 +115,7 @@ def comandos_usinas_registros_tg(self) -> Optional[pd.DataFrame]: return None @property - def comandos_usinas_restricoes_eletricas(self) -> Optional[pd.DataFrame]: + def comandos_usinas_restricoes_eletricas(self) -> pd.DataFrame | None: """ Tabela de comandos das usinas térmicas GNL com ajustes devido a restrições elétricas especiais. @@ -134,7 +138,7 @@ def comandos_usinas_restricoes_eletricas(self) -> Optional[pd.DataFrame]: return None @property - def relatorio_operacao_termica(self) -> Optional[pd.DataFrame]: + def relatorio_operacao_termica(self) -> pd.DataFrame | None: """ Tabela com o relatório do despacho sinalizado para as usinas térmicas GNL. diff --git a/idecomp/decomp/vazoes.py b/idecomp/decomp/vazoes.py index 089d145..3f7e6c4 100644 --- a/idecomp/decomp/vazoes.py +++ b/idecomp/decomp/vazoes.py @@ -1,7 +1,7 @@ -from typing import List, Optional, TypeVar +from typing import Any, TypeVar -import numpy as np # type: ignore -import pandas as pd # type: ignore +import numpy as np +import pandas as pd # type: ignore[import-untyped] from cfinterface.files.sectionfile import SectionFile from idecomp.decomp.modelos.vazoes import SecaoVazoesPostos @@ -18,26 +18,26 @@ class Vazoes(SectionFile): SECTIONS = [SecaoVazoesPostos] STORAGE = "BINARY" - def __init__(self, data=...) -> None: + def __init__(self, data: Any = ...) -> None: super().__init__(data) - self.__df_probabilidades: Optional[pd.DataFrame] = None - self.__df_previsoes_semanais: Optional[pd.DataFrame] = None - self.__df_cenarios_mensais_gerados: Optional[pd.DataFrame] = None - self.__df_previsoes_semanais_com_postos_artificiais: Optional[ - pd.DataFrame - ] = None - self.__df_cenarios_mensais_calculados_com_postos_artificiais: Optional[ - pd.DataFrame - ] = None - self.__df_observacoes_mensais: Optional[pd.DataFrame] = None - self.__df_observacoes_semanais: Optional[pd.DataFrame] = None - - def __obtem_secao_vazoes(self) -> Optional[SecaoVazoesPostos]: + self.__df_probabilidades: pd.DataFrame | None = None + self.__df_previsoes_semanais: pd.DataFrame | None = None + self.__df_cenarios_mensais_gerados: pd.DataFrame | None = None + self.__df_previsoes_semanais_com_postos_artificiais: ( + pd.DataFrame | None + ) = None + self.__df_cenarios_mensais_calculados_com_postos_artificiais: ( + pd.DataFrame | None + ) = None + self.__df_observacoes_mensais: pd.DataFrame | None = None + self.__df_observacoes_semanais: pd.DataFrame | None = None + + def __obtem_secao_vazoes(self) -> SecaoVazoesPostos | None: s = self.data.get_sections_of_type(SecaoVazoesPostos) return s if not isinstance(s, list) else None @property - def numero_aberturas_estagios(self) -> Optional[List[int]]: + def numero_aberturas_estagios(self) -> list[int] | None: """ Obtém a lista com o número de aberturas por estágio da árvore. @@ -51,13 +51,13 @@ def numero_aberturas_estagios(self) -> Optional[List[int]]: return None @numero_aberturas_estagios.setter - def numero_aberturas_estagios(self, a: List[int]): + def numero_aberturas_estagios(self, a: list[int]) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: dados.numero_aberturas_estagios = a @property - def probabilidades(self) -> Optional[pd.DataFrame]: + def probabilidades(self) -> pd.DataFrame | None: """ Obtém a tabela com as probabilidades de cada nó da árvore de cenários que foi gerada para o modelo. @@ -95,14 +95,14 @@ def probabilidades(self) -> Optional[pd.DataFrame]: return self.__df_probabilidades @probabilidades.setter - def probabilidades(self, df: pd.DataFrame): + def probabilidades(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: dados.probabilidades_nos = df["probabilidade"].tolist() self.__df_probabilidades = None @property - def previsoes(self) -> Optional[pd.DataFrame]: + def previsoes(self) -> pd.DataFrame | None: """ Obtém a tabela com as vazões previstas de cada nó da árvore de cenários, para a etapa determinística da execução, por posto. @@ -131,7 +131,7 @@ def previsoes(self) -> Optional[pd.DataFrame]: return self.__df_previsoes_semanais @previsoes.setter - def previsoes(self, df: pd.DataFrame): + def previsoes(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: n_postos = dados.numero_postos @@ -146,7 +146,7 @@ def previsoes(self, df: pd.DataFrame): self.__df_previsoes_semanais = None @property - def previsoes_com_postos_artificiais(self) -> Optional[pd.DataFrame]: + def previsoes_com_postos_artificiais(self) -> pd.DataFrame | None: """ Obtém a tabela com as vazões previstas de cada nó da árvore de cenários, para a etapa determinística da execução, por posto. @@ -178,7 +178,7 @@ def previsoes_com_postos_artificiais(self) -> Optional[pd.DataFrame]: return self.__df_previsoes_semanais_com_postos_artificiais @previsoes_com_postos_artificiais.setter - def previsoes_com_postos_artificiais(self, df: pd.DataFrame): + def previsoes_com_postos_artificiais(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: n_postos = dados.numero_postos @@ -195,7 +195,7 @@ def previsoes_com_postos_artificiais(self, df: pd.DataFrame): self.__df_previsoes_semanais_com_postos_artificiais = None @property - def cenarios_gerados(self) -> Optional[pd.DataFrame]: + def cenarios_gerados(self) -> pd.DataFrame | None: """ Obtém a tabela com as cenários gerados de cada nó da árvore de cenários, para a etapa estocástica da execução, por posto. @@ -237,7 +237,7 @@ def cenarios_gerados(self) -> Optional[pd.DataFrame]: return self.__df_cenarios_mensais_gerados @cenarios_gerados.setter - def cenarios_gerados(self, df: pd.DataFrame): + def cenarios_gerados(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: n_postos = dados.numero_postos @@ -250,7 +250,7 @@ def cenarios_gerados(self, df: pd.DataFrame): @property def cenarios_calculados_com_postos_artificiais( self, - ) -> Optional[pd.DataFrame]: + ) -> pd.DataFrame | None: """ Obtém a tabela com as cenários gerados de cada nó da árvore de cenários, para a etapa estocástica da execução, por posto. @@ -293,7 +293,9 @@ def cenarios_calculados_com_postos_artificiais( return self.__df_cenarios_mensais_calculados_com_postos_artificiais @cenarios_calculados_com_postos_artificiais.setter - def cenarios_calculados_com_postos_artificiais(self, df: pd.DataFrame): + def cenarios_calculados_com_postos_artificiais( + self, df: pd.DataFrame + ) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: n_postos = dados.numero_postos @@ -304,7 +306,7 @@ def cenarios_calculados_com_postos_artificiais(self, df: pd.DataFrame): self.__df_cenarios_mensais_calculados_com_postos_artificiais = None @property - def observacoes_semanais(self) -> Optional[pd.DataFrame]: + def observacoes_semanais(self) -> pd.DataFrame | None: """ Obtém a tabela com as observações semanais de cada nó da árvore de cenários, por posto. @@ -333,7 +335,7 @@ def observacoes_semanais(self) -> Optional[pd.DataFrame]: return self.__df_observacoes_semanais @observacoes_semanais.setter - def observacoes_semanais(self, df: pd.DataFrame): + def observacoes_semanais(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: n_postos = dados.numero_postos @@ -350,7 +352,7 @@ def observacoes_semanais(self, df: pd.DataFrame): self.__df_observacoes_semanais = None @property - def observacoes_mensais(self) -> Optional[pd.DataFrame]: + def observacoes_mensais(self) -> pd.DataFrame | None: """ Obtém a tabela com as observações mensais de cada nó da árvore de cenários, por posto. @@ -379,7 +381,7 @@ def observacoes_mensais(self) -> Optional[pd.DataFrame]: return self.__df_observacoes_mensais @observacoes_mensais.setter - def observacoes_mensais(self, df: pd.DataFrame): + def observacoes_mensais(self, df: pd.DataFrame) -> None: dados = self.__obtem_secao_vazoes() if dados is not None: n_postos = dados.numero_postos diff --git a/idecomp/libs/modelos/restricoes.py b/idecomp/libs/modelos/restricoes.py index 5bd0fdc..fe94b43 100644 --- a/idecomp/libs/modelos/restricoes.py +++ b/idecomp/libs/modelos/restricoes.py @@ -1,11 +1,12 @@ -from typing import Optional -from cfinterface.components.register import Register -from cfinterface.components.line import Line +from datetime import datetime + +from cfinterface.components.datetimefield import DatetimeField +from cfinterface.components.floatfield import FloatField from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField -from cfinterface.components.floatfield import FloatField -from cfinterface.components.datetimefield import DatetimeField -from datetime import datetime +from cfinterface.components.register import Register + from idecomp.config import FORMATO_CAMPOS_DATA_LIBS @@ -30,7 +31,7 @@ class RegistroRestricaoEletricaHorizontePeriodo(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -40,11 +41,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da validade da restrição. @@ -54,11 +55,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da validade da restrição. @@ -68,7 +69,7 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, n: int): + def estagio_fim(self, n: int) -> None: self.data[2] = n @@ -93,7 +94,7 @@ class RegistroRestricaoEletricaHorizonteData(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -103,11 +104,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início da validade da restrição. @@ -117,11 +118,11 @@ def data_inicio(self) -> Optional[datetime]: return self.data[1] @data_inicio.setter - def data_inicio(self, c: datetime): + def data_inicio(self, c: datetime) -> None: self.data[1] = c @property - def data_fim(self) -> Optional[datetime]: + def data_fim(self) -> datetime | None: """ A data de fim da validade da restrição. @@ -131,7 +132,7 @@ def data_fim(self) -> Optional[datetime]: return self.data[2] @data_fim.setter - def data_fim(self, n: datetime): + def data_fim(self, n: datetime) -> None: self.data[2] = n @@ -154,7 +155,7 @@ class RegistroRestricaoEletricaFormula(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -164,11 +165,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def formula(self) -> Optional[str]: + def formula(self) -> str | None: """ A fórmula da restrição. @@ -178,7 +179,7 @@ def formula(self) -> Optional[str]: return self.data[1] @formula.setter - def formula(self, n: str): + def formula(self, n: str) -> None: self.data[1] = n @@ -205,7 +206,7 @@ class RegistroRestricaoEletricaFormulaPeriodoPatamar(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -215,11 +216,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da restrição. @@ -229,11 +230,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da restrição. @@ -243,11 +244,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, c: int): + def estagio_fim(self, c: int) -> None: self.data[2] = c @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar de carga. @@ -257,11 +258,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, c: int): + def patamar(self, c: int) -> None: self.data[3] = c @property - def formula(self) -> Optional[str]: + def formula(self) -> str | None: """ A fórmula da restrição. @@ -271,7 +272,7 @@ def formula(self) -> Optional[str]: return self.data[4] @formula.setter - def formula(self, n: str): + def formula(self, n: str) -> None: self.data[4] = n @@ -298,7 +299,7 @@ class RegistroRestricaoEletricaFormulaDataPatamar(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -308,11 +309,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início da restrição. @@ -322,11 +323,11 @@ def data_inicio(self) -> Optional[datetime]: return self.data[1] @data_inicio.setter - def data_inicio(self, c: datetime): + def data_inicio(self, c: datetime) -> None: self.data[1] = c @property - def data_fim(self) -> Optional[datetime]: + def data_fim(self) -> datetime | None: """ A data de fim da restrição. @@ -336,11 +337,11 @@ def data_fim(self) -> Optional[datetime]: return self.data[2] @data_fim.setter - def data_fim(self, c: datetime): + def data_fim(self, c: datetime) -> None: self.data[2] = c @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar de carga. @@ -350,11 +351,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, c: int): + def patamar(self, c: int) -> None: self.data[3] = c @property - def formula(self) -> Optional[str]: + def formula(self) -> str | None: """ A fórmula da restrição. @@ -364,7 +365,7 @@ def formula(self) -> Optional[str]: return self.data[4] @formula.setter - def formula(self, n: str): + def formula(self, n: str) -> None: self.data[4] = n @@ -392,7 +393,7 @@ class RegistroRestricaoEletricaLimitesFormulaPeriodoPatamar(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -402,11 +403,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da validade dos limites da restrição. @@ -416,11 +417,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da validade dos limites da restrição. @@ -430,11 +431,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, v: int): + def estagio_fim(self, v: int) -> None: self.data[2] = v @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar para os limites. @@ -444,11 +445,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, v: int): + def patamar(self, v: int) -> None: self.data[3] = v @property - def limite_inferior(self) -> Optional[str]: + def limite_inferior(self) -> str | None: """ A equação que da o limite inferior da restrição. @@ -458,11 +459,11 @@ def limite_inferior(self) -> Optional[str]: return self.data[4] @limite_inferior.setter - def limite_inferior(self, v: str): + def limite_inferior(self, v: str) -> None: self.data[4] = v @property - def limite_superior(self) -> Optional[str]: + def limite_superior(self) -> str | None: """ A equação que da o limite superior da restrição. @@ -472,7 +473,7 @@ def limite_superior(self) -> Optional[str]: return self.data[5] @limite_superior.setter - def limite_superior(self, v: str): + def limite_superior(self, v: str) -> None: self.data[5] = v @@ -500,7 +501,7 @@ class RegistroRestricaoEletricaLimitesFormulaDataPatamar(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -510,11 +511,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início da validade dos limites da restrição. @@ -524,11 +525,11 @@ def data_inicio(self) -> Optional[datetime]: return self.data[1] @data_inicio.setter - def data_inicio(self, c: datetime): + def data_inicio(self, c: datetime) -> None: self.data[1] = c @property - def data_fim(self) -> Optional[datetime]: + def data_fim(self) -> datetime | None: """ A data de fim da validade dos limites da restrição. @@ -538,11 +539,11 @@ def data_fim(self) -> Optional[datetime]: return self.data[2] @data_fim.setter - def data_fim(self, v: datetime): + def data_fim(self, v: datetime) -> None: self.data[2] = v @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar para os limites. @@ -552,11 +553,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, v: int): + def patamar(self, v: int) -> None: self.data[3] = v @property - def limite_inferior(self) -> Optional[str]: + def limite_inferior(self) -> str | None: """ A equação que da o limite inferior da restrição. @@ -566,11 +567,11 @@ def limite_inferior(self) -> Optional[str]: return self.data[4] @limite_inferior.setter - def limite_inferior(self, v: str): + def limite_inferior(self, v: str) -> None: self.data[4] = v @property - def limite_superior(self) -> Optional[str]: + def limite_superior(self) -> str | None: """ A equação que da o limite superior da restrição. @@ -580,7 +581,7 @@ def limite_superior(self) -> Optional[str]: return self.data[5] @limite_superior.setter - def limite_superior(self, v: str): + def limite_superior(self, v: str) -> None: self.data[5] = v @@ -603,7 +604,7 @@ class RegistroAliasEletrico(Register): ) @property - def codigo_alias(self) -> Optional[int]: + def codigo_alias(self) -> int | None: """ O código do alias. @@ -613,11 +614,11 @@ def codigo_alias(self) -> Optional[int]: return self.data[0] @codigo_alias.setter - def codigo_alias(self, c: int): + def codigo_alias(self, c: int) -> None: self.data[0] = c @property - def identificador_alias(self) -> Optional[str]: + def identificador_alias(self) -> str | None: """ O identificador do alias elétrico personalizado. @@ -627,7 +628,7 @@ def identificador_alias(self) -> Optional[str]: return self.data[1] @identificador_alias.setter - def identificador_alias(self, n: str): + def identificador_alias(self, n: str) -> None: self.data[1] = n @@ -654,7 +655,7 @@ class RegistroAliasEletricoValorPeriodoPatamar(Register): ) @property - def codigo_alias(self) -> Optional[int]: + def codigo_alias(self) -> int | None: """ O código do alias. @@ -664,11 +665,11 @@ def codigo_alias(self) -> Optional[int]: return self.data[0] @codigo_alias.setter - def codigo_alias(self, c: int): + def codigo_alias(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da validade do dado. @@ -678,11 +679,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da validade do dado. @@ -692,11 +693,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, v: int): + def estagio_fim(self, v: int) -> None: self.data[2] = v @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar. @@ -706,11 +707,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, v: int): + def patamar(self, v: int) -> None: self.data[3] = v @property - def valor(self) -> Optional[float]: + def valor(self) -> float | None: """ O valor do alias elétrico. @@ -720,7 +721,7 @@ def valor(self) -> Optional[float]: return self.data[4] @valor.setter - def valor(self, v: float): + def valor(self, v: float) -> None: self.data[4] = v @@ -744,7 +745,7 @@ class RegistroRestricaoEletricaRegraAtivacao(Register): ) @property - def codigo_regra_ativacao(self) -> Optional[int]: + def codigo_regra_ativacao(self) -> int | None: """ O código da regra de ativação. @@ -754,11 +755,11 @@ def codigo_regra_ativacao(self) -> Optional[int]: return self.data[0] @codigo_regra_ativacao.setter - def codigo_regra_ativacao(self, c: int): + def codigo_regra_ativacao(self, c: int) -> None: self.data[0] = c @property - def regra_ativacao(self) -> Optional[str]: + def regra_ativacao(self) -> str | None: """ A regra condicional para ativação. @@ -768,7 +769,7 @@ def regra_ativacao(self) -> Optional[str]: return self.data[1] @regra_ativacao.setter - def regra_ativacao(self, n: str): + def regra_ativacao(self, n: str) -> None: self.data[1] = n @@ -792,7 +793,7 @@ class RegistroRestricaoEletricaHabilita(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -802,11 +803,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def codigo_regra_ativacao(self) -> Optional[int]: + def codigo_regra_ativacao(self) -> int | None: """ O código da regra de ativação. @@ -816,7 +817,7 @@ def codigo_regra_ativacao(self) -> Optional[int]: return self.data[1] @codigo_regra_ativacao.setter - def codigo_regra_ativacao(self, n: int): + def codigo_regra_ativacao(self, n: int) -> None: self.data[1] = n @@ -841,7 +842,7 @@ class RegistroRestricaoEletricaTratamentoViolacao(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -851,11 +852,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def tipo_violacao(self) -> Optional[str]: + def tipo_violacao(self) -> str | None: """ O tipo de violação. @@ -865,11 +866,11 @@ def tipo_violacao(self) -> Optional[str]: return self.data[1] @tipo_violacao.setter - def tipo_violacao(self, n: str): + def tipo_violacao(self, n: str) -> None: self.data[1] = n @property - def custo_violacao(self) -> Optional[float]: + def custo_violacao(self) -> float | None: """ O custo de violação. @@ -879,7 +880,7 @@ def custo_violacao(self) -> Optional[float]: return self.data[2] @custo_violacao.setter - def custo_violacao(self, n: float): + def custo_violacao(self, n: float) -> None: self.data[2] = n @@ -907,7 +908,7 @@ class RegistroRestricaoEletricaTratamentoViolacaoPeriodo(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -917,11 +918,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início. @@ -931,11 +932,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim. @@ -945,11 +946,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, c: int): + def estagio_fim(self, c: int) -> None: self.data[2] = c @property - def tipo_violacao(self) -> Optional[str]: + def tipo_violacao(self) -> str | None: """ O tipo de violação. @@ -959,11 +960,11 @@ def tipo_violacao(self) -> Optional[str]: return self.data[3] @tipo_violacao.setter - def tipo_violacao(self, n: str): + def tipo_violacao(self, n: str) -> None: self.data[3] = n @property - def custo_violacao(self) -> Optional[float]: + def custo_violacao(self) -> float | None: """ O custo de violação. @@ -973,7 +974,7 @@ def custo_violacao(self) -> Optional[float]: return self.data[4] @custo_violacao.setter - def custo_violacao(self, n: float): + def custo_violacao(self, n: float) -> None: self.data[4] = n @@ -998,7 +999,7 @@ class RegistroReHorizPer(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1008,11 +1009,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da validade da restrição. @@ -1022,11 +1023,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da validade da restrição. @@ -1036,7 +1037,7 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, n: int): + def estagio_fim(self, n: int) -> None: self.data[2] = n @@ -1061,7 +1062,7 @@ class RegistroReHorizData(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1071,11 +1072,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início da validade da restrição. @@ -1085,11 +1086,11 @@ def data_inicio(self) -> Optional[datetime]: return self.data[1] @data_inicio.setter - def data_inicio(self, c: datetime): + def data_inicio(self, c: datetime) -> None: self.data[1] = c @property - def data_fim(self) -> Optional[datetime]: + def data_fim(self) -> datetime | None: """ A data de fim da validade da restrição. @@ -1099,7 +1100,7 @@ def data_fim(self) -> Optional[datetime]: return self.data[2] @data_fim.setter - def data_fim(self, n: datetime): + def data_fim(self, n: datetime) -> None: self.data[2] = n @@ -1122,7 +1123,7 @@ class RegistroRe(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1132,11 +1133,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def formula(self) -> Optional[str]: + def formula(self) -> str | None: """ A fórmula da restrição. @@ -1146,7 +1147,7 @@ def formula(self) -> Optional[str]: return self.data[1] @formula.setter - def formula(self, n: str): + def formula(self, n: str) -> None: self.data[1] = n @@ -1173,7 +1174,7 @@ class RegistroRePerPat(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1183,11 +1184,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da restrição. @@ -1197,11 +1198,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da restrição. @@ -1211,11 +1212,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, c: int): + def estagio_fim(self, c: int) -> None: self.data[2] = c @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar de carga. @@ -1225,11 +1226,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, c: int): + def patamar(self, c: int) -> None: self.data[3] = c @property - def formula(self) -> Optional[str]: + def formula(self) -> str | None: """ A fórmula da restrição. @@ -1239,7 +1240,7 @@ def formula(self) -> Optional[str]: return self.data[4] @formula.setter - def formula(self, n: str): + def formula(self, n: str) -> None: self.data[4] = n @@ -1266,7 +1267,7 @@ class RegistroReDataPat(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1276,11 +1277,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início da restrição. @@ -1290,11 +1291,11 @@ def data_inicio(self) -> Optional[datetime]: return self.data[1] @data_inicio.setter - def data_inicio(self, c: datetime): + def data_inicio(self, c: datetime) -> None: self.data[1] = c @property - def data_fim(self) -> Optional[datetime]: + def data_fim(self) -> datetime | None: """ A data de fim da restrição. @@ -1304,11 +1305,11 @@ def data_fim(self) -> Optional[datetime]: return self.data[2] @data_fim.setter - def data_fim(self, c: datetime): + def data_fim(self, c: datetime) -> None: self.data[2] = c @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar de carga. @@ -1318,11 +1319,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, c: int): + def patamar(self, c: int) -> None: self.data[3] = c @property - def formula(self) -> Optional[str]: + def formula(self) -> str | None: """ A fórmula da restrição. @@ -1332,7 +1333,7 @@ def formula(self) -> Optional[str]: return self.data[4] @formula.setter - def formula(self, n: str): + def formula(self, n: str) -> None: self.data[4] = n @@ -1360,7 +1361,7 @@ class RegistroReLimFormPerPat(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1370,11 +1371,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da validade dos limites da restrição. @@ -1384,11 +1385,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da validade dos limites da restrição. @@ -1398,11 +1399,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, v: int): + def estagio_fim(self, v: int) -> None: self.data[2] = v @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar para os limites. @@ -1412,11 +1413,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, v: int): + def patamar(self, v: int) -> None: self.data[3] = v @property - def limite_inferior(self) -> Optional[str]: + def limite_inferior(self) -> str | None: """ A equação que da o limite inferior da restrição. @@ -1426,11 +1427,11 @@ def limite_inferior(self) -> Optional[str]: return self.data[4] @limite_inferior.setter - def limite_inferior(self, v: str): + def limite_inferior(self, v: str) -> None: self.data[4] = v @property - def limite_superior(self) -> Optional[str]: + def limite_superior(self) -> str | None: """ A equação que da o limite superior da restrição. @@ -1440,7 +1441,7 @@ def limite_superior(self) -> Optional[str]: return self.data[5] @limite_superior.setter - def limite_superior(self, v: str): + def limite_superior(self, v: str) -> None: self.data[5] = v @@ -1468,7 +1469,7 @@ class RegistroReLimFormDataPat(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1478,11 +1479,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def data_inicio(self) -> Optional[datetime]: + def data_inicio(self) -> datetime | None: """ A data de início da validade dos limites da restrição. @@ -1492,11 +1493,11 @@ def data_inicio(self) -> Optional[datetime]: return self.data[1] @data_inicio.setter - def data_inicio(self, c: datetime): + def data_inicio(self, c: datetime) -> None: self.data[1] = c @property - def data_fim(self) -> Optional[datetime]: + def data_fim(self) -> datetime | None: """ A data de fim da validade dos limites da restrição. @@ -1506,11 +1507,11 @@ def data_fim(self) -> Optional[datetime]: return self.data[2] @data_fim.setter - def data_fim(self, v: datetime): + def data_fim(self, v: datetime) -> None: self.data[2] = v @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar para os limites. @@ -1520,11 +1521,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, v: int): + def patamar(self, v: int) -> None: self.data[3] = v @property - def limite_inferior(self) -> Optional[str]: + def limite_inferior(self) -> str | None: """ A equação que da o limite inferior da restrição. @@ -1534,11 +1535,11 @@ def limite_inferior(self) -> Optional[str]: return self.data[4] @limite_inferior.setter - def limite_inferior(self, v: str): + def limite_inferior(self, v: str) -> None: self.data[4] = v @property - def limite_superior(self) -> Optional[str]: + def limite_superior(self) -> str | None: """ A equação que da o limite superior da restrição. @@ -1548,7 +1549,7 @@ def limite_superior(self) -> Optional[str]: return self.data[5] @limite_superior.setter - def limite_superior(self, v: str): + def limite_superior(self, v: str) -> None: self.data[5] = v @@ -1571,7 +1572,7 @@ class RegistroAliasElet(Register): ) @property - def codigo_alias(self) -> Optional[int]: + def codigo_alias(self) -> int | None: """ O código do alias. @@ -1581,11 +1582,11 @@ def codigo_alias(self) -> Optional[int]: return self.data[0] @codigo_alias.setter - def codigo_alias(self, c: int): + def codigo_alias(self, c: int) -> None: self.data[0] = c @property - def identificador_alias(self) -> Optional[str]: + def identificador_alias(self) -> str | None: """ O identificador do alias elétrico personalizado. @@ -1595,7 +1596,7 @@ def identificador_alias(self) -> Optional[str]: return self.data[1] @identificador_alias.setter - def identificador_alias(self, n: str): + def identificador_alias(self, n: str) -> None: self.data[1] = n @@ -1622,7 +1623,7 @@ class RegistroAliasEletValPerPat(Register): ) @property - def codigo_alias(self) -> Optional[int]: + def codigo_alias(self) -> int | None: """ O código do alias. @@ -1632,11 +1633,11 @@ def codigo_alias(self) -> Optional[int]: return self.data[0] @codigo_alias.setter - def codigo_alias(self, c: int): + def codigo_alias(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início da validade do dado. @@ -1646,11 +1647,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim da validade do dado. @@ -1660,11 +1661,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, v: int): + def estagio_fim(self, v: int) -> None: self.data[2] = v @property - def patamar(self) -> Optional[int]: + def patamar(self) -> int | None: """ O índice do patamar. @@ -1674,11 +1675,11 @@ def patamar(self) -> Optional[int]: return self.data[3] @patamar.setter - def patamar(self, v: int): + def patamar(self, v: int) -> None: self.data[3] = v @property - def valor(self) -> Optional[float]: + def valor(self) -> float | None: """ O valor do alias elétrico. @@ -1688,7 +1689,7 @@ def valor(self) -> Optional[float]: return self.data[4] @valor.setter - def valor(self, v: float): + def valor(self, v: float) -> None: self.data[4] = v @@ -1712,7 +1713,7 @@ class RegistroReRegraAtiva(Register): ) @property - def codigo_regra_ativacao(self) -> Optional[int]: + def codigo_regra_ativacao(self) -> int | None: """ O código da regra de ativação. @@ -1722,11 +1723,11 @@ def codigo_regra_ativacao(self) -> Optional[int]: return self.data[0] @codigo_regra_ativacao.setter - def codigo_regra_ativacao(self, c: int): + def codigo_regra_ativacao(self, c: int) -> None: self.data[0] = c @property - def regra_ativacao(self) -> Optional[str]: + def regra_ativacao(self) -> str | None: """ A regra condicional para ativação. @@ -1736,7 +1737,7 @@ def regra_ativacao(self) -> Optional[str]: return self.data[1] @regra_ativacao.setter - def regra_ativacao(self, n: str): + def regra_ativacao(self, n: str) -> None: self.data[1] = n @@ -1760,7 +1761,7 @@ class RegistroReHabilita(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1770,11 +1771,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def codigo_regra_ativacao(self) -> Optional[int]: + def codigo_regra_ativacao(self) -> int | None: """ O código da regra de ativação. @@ -1784,7 +1785,7 @@ def codigo_regra_ativacao(self) -> Optional[int]: return self.data[1] @codigo_regra_ativacao.setter - def codigo_regra_ativacao(self, n: int): + def codigo_regra_ativacao(self, n: int) -> None: self.data[1] = n @@ -1809,7 +1810,7 @@ class RegistroReTratViol(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1819,11 +1820,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def tipo_violacao(self) -> Optional[str]: + def tipo_violacao(self) -> str | None: """ O tipo de violação. @@ -1833,11 +1834,11 @@ def tipo_violacao(self) -> Optional[str]: return self.data[1] @tipo_violacao.setter - def tipo_violacao(self, n: str): + def tipo_violacao(self, n: str) -> None: self.data[1] = n @property - def custo_violacao(self) -> Optional[float]: + def custo_violacao(self) -> float | None: """ O custo de violação. @@ -1847,7 +1848,7 @@ def custo_violacao(self) -> Optional[float]: return self.data[2] @custo_violacao.setter - def custo_violacao(self, n: float): + def custo_violacao(self, n: float) -> None: self.data[2] = n @@ -1875,7 +1876,7 @@ class RegistroReTratViolPer(Register): ) @property - def codigo_restricao(self) -> Optional[int]: + def codigo_restricao(self) -> int | None: """ O código da restrição. @@ -1885,11 +1886,11 @@ def codigo_restricao(self) -> Optional[int]: return self.data[0] @codigo_restricao.setter - def codigo_restricao(self, c: int): + def codigo_restricao(self, c: int) -> None: self.data[0] = c @property - def estagio_inicio(self) -> Optional[int]: + def estagio_inicio(self) -> int | None: """ O estágio de início. @@ -1899,11 +1900,11 @@ def estagio_inicio(self) -> Optional[int]: return self.data[1] @estagio_inicio.setter - def estagio_inicio(self, c: int): + def estagio_inicio(self, c: int) -> None: self.data[1] = c @property - def estagio_fim(self) -> Optional[int]: + def estagio_fim(self) -> int | None: """ O estágio de fim. @@ -1913,11 +1914,11 @@ def estagio_fim(self) -> Optional[int]: return self.data[2] @estagio_fim.setter - def estagio_fim(self, c: int): + def estagio_fim(self, c: int) -> None: self.data[2] = c @property - def tipo_violacao(self) -> Optional[str]: + def tipo_violacao(self) -> str | None: """ O tipo de violação. @@ -1927,11 +1928,11 @@ def tipo_violacao(self) -> Optional[str]: return self.data[3] @tipo_violacao.setter - def tipo_violacao(self, n: str): + def tipo_violacao(self, n: str) -> None: self.data[3] = n @property - def custo_violacao(self) -> Optional[float]: + def custo_violacao(self) -> float | None: """ O custo de violação. @@ -1941,5 +1942,5 @@ def custo_violacao(self) -> Optional[float]: return self.data[4] @custo_violacao.setter - def custo_violacao(self, n: float): + def custo_violacao(self, n: float) -> None: self.data[4] = n diff --git a/idecomp/libs/modelos/usinas_hidreletricas.py b/idecomp/libs/modelos/usinas_hidreletricas.py index 97f96c5..1a0a2da 100644 --- a/idecomp/libs/modelos/usinas_hidreletricas.py +++ b/idecomp/libs/modelos/usinas_hidreletricas.py @@ -1,10 +1,8 @@ -from cfinterface.components.register import Register -from cfinterface.components.line import Line -from cfinterface.components.integerfield import IntegerField from cfinterface.components.floatfield import FloatField +from cfinterface.components.integerfield import IntegerField +from cfinterface.components.line import Line from cfinterface.components.literalfield import LiteralField - -from typing import Optional +from cfinterface.components.register import Register class HidreletricaCurvaJusante(Register): @@ -24,7 +22,7 @@ class HidreletricaCurvaJusante(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da usina hidrelétrica relacionada ao polinômio. @@ -34,11 +32,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def indice_familia(self) -> Optional[int]: + def indice_familia(self) -> int | None: """ O índice da família de polinômios. @@ -48,11 +46,11 @@ def indice_familia(self) -> Optional[int]: return self.data[1] @indice_familia.setter - def indice_familia(self, c: int): + def indice_familia(self, c: int) -> None: self.data[1] = c @property - def nivel_montante_referencia(self) -> Optional[float]: + def nivel_montante_referencia(self) -> float | None: """ O nível de montante da usina de jusante de referência. @@ -63,7 +61,7 @@ def nivel_montante_referencia(self) -> Optional[float]: return self.data[2] @nivel_montante_referencia.setter - def nivel_montante_referencia(self, c: float): + def nivel_montante_referencia(self, c: float) -> None: self.data[2] = c @@ -84,7 +82,7 @@ class HidreletricaCurvaJusantePolinomioPorPartes(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da usina hidrelétrica relacionada ao polinômio. @@ -94,11 +92,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def indice_familia(self) -> Optional[int]: + def indice_familia(self) -> int | None: """ O índice da família de polinômios. @@ -108,11 +106,11 @@ def indice_familia(self) -> Optional[int]: return self.data[1] @indice_familia.setter - def indice_familia(self, c: int): + def indice_familia(self, c: int) -> None: self.data[1] = c @property - def numero_polinomios(self) -> Optional[int]: + def numero_polinomios(self) -> int | None: """ O número de polinômios existentes na família. @@ -122,7 +120,7 @@ def numero_polinomios(self) -> Optional[int]: return self.data[2] @numero_polinomios.setter - def numero_polinomios(self, c: int): + def numero_polinomios(self, c: int) -> None: self.data[2] = c @@ -150,7 +148,7 @@ class HidreletricaCurvaJusantePolinomioPorPartesSegmento(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da usina hidrelétrica relacionada ao polinômio. @@ -160,11 +158,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def indice_familia(self) -> Optional[int]: + def indice_familia(self) -> int | None: """ O índice da família de polinômios. @@ -174,11 +172,11 @@ def indice_familia(self) -> Optional[int]: return self.data[1] @indice_familia.setter - def indice_familia(self, c: int): + def indice_familia(self, c: int) -> None: self.data[1] = c @property - def indice_polinomio(self) -> Optional[int]: + def indice_polinomio(self) -> int | None: """ O índice do polinômio da respectiva família. @@ -188,11 +186,11 @@ def indice_polinomio(self) -> Optional[int]: return self.data[2] @indice_polinomio.setter - def indice_polinomio(self, c: int): + def indice_polinomio(self, c: int) -> None: self.data[2] = c @property - def limite_inferior_vazao_jusante(self) -> Optional[float]: + def limite_inferior_vazao_jusante(self) -> float | None: """ O limite inferior de vazão de jusante (defluência mais lateral) para janela de validade do polinômio. @@ -203,11 +201,11 @@ def limite_inferior_vazao_jusante(self) -> Optional[float]: return self.data[3] @limite_inferior_vazao_jusante.setter - def limite_inferior_vazao_jusante(self, c: float): + def limite_inferior_vazao_jusante(self, c: float) -> None: self.data[3] = c @property - def limite_superior_vazao_jusante(self) -> Optional[float]: + def limite_superior_vazao_jusante(self) -> float | None: """ O limite superior de vazão de jusante (defluência mais lateral) para janela de validade do polinômio. @@ -218,11 +216,11 @@ def limite_superior_vazao_jusante(self) -> Optional[float]: return self.data[4] @limite_superior_vazao_jusante.setter - def limite_superior_vazao_jusante(self, c: float): + def limite_superior_vazao_jusante(self, c: float) -> None: self.data[4] = c @property - def coeficiente_a0(self) -> Optional[float]: + def coeficiente_a0(self) -> float | None: """ O coeficiente de grau 0 do polinômio. @@ -232,11 +230,11 @@ def coeficiente_a0(self) -> Optional[float]: return self.data[5] @coeficiente_a0.setter - def coeficiente_a0(self, c: float): + def coeficiente_a0(self, c: float) -> None: self.data[5] = c @property - def coeficiente_a1(self) -> Optional[float]: + def coeficiente_a1(self) -> float | None: """ O coeficiente de grau 1 do polinômio. @@ -246,11 +244,11 @@ def coeficiente_a1(self) -> Optional[float]: return self.data[6] @coeficiente_a1.setter - def coeficiente_a1(self, c: float): + def coeficiente_a1(self, c: float) -> None: self.data[6] = c @property - def coeficiente_a2(self) -> Optional[float]: + def coeficiente_a2(self) -> float | None: """ O coeficiente de grau 2 do polinômio. @@ -260,11 +258,11 @@ def coeficiente_a2(self) -> Optional[float]: return self.data[7] @coeficiente_a2.setter - def coeficiente_a2(self, c: float): + def coeficiente_a2(self, c: float) -> None: self.data[7] = c @property - def coeficiente_a3(self) -> Optional[float]: + def coeficiente_a3(self) -> float | None: """ O coeficiente de grau 3 do polinômio. @@ -274,11 +272,11 @@ def coeficiente_a3(self) -> Optional[float]: return self.data[8] @coeficiente_a3.setter - def coeficiente_a3(self, c: float): + def coeficiente_a3(self, c: float) -> None: self.data[8] = c @property - def coeficiente_a4(self) -> Optional[float]: + def coeficiente_a4(self) -> float | None: """ O coeficiente de grau 4 do polinômio. @@ -288,7 +286,7 @@ def coeficiente_a4(self) -> Optional[float]: return self.data[9] @coeficiente_a4.setter - def coeficiente_a4(self, c: float): + def coeficiente_a4(self, c: float) -> None: self.data[9] = c @@ -308,7 +306,7 @@ class HidreletricaCurvaJusanteAfogamentoExplicitoUsina(Register): ) @property - def codigo_usina(self) -> Optional[int]: + def codigo_usina(self) -> int | None: """ O código da usina hidrelétrica relacionada ao polinômio. @@ -318,11 +316,11 @@ def codigo_usina(self) -> Optional[int]: return self.data[0] @codigo_usina.setter - def codigo_usina(self, c: int): + def codigo_usina(self, c: int) -> None: self.data[0] = c @property - def considera_afogamento(self) -> Optional[str]: + def considera_afogamento(self) -> str | None: """ Habilitação do afogamento explícito. @@ -332,7 +330,7 @@ def considera_afogamento(self) -> Optional[str]: return self.data[1] @considera_afogamento.setter - def considera_afogamento(self, c: str): + def considera_afogamento(self, c: str) -> None: self.data[1] = c @@ -351,7 +349,7 @@ class HidreletricaCurvaJusanteAfogamentoExplicitoPadrao(Register): ) @property - def considera_afogamento(self) -> Optional[str]: + def considera_afogamento(self) -> str | None: """ Habilitação do afogamento explícito. @@ -361,5 +359,5 @@ def considera_afogamento(self) -> Optional[str]: return self.data[0] @considera_afogamento.setter - def considera_afogamento(self, c: str): + def considera_afogamento(self, c: str) -> None: self.data[0] = c diff --git a/idecomp/libs/restricoes.py b/idecomp/libs/restricoes.py index c775414..0bc5b68 100644 --- a/idecomp/libs/restricoes.py +++ b/idecomp/libs/restricoes.py @@ -1,24 +1,24 @@ -from typing import Type, TypeVar, List, Optional, Union from datetime import datetime -import pandas as pd # type: ignore +from typing import Any, TypeVar + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.register import Register from cfinterface.files.registerfile import RegisterFile + from idecomp.libs.modelos.restricoes import ( + RegistroAliasElet, + RegistroAliasEletrico, + RegistroAliasEletricoValorPeriodoPatamar, + RegistroAliasEletValPerPat, RegistroRe, - RegistroReHorizPer, - RegistroReHorizData, - RegistroRePerPat, RegistroReDataPat, - RegistroReLimFormPerPat, + RegistroReHabilita, + RegistroReHorizData, + RegistroReHorizPer, RegistroReLimFormDataPat, + RegistroReLimFormPerPat, + RegistroRePerPat, RegistroReRegraAtiva, - RegistroReHabilita, - RegistroReTratViol, - RegistroReTratViolPer, - RegistroAliasElet, - RegistroAliasEletValPerPat, -) -from idecomp.libs.modelos.restricoes import ( RegistroRestricaoEletricaFormula, RegistroRestricaoEletricaFormulaDataPatamar, RegistroRestricaoEletricaFormulaPeriodoPatamar, @@ -30,8 +30,8 @@ RegistroRestricaoEletricaRegraAtivacao, RegistroRestricaoEletricaTratamentoViolacao, RegistroRestricaoEletricaTratamentoViolacaoPeriodo, - RegistroAliasEletrico, - RegistroAliasEletricoValorPeriodoPatamar, + RegistroReTratViol, + RegistroReTratViolPer, ) @@ -72,9 +72,12 @@ class Restricoes(RegisterFile): RegistroRe, ] + def __init__(self, data: Any = ...) -> None: + super().__init__(data) + def __registros_ou_df( - self, t: Type[T], **kwargs - ) -> Optional[Union[T, List[T], pd.DataFrame]]: + self, t: type[T], **kwargs: Any + ) -> T | list[T] | pd.DataFrame | None: if kwargs.get("df"): return self._as_df(t) else: @@ -83,16 +86,15 @@ def __registros_ou_df( def restricao_eletrica_formula( self, - codigo_restricao: Optional[int] = None, - formula: Optional[str] = None, + codigo_restricao: int | None = None, + formula: str | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaFormula, - List[RegistroRestricaoEletricaFormula], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaFormula + | list[RegistroRestricaoEletricaFormula] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra uma restrição elétrica (RE), definido através do nome completo do card. @@ -114,10 +116,10 @@ def restricao_eletrica_formula( def re( self, - codigo_restricao: Optional[int] = None, - formula: Optional[str] = None, + codigo_restricao: int | None = None, + formula: str | None = None, df: bool = False, - ) -> Optional[Union[RegistroRe, List[RegistroRe], pd.DataFrame]]: + ) -> RegistroRe | list[RegistroRe] | pd.DataFrame | None: """ Obtém um registro que cadastra uma restrição elétrica (RE), definido através do nome alternativo (apelido) do card. @@ -139,17 +141,16 @@ def re( def restricao_eletrica_horizonte_periodo( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaHorizontePeriodo, - List[RegistroRestricaoEletricaHorizontePeriodo], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaHorizontePeriodo + | list[RegistroRestricaoEletricaHorizontePeriodo] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra o horizonte de validade de uma restrição elétrica com intervalo de estágios, @@ -175,13 +176,11 @@ def restricao_eletrica_horizonte_periodo( def re_horiz_per( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReHorizPer, List[RegistroReHorizPer], pd.DataFrame] - ]: + ) -> RegistroReHorizPer | list[RegistroReHorizPer] | pd.DataFrame | None: """ Obtém um registro que cadastra o horizonte de validade de uma restrição elétrica com intervalo de estágios, @@ -207,17 +206,16 @@ def re_horiz_per( def restricao_eletrica_horizonte_data( self, - codigo_restricao: Optional[int] = None, - data_inicio: Optional[datetime] = None, - data_fim: Optional[datetime] = None, + codigo_restricao: int | None = None, + data_inicio: datetime | None = None, + data_fim: datetime | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaHorizonteData, - List[RegistroRestricaoEletricaHorizonteData], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaHorizonteData + | list[RegistroRestricaoEletricaHorizonteData] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra o horizonte de validade de uma restrição elétrica com intervalo de data, @@ -243,13 +241,11 @@ def restricao_eletrica_horizonte_data( def re_horiz_data( self, - codigo_restricao: Optional[int] = None, - data_inicio: Optional[datetime] = None, - data_fim: Optional[datetime] = None, + codigo_restricao: int | None = None, + data_inicio: datetime | None = None, + data_fim: datetime | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReHorizData, List[RegistroReHorizData], pd.DataFrame] - ]: + ) -> RegistroReHorizData | list[RegistroReHorizData] | pd.DataFrame | None: """ Obtém um registro que cadastra o horizonte de validade de uma restrição elétrica com intervalo de data, @@ -275,19 +271,18 @@ def re_horiz_data( def restricao_eletrica_formula_periodo_patamar( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - patamar: Optional[int] = None, - formula: Optional[str] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + patamar: int | None = None, + formula: str | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaFormulaPeriodoPatamar, - List[RegistroRestricaoEletricaFormulaPeriodoPatamar], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaFormulaPeriodoPatamar + | list[RegistroRestricaoEletricaFormulaPeriodoPatamar] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra uma restrição elétrica (RE) que varia ao longo do tempo, informada por intervalo de estágios, @@ -320,15 +315,13 @@ def restricao_eletrica_formula_periodo_patamar( def re_per_pat( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - patamar: Optional[int] = None, - formula: Optional[str] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + patamar: int | None = None, + formula: str | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroRePerPat, List[RegistroRePerPat], pd.DataFrame] - ]: + ) -> RegistroRePerPat | list[RegistroRePerPat] | pd.DataFrame | None: """ Obtém um registro que cadastra uma restrição elétrica (RE) que varia ao longo do tempo, informada por intervalo de estágios, @@ -361,19 +354,18 @@ def re_per_pat( def restricao_eletrica_formula_data_patamar( self, - codigo_restricao: Optional[int] = None, - data_inicio: Optional[datetime] = None, - data_fim: Optional[datetime] = None, - patamar: Optional[int] = None, - formula: Optional[str] = None, + codigo_restricao: int | None = None, + data_inicio: datetime | None = None, + data_fim: datetime | None = None, + patamar: int | None = None, + formula: str | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaFormulaDataPatamar, - List[RegistroRestricaoEletricaFormulaDataPatamar], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaFormulaDataPatamar + | list[RegistroRestricaoEletricaFormulaDataPatamar] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra uma restrição elétrica (RE) que varia ao longo do tempo, informada por intervalo de data, @@ -406,15 +398,13 @@ def restricao_eletrica_formula_data_patamar( def re_data_pat( self, - codigo_restricao: Optional[int] = None, - data_inicio: Optional[datetime] = None, - data_fim: Optional[datetime] = None, - patamar: Optional[int] = None, - formula: Optional[str] = None, + codigo_restricao: int | None = None, + data_inicio: datetime | None = None, + data_fim: datetime | None = None, + patamar: int | None = None, + formula: str | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReDataPat, List[RegistroReDataPat], pd.DataFrame] - ]: + ) -> RegistroReDataPat | list[RegistroReDataPat] | pd.DataFrame | None: """ Obtém um registro que cadastra uma restrição elétrica (RE) que varia ao longo do tempo, informada por intervalo de data, @@ -447,20 +437,19 @@ def re_data_pat( def restricao_eletrica_limites_formula_data_patamar( self, - codigo_restricao: Optional[int] = None, - data_inicio: Optional[datetime] = None, - data_fim: Optional[datetime] = None, - patamar: Optional[int] = None, - limite_inferior: Optional[float] = None, - limite_superior: Optional[float] = None, + codigo_restricao: int | None = None, + data_inicio: datetime | None = None, + data_fim: datetime | None = None, + patamar: int | None = None, + limite_inferior: float | None = None, + limite_superior: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaLimitesFormulaDataPatamar, - List[RegistroRestricaoEletricaLimitesFormulaDataPatamar], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaLimitesFormulaDataPatamar + | list[RegistroRestricaoEletricaLimitesFormulaDataPatamar] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra os limites por horizonte, definido por intervalo de data, e por patamar para uma restrição elétrica, @@ -496,20 +485,19 @@ def restricao_eletrica_limites_formula_data_patamar( def re_lim_form_data_pat( self, - codigo_restricao: Optional[int] = None, - data_inicio: Optional[datetime] = None, - data_fim: Optional[datetime] = None, - patamar: Optional[int] = None, - limite_inferior: Optional[float] = None, - limite_superior: Optional[float] = None, + codigo_restricao: int | None = None, + data_inicio: datetime | None = None, + data_fim: datetime | None = None, + patamar: int | None = None, + limite_inferior: float | None = None, + limite_superior: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroReLimFormDataPat, - List[RegistroReLimFormDataPat], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroReLimFormDataPat + | list[RegistroReLimFormDataPat] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra os limites por horizonte, definido por intervalo de data, e por patamar para uma restrição elétrica, @@ -544,20 +532,19 @@ def re_lim_form_data_pat( def restricao_eletrica_limite_formula_periodo_patamar( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - patamar: Optional[int] = None, - limite_inferior: Optional[float] = None, - limite_superior: Optional[float] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + patamar: int | None = None, + limite_inferior: float | None = None, + limite_superior: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaLimitesFormulaPeriodoPatamar, - List[RegistroRestricaoEletricaLimitesFormulaPeriodoPatamar], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaLimitesFormulaPeriodoPatamar + | list[RegistroRestricaoEletricaLimitesFormulaPeriodoPatamar] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra os limites por horizonte, definido por intervalo de estágios, e por patamar para uma restrição elétrica, @@ -593,20 +580,19 @@ def restricao_eletrica_limite_formula_periodo_patamar( def re_lim_form_per_pat( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - patamar: Optional[int] = None, - limite_inferior: Optional[float] = None, - limite_superior: Optional[float] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + patamar: int | None = None, + limite_inferior: float | None = None, + limite_superior: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroReLimFormPerPat, - List[RegistroReLimFormPerPat], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroReLimFormPerPat + | list[RegistroReLimFormPerPat] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra os limites por horizonte, definido por intervalo de estágios, e por patamar para uma restrição elétrica, @@ -641,12 +627,15 @@ def re_lim_form_per_pat( def alias_eletrico( self, - codigo_alias: Optional[int] = None, - identificador_alias: Optional[str] = None, + codigo_alias: int | None = None, + identificador_alias: str | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroAliasEletrico, List[RegistroAliasEletrico], pd.DataFrame] - ]: + ) -> ( + RegistroAliasEletrico + | list[RegistroAliasEletrico] + | pd.DataFrame + | None + ): """ Obtém um registro que cadastra um alias elétrico, definido através do nome completo do card. @@ -668,12 +657,10 @@ def alias_eletrico( def alias_elet( self, - codigo_alias: Optional[int] = None, - identificador_alias: Optional[str] = None, + codigo_alias: int | None = None, + identificador_alias: str | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroAliasElet, List[RegistroAliasElet], pd.DataFrame] - ]: + ) -> RegistroAliasElet | list[RegistroAliasElet] | pd.DataFrame | None: """ Obtém um registro que cadastra um alias elétrico, definido através do nome alternativo (apelido) do card. @@ -695,19 +682,18 @@ def alias_elet( def alias_eletrico_valor_periodo_patamar( self, - codigo_alias: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - patamar: Optional[int] = None, - valor: Optional[float] = None, + codigo_alias: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + patamar: int | None = None, + valor: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroAliasEletricoValorPeriodoPatamar, - List[RegistroAliasEletricoValorPeriodoPatamar], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroAliasEletricoValorPeriodoPatamar + | list[RegistroAliasEletricoValorPeriodoPatamar] + | pd.DataFrame + | None + ): """ Obtém um registro que contém os valores assumidos pelo alias para cada período e patamar, @@ -739,19 +725,18 @@ def alias_eletrico_valor_periodo_patamar( def alias_elet_val_per_pat( self, - codigo_alias: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - patamar: Optional[int] = None, - valor: Optional[float] = None, + codigo_alias: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + patamar: int | None = None, + valor: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroAliasEletValPerPat, - List[RegistroAliasEletValPerPat], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroAliasEletValPerPat + | list[RegistroAliasEletValPerPat] + | pd.DataFrame + | None + ): """ Obtém um registro que contém os valores assumidos pelo alias para cada período e patamar, @@ -783,16 +768,15 @@ def alias_elet_val_per_pat( def restricao_eletrica_regra_ativacao( self, - codigo_regra_ativacao: Optional[int] = None, - regra_ativacao: Optional[str] = None, + codigo_regra_ativacao: int | None = None, + regra_ativacao: str | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaRegraAtivacao, - List[RegistroRestricaoEletricaRegraAtivacao], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaRegraAtivacao + | list[RegistroRestricaoEletricaRegraAtivacao] + | pd.DataFrame + | None + ): """ Obtém um registro que define uma regra para ativação e desativação de restrições elétricas, @@ -815,12 +799,12 @@ def restricao_eletrica_regra_ativacao( def re_regra_ativa( self, - codigo_regra_ativacao: Optional[int] = None, - regra_ativacao: Optional[str] = None, + codigo_regra_ativacao: int | None = None, + regra_ativacao: str | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReRegraAtiva, List[RegistroReRegraAtiva], pd.DataFrame] - ]: + ) -> ( + RegistroReRegraAtiva | list[RegistroReRegraAtiva] | pd.DataFrame | None + ): """ Obtém um registro que define uma regra para ativação e desativação de restrições elétricas, @@ -843,16 +827,15 @@ def re_regra_ativa( def restricao_eletrica_habilita( self, - codigo_restricao: Optional[int] = None, - codigo_regra_ativacao: Optional[int] = None, + codigo_restricao: int | None = None, + codigo_regra_ativacao: int | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaHabilita, - List[RegistroRestricaoEletricaHabilita], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaHabilita + | list[RegistroRestricaoEletricaHabilita] + | pd.DataFrame + | None + ): """ Obtém um registro que contém a associação entre uma regra de ativação e uma restrição elétrica, @@ -875,12 +858,10 @@ def restricao_eletrica_habilita( def re_habilita( self, - codigo_restricao: Optional[int] = None, - codigo_regra_ativacao: Optional[int] = None, + codigo_restricao: int | None = None, + codigo_regra_ativacao: int | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReHabilita, List[RegistroReHabilita], pd.DataFrame] - ]: + ) -> RegistroReHabilita | list[RegistroReHabilita] | pd.DataFrame | None: """ Obtém um registro que contém a associação entre uma regra de ativação e uma restrição elétrica, @@ -903,17 +884,16 @@ def re_habilita( def restricao_eletrica_tratamento_violacao( self, - codigo_restricao: Optional[int] = None, - tipo_violacao: Optional[str] = None, - custo_violacao: Optional[float] = None, + codigo_restricao: int | None = None, + tipo_violacao: str | None = None, + custo_violacao: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaTratamentoViolacao, - List[RegistroRestricaoEletricaTratamentoViolacao], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaTratamentoViolacao + | list[RegistroRestricaoEletricaTratamentoViolacao] + | pd.DataFrame + | None + ): """ Obtém um registro que contém definição do tipo de violação e o valor do custo de violação de uma restrição elétrica, @@ -939,13 +919,11 @@ def restricao_eletrica_tratamento_violacao( def re_trat_viol( self, - codigo_restricao: Optional[int] = None, - tipo_violacao: Optional[str] = None, - custo_violacao: Optional[float] = None, + codigo_restricao: int | None = None, + tipo_violacao: str | None = None, + custo_violacao: float | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReTratViol, List[RegistroReTratViol], pd.DataFrame] - ]: + ) -> RegistroReTratViol | list[RegistroReTratViol] | pd.DataFrame | None: """ Obtém um registro que contém definição do tipo de violação e o valor do custo de violação de uma restrição elétrica, @@ -971,19 +949,18 @@ def re_trat_viol( def restricao_eletrica_tratamento_violacao_periodo( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - tipo_violacao: Optional[str] = None, - custo_violacao: Optional[float] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + tipo_violacao: str | None = None, + custo_violacao: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - RegistroRestricaoEletricaTratamentoViolacaoPeriodo, - List[RegistroRestricaoEletricaTratamentoViolacaoPeriodo], - pd.DataFrame, - ] - ]: + ) -> ( + RegistroRestricaoEletricaTratamentoViolacaoPeriodo + | list[RegistroRestricaoEletricaTratamentoViolacaoPeriodo] + | pd.DataFrame + | None + ): """ Obtém um registro que contém definição do tipo de violação e o valor do custo de violação de uma restrição elétrica definida para um @@ -1017,15 +994,18 @@ def restricao_eletrica_tratamento_violacao_periodo( def re_trat_viol_per( self, - codigo_restricao: Optional[int] = None, - estagio_inicio: Optional[int] = None, - estagio_fim: Optional[int] = None, - tipo_violacao: Optional[str] = None, - custo_violacao: Optional[float] = None, + codigo_restricao: int | None = None, + estagio_inicio: int | None = None, + estagio_fim: int | None = None, + tipo_violacao: str | None = None, + custo_violacao: float | None = None, df: bool = False, - ) -> Optional[ - Union[RegistroReTratViolPer, List[RegistroReTratViolPer], pd.DataFrame] - ]: + ) -> ( + RegistroReTratViolPer + | list[RegistroReTratViolPer] + | pd.DataFrame + | None + ): """ Obtém um registro que contém definição do tipo de violação e o valor do custo de violação de uma restrição elétrica definida para um diff --git a/idecomp/libs/usinas_hidreletricas.py b/idecomp/libs/usinas_hidreletricas.py index f3b63e5..daace4d 100644 --- a/idecomp/libs/usinas_hidreletricas.py +++ b/idecomp/libs/usinas_hidreletricas.py @@ -1,13 +1,15 @@ -from typing import Type, TypeVar, Optional, List, Union +from typing import Any, TypeVar + +import pandas as pd # type: ignore[import-untyped] from cfinterface.components.register import Register from cfinterface.files.registerfile import RegisterFile -import pandas as pd # type: ignore + from idecomp.libs.modelos.usinas_hidreletricas import ( HidreletricaCurvaJusante, + HidreletricaCurvaJusanteAfogamentoExplicitoPadrao, + HidreletricaCurvaJusanteAfogamentoExplicitoUsina, HidreletricaCurvaJusantePolinomioPorPartes, HidreletricaCurvaJusantePolinomioPorPartesSegmento, - HidreletricaCurvaJusanteAfogamentoExplicitoUsina, - HidreletricaCurvaJusanteAfogamentoExplicitoPadrao, ) @@ -27,9 +29,12 @@ class UsinasHidreletricas(RegisterFile): HidreletricaCurvaJusante, ] + def __init__(self, data: Any = ...) -> None: + super().__init__(data) + def __registros_ou_df( - self, t: Type[T], **kwargs - ) -> Optional[Union[T, List[T], pd.DataFrame]]: + self, t: type[T], **kwargs: Any + ) -> T | list[T] | pd.DataFrame | None: if kwargs.get("df"): return self._as_df(t) else: @@ -38,17 +43,16 @@ def __registros_ou_df( def hidreletrica_curvajusante( self, - codigo_usina: Optional[int] = None, - indice_familia: Optional[int] = None, - nivel_montante_referencia: Optional[float] = None, + codigo_usina: int | None = None, + indice_familia: int | None = None, + nivel_montante_referencia: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - HidreletricaCurvaJusante, - List[HidreletricaCurvaJusante], - pd.DataFrame, - ] - ]: + ) -> ( + HidreletricaCurvaJusante + | list[HidreletricaCurvaJusante] + | pd.DataFrame + | None + ): """ Obtém registros que cadastram uma família de curvas de jusante para uma usina hidrelétrica. Opcionalmente, @@ -79,17 +83,16 @@ def hidreletrica_curvajusante( def hidreletrica_curvajusante_polinomio( self, - codigo_usina: Optional[int] = None, - indice_familia: Optional[int] = None, - numero_polinomios: Optional[int] = None, + codigo_usina: int | None = None, + indice_familia: int | None = None, + numero_polinomios: int | None = None, df: bool = False, - ) -> Optional[ - Union[ - HidreletricaCurvaJusantePolinomioPorPartes, - List[HidreletricaCurvaJusantePolinomioPorPartes], - pd.DataFrame, - ] - ]: + ) -> ( + HidreletricaCurvaJusantePolinomioPorPartes + | list[HidreletricaCurvaJusantePolinomioPorPartes] + | pd.DataFrame + | None + ): """ Obtém registros que cadastram uma família de curvas de jusante para uma usina hidrelétrica. Opcionalmente, @@ -119,24 +122,23 @@ def hidreletrica_curvajusante_polinomio( def hidreletrica_curvajusante_polinomio_segmento( self, - codigo_usina: Optional[int] = None, - indice_familia: Optional[int] = None, - indice_polinomio: Optional[int] = None, - limite_inferior_vazao_jusante: Optional[float] = None, - limite_superior_vazao_jusante: Optional[float] = None, - coeficiente_a0: Optional[float] = None, - coeficiente_a1: Optional[float] = None, - coeficiente_a2: Optional[float] = None, - coeficiente_a3: Optional[float] = None, - coeficiente_a4: Optional[float] = None, + codigo_usina: int | None = None, + indice_familia: int | None = None, + indice_polinomio: int | None = None, + limite_inferior_vazao_jusante: float | None = None, + limite_superior_vazao_jusante: float | None = None, + coeficiente_a0: float | None = None, + coeficiente_a1: float | None = None, + coeficiente_a2: float | None = None, + coeficiente_a3: float | None = None, + coeficiente_a4: float | None = None, df: bool = False, - ) -> Optional[ - Union[ - HidreletricaCurvaJusantePolinomioPorPartesSegmento, - List[HidreletricaCurvaJusantePolinomioPorPartesSegmento], - pd.DataFrame, - ] - ]: + ) -> ( + HidreletricaCurvaJusantePolinomioPorPartesSegmento + | list[HidreletricaCurvaJusantePolinomioPorPartesSegmento] + | pd.DataFrame + | None + ): """ Obtém registros que cadastram os polinômios para cada família de curvas de jusante para uma usina hidrelétrica. Opcionalmente, @@ -190,16 +192,15 @@ def hidreletrica_curvajusante_polinomio_segmento( def hidreletrica_curvajusante_afogamentoexplicito_usina( self, - codigo_usina: Optional[int] = None, - considera_afogamento: Optional[str] = None, + codigo_usina: int | None = None, + considera_afogamento: str | None = None, df: bool = False, - ) -> Optional[ - Union[ - HidreletricaCurvaJusanteAfogamentoExplicitoUsina, - List[HidreletricaCurvaJusanteAfogamentoExplicitoUsina], - pd.DataFrame, - ] - ]: + ) -> ( + HidreletricaCurvaJusanteAfogamentoExplicitoUsina + | list[HidreletricaCurvaJusanteAfogamentoExplicitoUsina] + | pd.DataFrame + | None + ): """ Obtém registros que habilitam ou desabilitam a consideração do tratamento do afogamento explícito por usina. Opcionalmente, @@ -223,14 +224,13 @@ def hidreletrica_curvajusante_afogamentoexplicito_usina( ) def hidreletrica_curvajusante_afogamentoexplicito_padrao( - self, considera_afogamento: Optional[str] = None, df: bool = False - ) -> Optional[ - Union[ - HidreletricaCurvaJusanteAfogamentoExplicitoPadrao, - List[HidreletricaCurvaJusanteAfogamentoExplicitoPadrao], - pd.DataFrame, - ] - ]: + self, considera_afogamento: str | None = None, df: bool = False + ) -> ( + HidreletricaCurvaJusanteAfogamentoExplicitoPadrao + | list[HidreletricaCurvaJusanteAfogamentoExplicitoPadrao] + | pd.DataFrame + | None + ): """ Obtém registros que habilitam ou desabilitam a consideração do tratamento do afogamento explícito padrão. diff --git a/pyproject.toml b/pyproject.toml index a511226..1d4e314 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,11 +6,11 @@ build-backend = "hatchling.build" name = "idecomp" dynamic = ["version"] dependencies = [ - "cfinterface>=1.8,<=1.8.3", - "numpy>=2.0", - "pandas>=2.2", + "cfinterface>=1.9.0", + "numpy>=2.2.1", + "pandas>=3.0.0", ] -requires-python = ">= 3.10" +requires-python = ">= 3.11" authors = [ {name = "Rogerio Alves", email = "rogerioalves.ee@gmail.com"}, {name = "Mariana Noel", email = "marianasimoesnoel@gmail.com"}, @@ -22,16 +22,17 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Typing :: Typed", "Operating System :: OS Independent", ] [project.optional-dependencies] -test = ["pytest", "pytest-cov"] -lint = ["ruff", "mypy"] +test = ["pytest", "pytest-cov", "pytest-xdist"] +lint = ["mypy", "pre-commit", "ruff"] docs = [ "sphinx", "furo", @@ -58,15 +59,18 @@ include = [ line-length = 80 [tool.ruff.lint] -select = ["E", "F", "I", "UP"] +per-file-ignores = { "examples/*.py" = ["E402"] } [tool.mypy] -python_version = "3.10" warn_return_any = false -warn_unused_configs = true -strict = true +warn_unused_ignores = true [[tool.mypy.overrides]] -module = "cfinterface.*" -ignore_missing_imports = true +module = [ + "idecomp", + "idecomp.decomp", + "idecomp.decomp.*", + "idecomp.libs.*", +] +strict = true warn_return_any = false diff --git a/tests/libs/test_restricoes.py b/tests/libs/test_restricoes.py index a6c63f9..9a94195 100644 --- a/tests/libs/test_restricoes.py +++ b/tests/libs/test_restricoes.py @@ -683,7 +683,7 @@ def test_neq_restricoes(): with patch("builtins.open", m): cf1 = Restricoes.read(ARQ_TESTE) cf2 = Restricoes.read(ARQ_TESTE) - cf2.data.remove(cf1.re_horiz_per()[0]) + cf2.data.remove(cf2.re_horiz_per()[0]) assert cf1 != cf2 From 1f4948eacb77b2f15d08d71d199761b774a9fbe8 Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 21:12:03 -0300 Subject: [PATCH 7/9] chore: remove plans and agent memory from tracking Add plans/ and .claude/ to .gitignore to keep implementation artifacts out of the repository. Co-Authored-By: Claude Opus 4.6 --- .claude/agent-memory/code-reviewer/MEMORY.md | 59 --- .gitignore | 8 +- .../.implementation-state.json | 409 ------------------ plans/infra-docs-overhaul/00-master-plan.md | 118 ----- plans/infra-docs-overhaul/README.md | 67 --- .../00-epic-overview.md | 38 -- .../ticket-001-modernize-pyproject-toml.md | 144 ------ .../ticket-002-restructure-ci-workflow.md | 111 ----- .../ticket-003-migrate-docs-deployment.md | 124 ------ .../ticket-004-create-release-workflow.md | 139 ------ .../ticket-005-add-pre-commit-hooks.md | 125 ------ .../00-epic-overview.md | 31 -- .../epic-02-sphinx-modernization/learnings.md | 80 ---- ...ticket-006-migrate-sphinx-theme-to-furo.md | 126 ------ ...cket-007-update-sphinx-gallery-examples.md | 135 ------ .../00-epic-overview.md | 36 -- .../ticket-008-create-architecture-page.md | 117 ----- .../ticket-009-create-faq-page.md | 128 ------ .../ticket-010-create-performance-guide.md | 135 ------ ...t-011-improve-api-reference-autosummary.md | 127 ------ .../ticket-012-update-index-toctree.md | 115 ----- .../00-epic-overview.md | 35 -- .../epic-04-repository-polish/learnings.md | 74 ---- .../ticket-013-expand-readme.md | 120 ----- .../ticket-014-create-contributing.md | 127 ------ .../ticket-015-reformat-changelog.md | 154 ------- .../ticket-016-update-installation-docs.md | 138 ------ .../learnings/epic-01-summary.md | 86 ---- .../learnings/epic-02-summary.md | 69 --- .../learnings/epic-03-summary.md | 89 ---- .../learnings/epic-04-summary.md | 94 ---- 31 files changed, 7 insertions(+), 3351 deletions(-) delete mode 100644 .claude/agent-memory/code-reviewer/MEMORY.md delete mode 100644 plans/infra-docs-overhaul/.implementation-state.json delete mode 100644 plans/infra-docs-overhaul/00-master-plan.md delete mode 100644 plans/infra-docs-overhaul/README.md delete mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md delete mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md delete mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md delete mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md delete mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md delete mode 100644 plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md delete mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md delete mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md delete mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md delete mode 100644 plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md delete mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md delete mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md delete mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md delete mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md delete mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md delete mode 100644 plans/infra-docs-overhaul/epic-03-documentation-content/ticket-012-update-index-toctree.md delete mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/00-epic-overview.md delete mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md delete mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md delete mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md delete mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md delete mode 100644 plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md delete mode 100644 plans/infra-docs-overhaul/learnings/epic-01-summary.md delete mode 100644 plans/infra-docs-overhaul/learnings/epic-02-summary.md delete mode 100644 plans/infra-docs-overhaul/learnings/epic-03-summary.md delete mode 100644 plans/infra-docs-overhaul/learnings/epic-04-summary.md diff --git a/.claude/agent-memory/code-reviewer/MEMORY.md b/.claude/agent-memory/code-reviewer/MEMORY.md deleted file mode 100644 index 655fbbd..0000000 --- a/.claude/agent-memory/code-reviewer/MEMORY.md +++ /dev/null @@ -1,59 +0,0 @@ -# Code Reviewer Memory — idecomp project - -## Project Conventions - -- Stack: Python, pandas, numpy, cfinterface framework -- Lint: ruff (default config, F401 not enabled by default) -- Type check: mypy strict on idecomp.decomp._ and idecomp.libs._ -- Test framework: pytest + mock_open patterns for round-trip tests -- Python >= 3.10 (lowercase generics like `list[str]` are fine) -- No `from __future__ import annotations` — not used anywhere in codebase - -## Confirmed Patterns - -### Round-trip test mock_open extraction - -cfinterface TextualRepository calls `open(path, "w", encoding=...)` then `.close()` directly -(no context manager overhead in mock_calls). So mock_calls structure is: - -- [0] call(path, mode, encoding=...) <- open -- [1..n-2] call().write(...) <- writes -- [n-1] call().close() - -`range(1, len(chamadas) - 1)` correctly extracts just the write calls for SectionFile/BlockFile. -`range(2, len(chamadas) - 1)` is used for RegisterFile (dadger, dadgnl) — likely a header/encoding -write at index 1 that should be skipped. - -### pd.concat empty-list guard pattern - -The safe pattern (used in relato.py, custos.py, relgnl.py): - -```python -dfs = [...] -if dfs: - return pd.concat(dfs, ignore_index=True) -return None -``` - -The UNSAFE pattern (found in mapcut modelos after refactor): - -```python -dfs = [...] -return pd.concat(dfs).reset_index(drop=True) # raises ValueError when dfs is [] -``` - -pd.concat([]) raises `ValueError: No objects to concatenate` in pandas >= 2.x. - -### PEP 562 lazy imports (decomp/**init**.py) - -`globals()[name] = value` caching in `__getattr__` is the standard PEP 562 idiom. -`__dir__` returning `__all__` is minimal but documented and acceptable. - -## Common False Positives to Avoid - -- `type: ignore[override]` on cfinterface Block/Section read/write methods: intentional, - cfinterface base signature returns bool but subclasses return None. -- `type: ignore[assignment]` in dadger.py: attribute types from cfinterface registers - are not known to mypy; suppressing is correct. -- Redundant mypy overrides (e.g., `idecomp.decomp.modelos.blocos.*` after `idecomp.decomp.modelos.*`): - harmless, not a bug. diff --git a/.gitignore b/.gitignore index b5033d4..c0e4407 100644 --- a/.gitignore +++ b/.gitignore @@ -141,4 +141,10 @@ teste.py teste.txt # UV -uv.lock \ No newline at end of file +uv.lock + +# Claude Code +.claude/ + +# Implementation plans +plans/ \ No newline at end of file diff --git a/plans/infra-docs-overhaul/.implementation-state.json b/plans/infra-docs-overhaul/.implementation-state.json deleted file mode 100644 index b99fe83..0000000 --- a/plans/infra-docs-overhaul/.implementation-state.json +++ /dev/null @@ -1,409 +0,0 @@ -{ - "plan": "infra-docs-overhaul", - "last_updated": "2026-03-09T21:43:31Z", - "progressive": true, - "tickets": { - "ticket-001-modernize-pyproject-toml.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "python-task-automation-developer" - }, - "ticket-002-restructure-ci-workflow.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "python-task-automation-developer" - }, - "ticket-003-migrate-docs-deployment.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "python-task-automation-developer" - }, - "ticket-004-create-release-workflow.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "python-task-automation-developer" - }, - "ticket-005-add-pre-commit-hooks.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "python-task-automation-developer" - }, - "ticket-006-migrate-sphinx-theme-to-furo.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 0.85, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 0.5, - "type_safety": 0.5, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-007-update-sphinx-gallery-examples.md": { - "status": "completed", - "detail_level": "detailed", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 0.94, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 0.8, - "dependency_clarity": 1.0, - "atomicity": 0.8 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-008-create-architecture-page.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-009-create-faq-page.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-010-create-performance-guide.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-011-improve-api-reference-autosummary.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-012-update-index-toctree.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-013-expand-readme.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-014-create-contributing.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-015-reformat-changelog.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - }, - "ticket-016-update-installation-docs.md": { - "status": "completed", - "detail_level": "refined", - "scores": { - "quality": 1.0, - "quality_breakdown": { - "conformance": 1.0, - "scope_adherence": 1.0, - "lint_cleanliness": 1.0, - "type_safety": 1.0, - "test_delta": 1.0 - }, - "readiness": 1.0, - "readiness_breakdown": { - "structure": 1.0, - "testability": 1.0, - "boundary": 1.0, - "dependency_clarity": 1.0, - "atomicity": 1.0 - } - }, - "workflow_step": null, - "agent": "open-source-documentation-writer" - } - }, - "epics": { - "epic-01-packaging-ci-modernization": { - "phase": "completed", - "learning_extracted": true - }, - "epic-02-sphinx-modernization": { - "phase": "completed", - "learning_extracted": true - }, - "epic-03-documentation-content": { - "phase": "completed", - "learning_extracted": true - }, - "epic-04-repository-polish": { - "phase": "completed", - "learning_extracted": true - } - } -} diff --git a/plans/infra-docs-overhaul/00-master-plan.md b/plans/infra-docs-overhaul/00-master-plan.md deleted file mode 100644 index aa85765..0000000 --- a/plans/infra-docs-overhaul/00-master-plan.md +++ /dev/null @@ -1,118 +0,0 @@ -# Master Plan: Infrastructure & Documentation Overhaul (idecomp) - -## Executive Summary - -This plan modernizes the idecomp Python package infrastructure, CI/CD pipelines, documentation theme, content, and repository polish. It replicates the successful overhaul completed for the sibling inewave package, adapted for idecomp's DECOMP-specific structure (the `idecomp/decomp/` and `idecomp/libs/` modules, DECOMP file formats, and existing `py.typed` marker). - -The plan is organized into 4 epics with 16 tickets, following the same proven structure from the inewave overhaul. - -## Goals & Non-Goals - -### Goals - -- Modernize `pyproject.toml` with proper metadata, dependency groups, and tool configuration -- Split the monolithic CI workflow into parallel, focused jobs (lint, typecheck, test matrix, docs) -- Migrate docs deployment from deprecated `peaceiris/actions-gh-pages@v3` to official GitHub Pages actions -- Create a tag-triggered release workflow with version validation (regex, not exec) -- Add pre-commit hooks for ruff and mypy -- Migrate Sphinx theme from sphinx-rtd-theme to Furo with dark mode -- Expand documentation with architecture, FAQ, and performance pages (all in Brazilian Portuguese) -- Improve API reference with autosummary -- Polish repository with expanded README, CONTRIBUTING.md, reformatted CHANGELOG - -### Non-Goals - -- Changing the idecomp public API or data models -- Adding new DECOMP file support -- Migrating away from cfinterface -- Changing the test suite logic (only CI pipeline changes) -- Translating any content to English - -## Architecture Overview - -### Current State - -- **Build**: Hatchling, version in `idecomp/__init__.py`, `py.typed` present -- **pyproject.toml**: Flat dev deps, minimal ruff config, no mypy config, sparse classifiers, description is just "idecomp" -- **CI**: Single `main.yml` job running lint/typecheck/test/docs sequentially per matrix entry; separate `docs.yml` using deprecated peaceiris action; `publish.yml` triggered on GitHub release events -- **Docs**: sphinx-rtd-theme, pt_BR, extensions include autosummary/autodoc/intersphinx/viewcode/sphinx-gallery/numpydoc; sections for apresentacao, geral (instalacao/tutorial/contribuicao), referencia (decomp + libs) -- **Repo**: Minimal README (36 lines), no CONTRIBUTING.md, no .pre-commit-config.yaml, no mypy config, CHANGELOG in ad-hoc format - -### Target State - -- **pyproject.toml**: Full Portuguese description, Python 3.10/3.11/3.12 classifiers, `Typing :: Typed`, split dep groups (test/lint/docs/dev), mypy and ruff config sections -- **CI**: 4 parallel jobs in main.yml (lint, typecheck, test matrix, docs); docs.yml using official `upload-pages-artifact@v3` + `deploy-pages@v4`; release.yml with tag trigger and version validation -- **Docs**: Furo theme with dark mode (monokai), updated sphinx-gallery examples, new pages (arquitetura, faq, desempenho), improved autosummary, updated index.rst toctree -- **Repo**: Expanded README with badges, CONTRIBUTING.md, Keep a Changelog format, updated installation docs, .pre-commit-config.yaml - -### Key Design Decisions - -1. **Regex for version extraction** in release workflow (never `exec()` with relative imports) -2. **mypy hook at `stages: [manual]`** due to cfinterface compatibility issues -3. **All documentation in Brazilian Portuguese** (pt_BR) -4. **`uv run` prefix** for all tool invocations in CI and docs -5. **Furo theme** replaces sphinx-rtd-theme (dark mode with monokai pygments) -6. **Keep a Changelog** format for CHANGELOG.md with Portuguese category names - -## Technical Approach - -### Tech Stack - -- Python 3.10+ with Hatchling build system -- GitHub Actions for CI/CD -- Sphinx with Furo theme for documentation -- ruff for linting/formatting, mypy for type checking -- pre-commit for local hooks - -### Component/Module Breakdown - -| Component | Current | Target | -| ----------------------- | ----------------------------- | --------------------------------------------- | -| pyproject.toml | Flat deps, sparse metadata | Split groups, full metadata, tool configs | -| main.yml | 1 sequential job | 4 parallel jobs | -| docs.yml | peaceiris/actions-gh-pages@v3 | upload-pages-artifact@v3 + deploy-pages@v4 | -| publish.yml | Release trigger | Tag trigger + version validation + GH release | -| .pre-commit-config.yaml | N/A | ruff + mypy hooks | -| docs/source/conf.py | sphinx-rtd-theme | Furo | -| docs content | 3 pages | 3 + 3 new guide pages | -| README.md | 36 lines | Full with badges | -| CONTRIBUTING.md | N/A | Full contributor guide | -| CHANGELOG.md | Ad-hoc format | Keep a Changelog | - -### Data Flow - -No runtime data flow changes. This plan affects only build/CI/docs/repo infrastructure. - -### Testing Strategy - -- Verify CI workflows with dry-run analysis (YAML structure validation) -- Verify Sphinx builds successfully with Furo theme -- Verify pre-commit hooks run correctly -- No changes to the existing test suite - -## Phases & Milestones - -| Epic | Name | Tickets | Estimated Duration | Milestone | -| ---- | ------------------------------- | ------- | ------------------ | ------------------------------------------------------- | -| 1 | Packaging & CI Modernization | 5 | 1-2 weeks | CI pipeline fully parallel, pre-commit hooks active | -| 2 | Sphinx Modernization | 2 | 3-5 days | Furo theme live, gallery working | -| 3 | Documentation Content Expansion | 5 | 2-3 weeks | 3 new guide pages, improved API ref, updated toctree | -| 4 | Repository Polish | 4 | 1-2 weeks | Full README, CONTRIBUTING, CHANGELOG, installation docs | - -## Risk Analysis - -| Risk | Likelihood | Impact | Mitigation | -| ----------------------------------------------- | ---------- | ------ | ------------------------------------------------------------------------------ | -| cfinterface import errors in sphinx-gallery | Medium | Medium | Known from inewave; test gallery build early in Epic 2 | -| mypy strict + cfinterface conflicts | Medium | Low | Known fix: manual stage for pre-commit hook, `warn_return_any: false` override | -| ruff auto-format changes many files | High | Low | Run ruff format once, commit separately before other changes | -| GitHub Pages action migration breaks deployment | Low | High | Test in PR before merging; keep old workflow as fallback | - -## Success Metrics - -- All 4 CI jobs pass independently on PR -- Sphinx builds with Furo theme without warnings -- Pre-commit hooks catch formatting/linting issues locally -- Documentation site has architecture, FAQ, and performance pages -- README displays all badges correctly -- Release workflow creates GitHub release and publishes to PyPI on tag push diff --git a/plans/infra-docs-overhaul/README.md b/plans/infra-docs-overhaul/README.md deleted file mode 100644 index e9bec3c..0000000 --- a/plans/infra-docs-overhaul/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Infrastructure & Documentation Overhaul (idecomp) - -Modernize the idecomp Python package infrastructure, CI/CD pipelines, documentation theme/content, and repository polish. Replicates the successful overhaul from the inewave sibling project, adapted for idecomp's DECOMP-specific structure. - -## Tech Stack - -- Python 3.10+ / Hatchling build system -- GitHub Actions CI/CD -- Sphinx + Furo documentation theme -- ruff (linting/formatting) + mypy (type checking) -- pre-commit hooks - -## Epics - -| Epic | Name | Tickets | Detail Level | Phase | -| ---- | ------------------------------- | ------- | ------------ | --------- | -| 1 | Packaging & CI Modernization | 5 | Detailed | completed | -| 2 | Sphinx Modernization | 2 | Detailed | completed | -| 3 | Documentation Content Expansion | 5 | Refined | completed | -| 4 | Repository Polish | 4 | Refined | completed | - -## Progress - -| Ticket | Title | Epic | Status | Detail Level | Readiness | Quality | Badge | -| ---------- | -------------------------------------------------------- | ------- | --------- | ------------ | --------- | ------- | ---------- | -| ticket-001 | Modernize pyproject.toml Metadata and Dependency Groups | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-002 | Restructure CI Workflow into Parallel Jobs | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-003 | Migrate Docs Deployment to Official GitHub Pages Actions | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-004 | Create Tag-Triggered Release Workflow | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-005 | Add Pre-commit Hooks Configuration | epic-01 | completed | Detailed | 1.00 | 1.00 | EXCELLENT | -| ticket-006 | Migrate Sphinx Theme to Furo | epic-02 | completed | Detailed | 1.00 | 0.85 | ACCEPTABLE | -| ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | epic-02 | completed | Detailed | 0.94 | 1.00 | EXCELLENT | -| ticket-008 | Create Architecture Documentation Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-009 | Create FAQ Documentation Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-010 | Create Performance Guide Page | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-011 | Improve API Reference with Autosummary Blocks | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-012 | Update index.rst Toctree with Guias Section | epic-03 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-013 | Expand README with Badges and Sections | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-014 | Create CONTRIBUTING.md | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | -| ticket-016 | Update Installation Documentation | epic-04 | completed | Refined | 1.00 | 1.00 | EXCELLENT | - -## Dependency Graph - -``` -ticket-001 (pyproject.toml) - |---> ticket-002 (CI restructure) ---> ticket-013 (README badges) - |---> ticket-004 (release workflow) - |---> ticket-005 (pre-commit) ---> ticket-014 (CONTRIBUTING) - |---> ticket-006 (Furo theme) - |---> ticket-007 (gallery examples) - |---> ticket-008 (architecture page) --+ - |---> ticket-009 (FAQ page) -----------+--> ticket-012 (index toctree) - |---> ticket-010 (performance page) ---+ - |---> ticket-011 (API autosummary) - -ticket-003 (docs deployment) [independent] -ticket-015 (changelog) [independent] -ticket-016 (installation docs) [independent] -``` - -## Notes - -- This is a **progressive plan**: Epics 1-2 have fully detailed tickets; Epics 3-4 have outline tickets that were refined with learnings from earlier epics -- All 4 epics have been **refined** and are ready for implementation -- All documentation content must be written in **Brazilian Portuguese** (pt_BR) -- Key learnings from the inewave overhaul are embedded in the detailed tickets (regex version extraction, mypy manual stage, cfinterface workarounds) diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md deleted file mode 100644 index 3e74385..0000000 --- a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/00-epic-overview.md +++ /dev/null @@ -1,38 +0,0 @@ -# Epic 1: Packaging & CI Modernization - -## Goal - -Modernize the idecomp build metadata, CI/CD pipelines, and developer tooling to match the standards established in the inewave sibling project. After this epic, the CI will run 4 parallel jobs (lint, typecheck, test matrix, docs), the release workflow will be tag-triggered with version validation, and pre-commit hooks will enforce code quality locally. - -## Scope - -- Modernize `pyproject.toml` metadata, dependency groups, and tool configuration -- Restructure `main.yml` into 4 parallel CI jobs -- Migrate `docs.yml` to official GitHub Pages deployment actions -- Create tag-triggered `release.yml` with version validation -- Add `.pre-commit-config.yaml` with ruff and mypy hooks - -## Tickets - -| Order | Ticket | Title | Points | -| ----- | ---------- | -------------------------------------------------------- | ------ | -| 1 | ticket-001 | Modernize pyproject.toml Metadata and Dependency Groups | 3 | -| 2 | ticket-002 | Restructure CI Workflow into Parallel Jobs | 3 | -| 3 | ticket-003 | Migrate Docs Deployment to Official GitHub Pages Actions | 2 | -| 4 | ticket-004 | Create Tag-Triggered Release Workflow | 3 | -| 5 | ticket-005 | Add Pre-commit Hooks Configuration | 2 | - -## Dependencies - -- ticket-002 depends on ticket-001 (needs dependency groups defined) -- ticket-003 is independent -- ticket-004 depends on ticket-001 (needs dependency groups) -- ticket-005 depends on ticket-001 (needs ruff/mypy config in pyproject.toml) - -## Completion Criteria - -- `uv sync --extra test` installs only test deps; `uv sync --extra lint` installs only lint deps -- `main.yml` runs 4 independent jobs that pass on PR -- `docs.yml` deploys using `actions/upload-pages-artifact@v3` and `actions/deploy-pages@v4` -- `release.yml` triggers on `v*` tags, validates version with regex, creates GitHub release -- `pre-commit run --all-files` executes ruff check + ruff format; `pre-commit run --hook-stage manual --all-files` runs mypy diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md deleted file mode 100644 index 9d42ef7..0000000 --- a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-001-modernize-pyproject-toml.md +++ /dev/null @@ -1,144 +0,0 @@ -# ticket-001 Modernize pyproject.toml Metadata and Dependency Groups - -## Context - -### Background - -The idecomp `pyproject.toml` has sparse metadata (description is just "idecomp", only Python 3.10 classifier), flat dev dependencies in a single group, and no tool configuration for mypy. This ticket brings it to parity with the inewave sibling project's modernized `pyproject.toml`. - -### Relation to Epic - -This is the foundational ticket of Epic 1. All other CI and tooling tickets depend on the dependency groups and tool configuration defined here. - -### Current State - -File: `/home/rogerio/git/idecomp/pyproject.toml` - -- `description = "idecomp"` (should be descriptive, in Portuguese) -- Classifiers: only `Python :: 3.10`, missing 3.11/3.12 and `Typing :: Typed` -- Single `[project.optional-dependencies] dev` group with all deps mixed together: pytest, pytest-cov, ruff, mypy, sphinx-rtd-theme, sphinx-gallery, sphinx, numpydoc, plotly, matplotlib -- `[tool.ruff]` has only `line-length = 80` -- No `[tool.mypy]` section -- No `[tool.ruff.lint]` section -- `py.typed` marker already exists at `/home/rogerio/git/idecomp/idecomp/py.typed` -- Wheel include already covers `idecomp/` (which includes `py.typed`) - -## Specification - -### Requirements - -1. Update `description` to `"Pacote para manipulacao dos arquivos do DECOMP"` (matching the README's Portuguese description) -2. Add classifiers: `Programming Language :: Python :: 3.11`, `Programming Language :: Python :: 3.12`, `Typing :: Typed` -3. Split `[project.optional-dependencies]` into 4 groups: - - `test = ["pytest", "pytest-cov"]` - - `lint = ["ruff", "mypy"]` - - `docs = ["sphinx", "furo", "sphinx-gallery", "numpydoc", "plotly", "matplotlib"]` - - `dev = ["idecomp[test,lint,docs]"]` (self-referencing alias) -4. Note: `furo` replaces `sphinx-rtd-theme` in the docs group (theme migration happens in ticket-006, but the dependency is set here) -5. Add `[tool.mypy]` section with strict settings and cfinterface override: - - ```toml - [tool.mypy] - python_version = "3.10" - warn_return_any = false - warn_unused_configs = true - disallow_untyped_defs = true - disallow_incomplete_defs = true - check_untyped_defs = true - no_implicit_optional = true - warn_redundant_casts = true - warn_unused_ignores = true - strict = true - - [[tool.mypy.overrides]] - module = "cfinterface.*" - ignore_missing_imports = true - warn_return_any = false - ``` - -6. Expand `[tool.ruff.lint]` with select rules: - ```toml - [tool.ruff.lint] - select = ["E", "F", "I", "UP"] - ``` -7. Verify `py.typed` is present and included in wheel build (no changes needed, just verify) - -### Inputs/Props - -- Current file: `/home/rogerio/git/idecomp/pyproject.toml` (57 lines) - -### Outputs/Behavior - -- Updated `pyproject.toml` with all changes above -- `uv sync --extra test` installs only pytest + pytest-cov -- `uv sync --extra lint` installs only ruff + mypy -- `uv sync --extra docs` installs only sphinx + furo + sphinx-gallery + numpydoc + plotly + matplotlib -- `uv sync --extra dev` installs all of the above - -### Error Handling - -- If the self-referencing `dev` extra causes resolution issues, fall back to listing all deps explicitly in the dev group -- The cfinterface pin `>=1.8,<=1.8.3` must remain unchanged - -## Acceptance Criteria - -- [ ] Given the updated `pyproject.toml`, when running `uv sync --extra test`, then only `pytest` and `pytest-cov` are installed as extras (verify with `uv pip list | grep -i pytest`) -- [ ] Given the updated `pyproject.toml`, when inspecting the `[project]` section, then `description` equals `"Pacote para manipulacao dos arquivos do DECOMP"` and classifiers include `Programming Language :: Python :: 3.11`, `Programming Language :: Python :: 3.12`, and `Typing :: Typed` -- [ ] Given the updated `pyproject.toml`, when inspecting `[tool.mypy]`, then `strict = true` is set and `[[tool.mypy.overrides]]` for `cfinterface.*` has `ignore_missing_imports = true` and `warn_return_any = false` -- [ ] Given the updated `pyproject.toml`, when inspecting `[tool.ruff.lint]`, then `select = ["E", "F", "I", "UP"]` is present -- [ ] Given the file `/home/rogerio/git/idecomp/idecomp/py.typed`, when inspecting the wheel build config `[tool.hatch.build.targets.wheel]`, then the include pattern `"idecomp/"` covers the `py.typed` marker file - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/pyproject.toml` -2. Update the `description` field in `[project]` -3. Add missing classifiers to the `classifiers` list -4. Replace the single `dev` group in `[project.optional-dependencies]` with 4 groups: `test`, `lint`, `docs`, `dev` -5. Add `[tool.mypy]` section after the existing `[tool.ruff]` section -6. Add `[tool.ruff.lint]` section after `[tool.ruff]` -7. Verify `idecomp/py.typed` exists and the wheel include covers it (read-only check) - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/pyproject.toml` (the only file modified) - -### Patterns to Follow - -- Match the inewave pyproject.toml structure for consistency across sibling projects -- Use self-referencing extras syntax: `"idecomp[test,lint,docs]"` - -### Pitfalls to Avoid - -- Do NOT remove the `cfinterface>=1.8,<=1.8.3` pin from `[project] dependencies` -- Do NOT add `sphinx-rtd-theme` to the docs group (it is being replaced by `furo`) -- Do NOT use `exec()` anywhere for version extraction (the Hatchling `path` config handles this) -- The `warn_return_any = false` must appear both at the top-level `[tool.mypy]` AND in the `[[tool.mypy.overrides]]` for cfinterface because `strict = true` re-enables it - -## Testing Requirements - -### Unit Tests - -- No code changes; no unit tests needed - -### Integration Tests - -- Run `uv sync --extra test` and verify pytest is available -- Run `uv sync --extra lint` and verify ruff and mypy are available -- Run `uv sync --extra docs` and verify sphinx and furo are available -- Run `uv sync --extra dev` and verify all tools are available - -### E2E Tests - -- Not applicable - -## Dependencies - -- **Blocked By**: None (first ticket in the plan) -- **Blocks**: ticket-002-restructure-ci-workflow.md, ticket-004-create-release-workflow.md, ticket-005-add-pre-commit-hooks.md, ticket-006-migrate-sphinx-theme-to-furo.md - -## Effort Estimate - -**Points**: 3 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md deleted file mode 100644 index e44fc61..0000000 --- a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-002-restructure-ci-workflow.md +++ /dev/null @@ -1,111 +0,0 @@ -# ticket-002 Restructure CI Workflow into Parallel Jobs - -## Context - -### Background - -The current `main.yml` workflow runs a single job that sequentially executes pytest, mypy, ruff, and sphinx-build for each Python version in the matrix. This wastes CI time because a linting failure only surfaces after tests complete. The inewave overhaul split this into 4 parallel jobs, and idecomp should follow the same pattern. - -### Relation to Epic - -Second ticket in Epic 1. Depends on ticket-001 for the dependency group split (each job installs only its required extras). - -### Current State - -File: `/home/rogerio/git/idecomp/.github/workflows/main.yml` - -- Workflow name: `tests` -- Triggers: push/PR to `main` -- Single `test` job with matrix `python-version: ["3.10", "3.11", "3.12"]` -- Steps: checkout, install uv, install python, `uv sync --all-extras --dev`, pytest with coverage, codecov upload, mypy, ruff check, sphinx-build -- All 7 steps run sequentially in each matrix entry - -## Specification - -### Requirements - -1. Replace the single `test` job with 4 independent jobs: - - **lint**: Runs `ruff check ./idecomp` and `ruff format --check ./idecomp`. Single Python version (3.12). Installs `--extra lint`. - - **typecheck**: Runs `uv run mypy ./idecomp`. Single Python version (3.12). Installs `--extra lint`. - - **test**: Runs `uv run pytest --cov-report=xml --cov=idecomp ./tests` with codecov upload. Matrix: 3.10, 3.11, 3.12. Installs `--extra test`. - - **docs**: Runs `uv run sphinx-build -M html docs/source docs/build`. Single Python version (3.12). Installs `--extra docs`. -2. Keep the workflow name as `tests` (badge URLs reference this name) -3. Keep triggers as push/PR to `main` -4. Each job: checkout, install uv, install python, `uv sync --extra `, run tool -5. The `test` job keeps the codecov upload step with the existing token reference - -### Inputs/Props - -- Current file: `/home/rogerio/git/idecomp/.github/workflows/main.yml` (46 lines) - -### Outputs/Behavior - -- 4 jobs appear in the GitHub Actions UI, running in parallel -- Each job fails independently without blocking others -- Total CI wall-clock time is reduced to the longest single job - -### Error Handling - -- Each job fails independently; a lint failure does not prevent test results from appearing -- Codecov token is only referenced in the test job - -## Acceptance Criteria - -- [ ] Given the updated `main.yml`, when parsing the YAML, then exactly 4 top-level keys exist under `jobs`: `lint`, `typecheck`, `test`, `docs` -- [ ] Given the `lint` job definition, when inspecting its steps, then it runs `uv sync --extra lint` (not `--all-extras`) and executes both `uv run ruff check ./idecomp` and `uv run ruff format --check ./idecomp` -- [ ] Given the `typecheck` job definition, when inspecting its steps, then it runs `uv sync --extra lint` and executes `uv run mypy ./idecomp` -- [ ] Given the `test` job definition, when inspecting its matrix, then `python-version` includes `["3.10", "3.11", "3.12"]` and the job runs `uv sync --extra test` and includes the codecov upload step -- [ ] Given the `docs` job definition, when inspecting its steps, then it runs `uv sync --extra docs` and executes `uv run sphinx-build -M html docs/source docs/build` - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/.github/workflows/main.yml` -2. Remove the existing `test` job entirely -3. Add 4 new jobs: `lint`, `typecheck`, `test`, `docs` -4. For each job, use the common preamble: `actions/checkout@v4`, `astral-sh/setup-uv@v3`, `uv python install `, `uv sync --extra ` -5. The `test` job gets the matrix strategy and codecov step from the old job -6. The `lint` and `typecheck` jobs use Python 3.12 (no matrix) -7. The `docs` job uses Python 3.12 (no matrix) - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/.github/workflows/main.yml` - -### Patterns to Follow - -- Match the inewave `main.yml` 4-job structure -- Use `uv sync --extra ` instead of `--all-extras --dev` for each job - -### Pitfalls to Avoid - -- Do NOT rename the workflow from `tests` (the README badge URL references `actions/workflows/main.yml/badge.svg`) -- Do NOT remove the codecov token secret reference; it must stay in the `test` job -- Do NOT add job dependencies between the 4 jobs; they must run in parallel -- The docs job needs `--extra docs` which pulls in sphinx, furo, sphinx-gallery, etc. -- it does NOT need test or lint deps - -## Testing Requirements - -### Unit Tests - -- Not applicable (YAML configuration) - -### Integration Tests - -- Validate the YAML structure is valid (no syntax errors) -- Verify each job references the correct extras group - -### E2E Tests - -- Push a PR and verify 4 separate jobs appear in the GitHub Actions UI - -## Dependencies - -- **Blocked By**: ticket-001-modernize-pyproject-toml.md -- **Blocks**: ticket-013-expand-readme.md (needs final workflow name for badge URL) - -## Effort Estimate - -**Points**: 3 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md deleted file mode 100644 index 3fa978c..0000000 --- a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-003-migrate-docs-deployment.md +++ /dev/null @@ -1,124 +0,0 @@ -# ticket-003 Migrate Docs Deployment to Official GitHub Pages Actions - -## Context - -### Background - -The current `docs.yml` workflow uses `peaceiris/actions-gh-pages@v3`, which is deprecated and no longer maintained. GitHub now provides official actions for Pages deployment: `actions/upload-pages-artifact@v3` and `actions/deploy-pages@v4`. The inewave overhaul successfully migrated to these official actions. - -### Relation to Epic - -Independent ticket within Epic 1. Does not depend on the pyproject.toml changes (it only changes the deployment mechanism, not what gets installed). - -### Current State - -File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml` - -- Triggers: push to `main`, workflow_dispatch -- Single `docs` job: checkout, install uv, install python, `uv sync --all-extras --dev`, pytest, sphinx-build, deploy with `peaceiris/actions-gh-pages@v3` -- Deploys to `gh-pages` branch with `force_orphan: true` -- Uses `${{ secrets.GITHUB_TOKEN }}` - -## Specification - -### Requirements - -1. Replace `peaceiris/actions-gh-pages@v3` deployment with official GitHub Pages actions -2. Add top-level `permissions` block: `pages: write`, `id-token: write` -3. Add `concurrency` group to prevent concurrent deployments: `group: "pages"`, `cancel-in-progress: false` -4. Split into 2 jobs: - - **build**: checkout, install uv, install python, `uv sync --extra docs`, sphinx-build, `actions/upload-pages-artifact@v3` pointing to `docs/build/html` - - **deploy**: depends on build, uses `actions/deploy-pages@v4`, environment `github-pages` -5. Remove the pytest step from the docs workflow (tests run in `main.yml`) -6. Remove the `--all-extras --dev` install; use `--extra docs` instead -7. Keep triggers: push to `main`, workflow_dispatch - -### Inputs/Props - -- Current file: `/home/rogerio/git/idecomp/.github/workflows/docs.yml` (33 lines) - -### Outputs/Behavior - -- Documentation deploys to GitHub Pages using the official deployment mechanism -- Deployment uses OIDC token (id-token: write) instead of GITHUB_TOKEN for Pages -- Concurrent pushes to main do not cause race conditions in deployment - -### Error Handling - -- If sphinx-build fails, the upload-pages-artifact step is skipped and deploy job is skipped -- The concurrency group ensures only one deployment runs at a time - -## Acceptance Criteria - -- [ ] Given the updated `docs.yml`, when parsing the YAML, then `peaceiris/actions-gh-pages` does not appear anywhere in the file -- [ ] Given the updated `docs.yml`, when inspecting the top-level keys, then `permissions` contains `pages: write` and `id-token: write`, and `concurrency` has `group: "pages"` with `cancel-in-progress: false` -- [ ] Given the `build` job, when inspecting its steps, then it runs `uv sync --extra docs` (not `--all-extras`) and uses `actions/upload-pages-artifact@v3` with `path: docs/build/html` -- [ ] Given the `deploy` job, when inspecting its configuration, then it has `needs: build`, uses `actions/deploy-pages@v4`, and sets `environment: name: github-pages` -- [ ] Given the updated `docs.yml`, when searching for `pytest`, then no pytest step exists in any job - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/.github/workflows/docs.yml` -2. Add top-level `permissions` and `concurrency` blocks after the `on` trigger -3. Rename the existing `docs` job to `build` and modify its steps: - - Keep: checkout, install uv, install python - - Change: `uv sync --all-extras --dev` to `uv sync --extra docs` - - Remove: pytest step - - Keep: sphinx-build step - - Replace: peaceiris action with `actions/upload-pages-artifact@v3` with `path: docs/build/html` -4. Add a new `deploy` job: - ```yaml - deploy: - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - ``` - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/.github/workflows/docs.yml` - -### Patterns to Follow - -- Match the inewave `docs.yml` structure with build + deploy jobs -- Use the official GitHub Pages actions pattern from GitHub's documentation - -### Pitfalls to Avoid - -- Do NOT keep the pytest step; tests belong in `main.yml` only -- Do NOT forget the `id-token: write` permission; it is required for the official Pages deployment -- Do NOT set `cancel-in-progress: true`; this could cancel an active deployment mid-way -- The `environment: name: github-pages` must match the GitHub Pages environment configured in the repo settings - -## Testing Requirements - -### Unit Tests - -- Not applicable (YAML configuration) - -### Integration Tests - -- Validate YAML syntax -- Verify no references to `peaceiris` remain - -### E2E Tests - -- After merging to main, verify docs deploy successfully to the GitHub Pages URL - -## Dependencies - -- **Blocked By**: None (independent within Epic 1) -- **Blocks**: None - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md deleted file mode 100644 index 474a1c0..0000000 --- a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-004-create-release-workflow.md +++ /dev/null @@ -1,139 +0,0 @@ -# ticket-004 Create Tag-Triggered Release Workflow - -## Context - -### Background - -The current `publish.yml` triggers on GitHub release creation events. The inewave overhaul moved to a tag-triggered workflow that validates the tag version against `__version__`, creates a GitHub release automatically, and publishes to PyPI. idecomp should adopt the same pattern. - -### Relation to Epic - -Fourth ticket in Epic 1. Depends on ticket-001 for dependency groups (the release workflow installs `--extra test` and `--extra lint` for validation steps). - -### Current State - -File: `/home/rogerio/git/idecomp/.github/workflows/publish.yml` - -- Name: `deploy` -- Trigger: `release: types: [created]` -- Single job `build-and-publish`: checkout, install uv, install python 3.11, `uv sync --all-extras --dev`, pytest, mypy, ruff check, uv build, pypa/gh-action-pypi-publish -- Uses trusted publishing (`id-token: write`) -- Environment: `pypi` with URL `https://pypi.org/p/idecomp` - -## Specification - -### Requirements - -1. Rename the file from `publish.yml` to `release.yml` -2. Rename the workflow from `deploy` to `release` -3. Change trigger from `release: types: [created]` to `push: tags: ["v*"]` -4. Add a version validation step that: - - Extracts version from `idecomp/__init__.py` using `grep` + regex (NOT `exec()` or `python -c "import idecomp"`) - - Extracts tag version from `GITHUB_REF` (strips `refs/tags/v` prefix) - - Compares them and fails if they do not match -5. Keep the test, mypy, and ruff validation steps but use `--extra test` and `--extra lint` instead of `--all-extras` -6. After successful build and publish, create a GitHub release using `gh release create $TAG --generate-notes` -7. Keep the trusted publishing setup (id-token: write, pypi environment) -8. Update Python version to 3.12 - -### Inputs/Props - -- Current file: `/home/rogerio/git/idecomp/.github/workflows/publish.yml` (41 lines) -- Version source: `/home/rogerio/git/idecomp/idecomp/__init__.py` line `__version__ = "1.8.2"` - -### Outputs/Behavior - -- Pushing a tag `v1.8.3` triggers the workflow -- Version validation fails if tag `v1.8.3` does not match `__version__ = "1.8.3"` in `__init__.py` -- On success: tests pass, package is built, published to PyPI, and a GitHub release is created - -### Error Handling - -- Version mismatch between tag and `__init__.py` fails the workflow early (before build/publish) -- Test/lint failures prevent package publication - -## Acceptance Criteria - -- [ ] Given the repository, when listing files in `.github/workflows/`, then `release.yml` exists and `publish.yml` does not exist -- [ ] Given the `release.yml`, when inspecting the `on` trigger, then it is `push: tags: ["v*"]` and no `release` trigger exists -- [ ] Given the version validation step, when inspecting the shell commands, then it uses `grep -oP '__version__\s*=\s*"\K[^"]+'` (or equivalent regex) on `idecomp/__init__.py` and does NOT use `exec()`, `eval()`, or `python -c "import idecomp"` -- [ ] Given the `release.yml`, when inspecting install steps, then it uses `uv sync --extra test --extra lint` (not `--all-extras --dev`) -- [ ] Given the workflow, when inspecting the final steps, then after `pypa/gh-action-pypi-publish`, there is a step running `gh release create` with the tag and `--generate-notes` - -## Implementation Guide - -### Suggested Approach - -1. Rename `/home/rogerio/git/idecomp/.github/workflows/publish.yml` to `release.yml` (use `git mv`) -2. Update the workflow `name` to `release` -3. Replace the `on` trigger: - ```yaml - on: - push: - tags: - - "v*" - ``` -4. Add a version validation step early in the job: - ```yaml - - name: Validate version - run: | - PKG_VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' idecomp/__init__.py) - TAG_VERSION=${GITHUB_REF#refs/tags/v} - if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then - echo "Version mismatch: __init__.py=$PKG_VERSION tag=$TAG_VERSION" - exit 1 - fi - ``` -5. Change install step to `uv sync --extra test --extra lint` -6. Update Python to 3.12: `uv python install 3.12` -7. Add GitHub release creation step after PyPI publish: - ```yaml - - name: Create GitHub Release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release create ${{ github.ref_name }} --generate-notes - ``` -8. Add `contents: write` to permissions (needed for `gh release create`) - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/.github/workflows/publish.yml` (rename to `release.yml` and rewrite) - -### Patterns to Follow - -- Match the inewave `release.yml` structure -- Use grep + regex for version extraction (never exec/eval) -- Trusted publishing with `pypa/gh-action-pypi-publish@release/v1` - -### Pitfalls to Avoid - -- Do NOT use `exec(open('__init__.py').read())` -- it fails because `__init__.py` has `from . import decomp` (relative import) -- Do NOT use `python -c "from idecomp import __version__"` -- it requires the package to be importable with all deps -- Do NOT forget `contents: write` permission for `gh release create` -- The `startsWith(github.ref, 'refs/tags')` guards on build/publish steps can be removed since the trigger already ensures this - -## Testing Requirements - -### Unit Tests - -- Not applicable (YAML configuration) - -### Integration Tests - -- Validate YAML syntax -- Verify the grep regex correctly extracts version from `idecomp/__init__.py` - -### E2E Tests - -- Create a test tag on a branch to verify the workflow triggers (can be deleted after) - -## Dependencies - -- **Blocked By**: ticket-001-modernize-pyproject-toml.md -- **Blocks**: None - -## Effort Estimate - -**Points**: 3 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md b/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md deleted file mode 100644 index be573fc..0000000 --- a/plans/infra-docs-overhaul/epic-01-packaging-ci-modernization/ticket-005-add-pre-commit-hooks.md +++ /dev/null @@ -1,125 +0,0 @@ -# ticket-005 Add Pre-commit Hooks Configuration - -## Context - -### Background - -idecomp has no `.pre-commit-config.yaml`. Developers must manually run ruff and mypy before committing. The inewave overhaul added pre-commit hooks for ruff (check + format) and mypy (manual stage due to cfinterface issues), and idecomp should adopt the same configuration. - -### Relation to Epic - -Fifth and final ticket of Epic 1. Depends on ticket-001 for ruff and mypy configuration in `pyproject.toml`. - -### Current State - -- No `.pre-commit-config.yaml` exists in the repository -- ruff and mypy are listed as dev dependencies (will be in `lint` group after ticket-001) -- mypy has known issues with cfinterface (missing type stubs, `warn_return_any` conflicts) - -## Specification - -### Requirements - -1. Create `.pre-commit-config.yaml` at repository root with: - - **ruff-pre-commit** hooks (from `https://github.com/astral-sh/ruff-pre-commit`): - - `ruff` hook (linting/check) - - `ruff-format` hook (formatting) - - **mypy** hook as a local hook: - - `language: system` (uses the project's installed mypy, not a separate venv) - - `entry: uv run mypy ./idecomp` - - `pass_filenames: false` (runs on entire package) - - `stages: [manual]` (not run on every commit due to cfinterface import issues; invoked explicitly with `pre-commit run --hook-stage manual`) -2. Use a recent ruff-pre-commit rev (e.g., `v0.8.6` or latest stable) - -### Inputs/Props - -- No existing file to modify; creating new file at `/home/rogerio/git/idecomp/.pre-commit-config.yaml` - -### Outputs/Behavior - -- `pre-commit run --all-files` executes ruff check and ruff format on all files -- `pre-commit run --hook-stage manual --all-files` additionally runs mypy -- ruff hooks use the pre-commit framework's own ruff installation (fast, isolated) -- mypy hook uses the system/project-installed mypy via `uv run` - -### Error Handling - -- If ruff finds formatting issues, the hook auto-fixes them and fails the commit (user re-stages and re-commits) -- If mypy fails due to cfinterface issues, it only affects manual runs, not regular commits - -## Acceptance Criteria - -- [ ] Given the repository root, when listing files, then `.pre-commit-config.yaml` exists at `/home/rogerio/git/idecomp/.pre-commit-config.yaml` -- [ ] Given the `.pre-commit-config.yaml`, when inspecting the ruff-pre-commit repo entry, then it contains hooks with ids `ruff` and `ruff-format` -- [ ] Given the `.pre-commit-config.yaml`, when inspecting the local hooks, then there is a hook with `id: mypy`, `language: system`, `entry: uv run mypy ./idecomp`, `pass_filenames: false`, and `stages: [manual]` -- [ ] Given a clean checkout with pre-commit installed, when running `pre-commit run --all-files`, then the ruff and ruff-format hooks execute (mypy does not execute because it is manual stage) - -## Implementation Guide - -### Suggested Approach - -1. Create `/home/rogerio/git/idecomp/.pre-commit-config.yaml` with the following structure: - - ```yaml - repos: - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 - hooks: - - id: ruff - args: [--fix] - - id: ruff-format - - - repo: local - hooks: - - id: mypy - name: mypy - entry: uv run mypy ./idecomp - language: system - types: [python] - pass_filenames: false - stages: [manual] - ``` - -2. Verify the file is valid YAML - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/.pre-commit-config.yaml` (new file) - -### Patterns to Follow - -- Match the inewave `.pre-commit-config.yaml` structure exactly -- Use `language: system` for mypy to avoid installing it in an isolated venv (it needs access to project deps) -- Use `stages: [manual]` to prevent mypy from running on every commit - -### Pitfalls to Avoid - -- Do NOT set mypy stages to `[pre-commit]` or omit stages -- cfinterface issues will cause every commit to fail -- Do NOT use `language: python` for mypy -- it installs mypy in an isolated venv without project deps -- Do NOT forget `pass_filenames: false` for mypy -- it should type-check the whole package, not individual files -- Do NOT forget `args: [--fix]` on the ruff hook -- without it, ruff only reports but does not fix issues - -## Testing Requirements - -### Unit Tests - -- Not applicable (configuration file) - -### Integration Tests - -- Validate YAML syntax of the new file -- Run `pre-commit run --all-files` and verify ruff hooks execute - -### E2E Tests - -- Not applicable - -## Dependencies - -- **Blocked By**: ticket-001-modernize-pyproject-toml.md -- **Blocks**: ticket-014-create-contributing.md (CONTRIBUTING references pre-commit setup) - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md deleted file mode 100644 index 1c73c4c..0000000 --- a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md +++ /dev/null @@ -1,31 +0,0 @@ -# Epic 2: Sphinx Modernization - -## Goal - -Replace the sphinx-rtd-theme with Furo for a modern documentation appearance with dark mode support, and ensure all sphinx-gallery examples build correctly with the new theme. - -## Scope - -- Migrate Sphinx theme from sphinx-rtd-theme to Furo -- Configure dark mode with monokai pygments style -- Update conf.py extensions and theme options -- Review and fix sphinx-gallery example scripts for compatibility - -## Tickets - -| Order | Ticket | Title | Points | -| ----- | ---------- | ----------------------------------------------------- | ------ | -| 1 | ticket-006 | Migrate Sphinx Theme to Furo | 3 | -| 2 | ticket-007 | Update Sphinx-Gallery Examples for Furo Compatibility | 2 | - -## Dependencies - -- ticket-006 depends on ticket-001 (needs docs dependency group with furo instead of sphinx-rtd-theme) -- ticket-007 depends on ticket-006 (needs Furo theme configured) - -## Completion Criteria - -- `uv run sphinx-build -M html docs/source docs/build` succeeds with Furo theme -- Documentation site renders with dark mode toggle -- All 4 example scripts in `/examples/` build successfully in the gallery -- No sphinx-rtd-theme references remain in the codebase diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md deleted file mode 100644 index 836f2b1..0000000 --- a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/learnings.md +++ /dev/null @@ -1,80 +0,0 @@ -# Epic 02 Learnings: Sphinx Modernization - -**Epic**: epic-02-sphinx-modernization -**Date**: 2026-03-08 -**Tickets**: ticket-006, ticket-007 -**Files changed**: `docs/source/conf.py` only (ticket-006); no files changed (ticket-007) - ---- - -## Patterns Established - -- **Furo theme is configured exclusively via `html_theme`, not `extensions`**: Unlike `sphinx-rtd-theme`, Furo must NOT appear in the `extensions` list. Adding it there causes a warning. The only required change is `html_theme = "furo"` plus the `html_theme_options` dict. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 43-54. - -- **Dual pygments style pattern for dark/light mode**: Furo requires two separate pygments settings: `pygments_style = "friendly"` for light mode and `pygments_dark_style = "monokai"` for dark mode. A single `pygments_style` setting (as with RTD) leaves dark mode code blocks unstyled. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 39-40. - -- **`sidebar_hide_name` with SVG logo**: When an `html_logo` is set, `"sidebar_hide_name": True` in `html_theme_options` prevents the project name from appearing redundantly next to the logo in the sidebar. Without it, both the logo and the text name display together. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 53. - -- **Brand color split into `light_css_variables` and `dark_css_variables`**: Furo uses CSS variable overrides to apply brand colors per mode. The pattern uses `"color-brand-primary"` and `"color-brand-content"` keys — primary for interactive elements, content for text/links. The light and dark values use different blue shades (`#2962ff` vs `#5c8aff`) to maintain contrast ratios in each mode. See `/home/rogerio/git/idecomp/docs/source/conf.py` lines 44-53. - -- **`from typing import List` replaced by built-in `list[str]`**: The original `conf.py` used `from typing import List` for the `exclude_patterns` type annotation. Since idecomp targets Python >= 3.10, this was updated to the built-in `list[str]` syntax and the import removed. See `/home/rogerio/git/idecomp/docs/source/conf.py` line 36. - -- **Example scripts use current class-based API (`ClassName.read()`) with no deprecated patterns**: All 4 example scripts in `/home/rogerio/git/idecomp/examples/` (`plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`) already use the modern API. No `le_arquivo` or `escreve_arquivo` calls were found. The gallery was already clean before ticket-007 ran. - -- **`pio.renderers.default = "sphinx_gallery"` is pre-existing and theme-agnostic**: The plotly renderer configuration at the top of `conf.py` (lines 4-6) predates the Furo migration and requires no changes when switching themes. It is a sphinx-gallery-specific setting, not an RTD-specific one. - ---- - -## Architectural Decisions - -- **conf.py is the single point of control for theme, pygments, and gallery**: The entire epic's scope was one file. No other files (pyproject.toml, CI workflows, example scripts) required modification. This confirms the epic-01 assessment that `furo` was already in the `docs` dependency group, and the theme switch is purely a `conf.py` concern. - -- **`abort_on_example_error = False` was NOT needed**: The ticket spec anticipated potential cfinterface import errors during gallery execution and listed this as a fallback. In practice, all 4 examples executed without error — the examples use idecomp's own API (not cfinterface directly), and the mock data files in `/home/rogerio/git/idecomp/examples/decomp/` are sufficient for clean execution. The fallback option was researched but not applied. - -- **RTD `html_theme_options` keys are wholly incompatible with Furo**: The original conf.py had RTD-specific keys (`logo_only`, `collapse_navigation`, `sticky_navigation`, `navigation_depth`, `includehidden`, `titles_only`). These were completely replaced, not adapted. The two themes have no overlapping option keys. Any future theme migration must treat `html_theme_options` as a clean replacement, not a patch. - -- **`sphinx.ext.githubpages` retained**: This extension creates the `.nojekyll` file required for GitHub Pages to serve the docs correctly. It is not RTD-specific. Removing it when switching themes would silently break the Pages deployment. It stays in `extensions` regardless of theme. - ---- - -## Files & Structures Created - -- `/home/rogerio/git/idecomp/docs/source/conf.py` — Updated in place: Furo theme, dual pygments styles, Furo `html_theme_options`, `sphinx_rtd_theme` removed from extensions, `from typing import List` replaced with built-in `list[str]`. No other files were modified in this epic. - ---- - -## Conventions Adopted - -- **Theme options must be a full replacement, never a patch**: When switching Sphinx themes, the `html_theme_options` dict must be completely rewritten for the new theme. Keeping any keys from the previous theme's dict causes Sphinx to emit warnings about unknown options (RTD keys are silently ignored by Furo, but this creates noise in build logs). - -- **Example mock data lives in `examples/decomp/`**: The 3 mock data files (`dadger.rv0`, `dec_oper_sist.csv`, `relato.rv0`) live at `/home/rogerio/git/idecomp/examples/decomp/`. Example scripts reference them via relative path `"./decomp/"`. This directory must be preserved and the files kept in sync with the format expected by the examples. Any new example script must place its mock data here. - -- **sphinx-gallery conf keys are stable and must not be changed**: The `sphinx_gallery_conf` dict at `/home/rogerio/git/idecomp/docs/source/conf.py` lines 72-76 uses `examples_dirs`, `gallery_dirs`, and `backreferences_dir` paths that are consistent with the project's directory layout. These paths must not be altered in future tickets — the gallery output goes to `docs/build/html/examples/` and `docs/source/gen_modules/generated/`. - -- **Brand colors from inewave sibling project are used verbatim**: The Furo CSS variable values (`#2962ff` / `#5c8aff`) match the inewave project's Furo configuration. This is an explicit cross-project consistency choice. Future theming work should consult the inewave conf.py before introducing any color changes. - ---- - -## Surprises & Deviations - -- **ticket-007 required zero code changes**: The ticket was allocated 2 points with medium confidence, anticipating potential API deprecation fixes or cfinterface fallback configuration. In practice, all 4 example scripts were already using the current API, the mock data was complete, and the gallery built cleanly without `abort_on_example_error`. The verification step was still valuable — it confirmed the gallery is healthy — but the ticket was a pure audit, not an implementation. - -- **conf.py was already partially modernized before the plan**: The original `conf.py` (as of commit `f84091f`) had the full scaffold: autosummary, intersphinx, sphinx-gallery, numpydoc, plotly renderer, SVG logo. The only gaps were the RTD theme, `sphinx_rtd_theme` in extensions, the legacy `pygments_style = "sphinx"`, no dark pygments style, and `from typing import List`. Epic 2 was a targeted diff, not a rewrite. - -- **Git history shows conf.py was not changed in the epic-01 commit**: The epic-01 completion commit (`e20d968`) included `pyproject.toml` and CI workflow changes but did not touch `conf.py`. The furo dependency was added to `pyproject.toml` in that commit, but the theme activation in `conf.py` was deferred to epic-02 as designed. This confirms the correct sequencing. - -- **No `html_logo` change needed**: The ticket spec noted the logo path must be preserved. The original `html_logo = "_static/logo_idecomp_svg.svg"` was already set before this epic and Furo supports the same `html_logo` config key as RTD. No path changes were needed. - ---- - -## Recommendations for Future Epics - -- **Epic 3 (Documentation Content)**: New RST pages added to `docs/source/` will render with the Furo theme. Use `.. code-block:: python` with explicit language hints — Furo's monokai dark style is optimized for syntax-highlighted blocks. Avoid using `::` (anonymous code blocks) since pygments will not apply syntax coloring. Build locally with `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build` before finalizing any RST content. - -- **Epic 3 (autosummary)**: The `autosummary_generate = True` setting at `/home/rogerio/git/idecomp/docs/source/conf.py` line 30 auto-generates stub pages from `.. autosummary::` directives. When ticket-011 improves the API reference, ensure that `_templates/autosummary/` contains the correct templates. The `numpydoc_show_class_members = False` setting (line 61) suppresses member tables in class pages — adjust this if the improved autosummary should show members inline. - -- **Epic 3 (intersphinx)**: The intersphinx mapping at `/home/rogerio/git/idecomp/docs/source/conf.py` lines 62-70 includes `cfinterface` pointing to `https://rjmalves.github.io/cfinterface/`. Any new documentation page that cross-references cfinterface types can use `:class:`cfinterface.SomeClass`` and it will resolve. Do not add cfinterface types to the API reference directly. - -- **Epic 4 (CONTRIBUTING.md)**: When documenting the local docs build process, the command is `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build`. The built output at `docs/build/html/index.html` can be opened directly. The gallery examples take significant time to execute on first build but are cached in `docs/source/examples/` on subsequent runs. - -- **No future sphinx-rtd-theme work needed**: The string `sphinx_rtd_theme` no longer appears anywhere in `docs/source/conf.py`. Any future search for this string returning results would indicate a regression. The pyproject.toml `docs` extra group (added in epic-01) lists `furo` as the theme package — do not re-add `sphinx-rtd-theme`. diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md deleted file mode 100644 index 51bb6fd..0000000 --- a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-migrate-sphinx-theme-to-furo.md +++ /dev/null @@ -1,126 +0,0 @@ -# ticket-006 Migrate Sphinx Theme to Furo - -## Context - -### Background - -idecomp's documentation uses `sphinx-rtd-theme`, which lacks dark mode and has a dated appearance. The inewave overhaul migrated to Furo, a modern Sphinx theme with built-in dark/light mode toggle and clean typography. idecomp should adopt the same theme for consistency across sibling projects. - -### Relation to Epic - -First ticket of Epic 2. Depends on ticket-001 which replaces `sphinx-rtd-theme` with `furo` in the docs dependency group. Blocks ticket-007 (gallery examples must be verified after theme change). - -### Current State - -File: `/home/rogerio/git/idecomp/docs/source/conf.py` (129 lines) - -- `html_theme = "sphinx_rtd_theme"` -- `extensions` list includes `"sphinx_rtd_theme"` -- `html_theme_options` configured for RTD: `logo_only`, `collapse_navigation`, `sticky_navigation`, `navigation_depth`, `includehidden`, `titles_only` -- `pygments_style = "sphinx"` -- `html_logo = "_static/logo_idecomp_svg.svg"` -- Existing extensions: autosummary, autodoc, intersphinx, mathjax, viewcode, githubpages, sphinx_gallery, numpydoc - -## Specification - -### Requirements - -1. Replace `html_theme = "sphinx_rtd_theme"` with `html_theme = "furo"` -2. Remove `"sphinx_rtd_theme"` from the `extensions` list (Furo is not loaded as an extension) -3. Replace `html_theme_options` with Furo-specific options: - ```python - html_theme_options = { - "light_css_variables": { - "color-brand-primary": "#2962ff", - "color-brand-content": "#2962ff", - }, - "dark_css_variables": { - "color-brand-primary": "#5c8aff", - "color-brand-content": "#5c8aff", - }, - "sidebar_hide_name": True, - } - ``` -4. Change `pygments_style` to `"friendly"` and add `pygments_dark_style = "monokai"` for dark mode code blocks -5. Keep `html_logo` pointing to the existing SVG logo -6. Keep all other extensions unchanged (autosummary, autodoc, intersphinx, mathjax, viewcode, githubpages, sphinx_gallery, numpydoc) -7. Remove the `from typing import List` import if no longer needed (check if `exclude_patterns: List[str]` can use `list[str]` since Python >= 3.10) - -### Inputs/Props - -- File: `/home/rogerio/git/idecomp/docs/source/conf.py` - -### Outputs/Behavior - -- `uv run sphinx-build -M html docs/source docs/build` produces documentation with Furo theme -- The site has a dark/light mode toggle button in the header -- Code blocks use friendly pygments in light mode and monokai in dark mode -- The idecomp SVG logo appears in the sidebar - -### Error Handling - -- If Furo is not installed, sphinx-build fails with a clear error about missing theme -- If the logo path is wrong, Furo shows a warning but builds successfully - -## Acceptance Criteria - -- [ ] Given the updated `conf.py`, when searching for `sphinx_rtd_theme`, then the string does not appear anywhere in the file (not in `extensions`, not in `html_theme`, not in imports) -- [ ] Given the updated `conf.py`, when inspecting `html_theme`, then it equals `"furo"` -- [ ] Given the updated `conf.py`, when inspecting `pygments_style` and `pygments_dark_style`, then they equal `"friendly"` and `"monokai"` respectively -- [ ] Given the updated `conf.py`, when inspecting `html_theme_options`, then it contains `"dark_css_variables"` and `"sidebar_hide_name"` keys (Furo-specific options, not RTD options) -- [ ] Given a virtual environment with the docs extras installed, when running `uv run sphinx-build -M html docs/source docs/build`, then the build completes without errors and `docs/build/html/index.html` contains `furo` in its HTML source - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/docs/source/conf.py` -2. Remove `"sphinx_rtd_theme"` from the `extensions` list (line 49) -3. Change `html_theme = "sphinx_rtd_theme"` to `html_theme = "furo"` (line 89) -4. Replace the entire `html_theme_options` dict with Furo options -5. Change `pygments_style = "sphinx"` to `pygments_style = "friendly"` (line 81) -6. Add `pygments_dark_style = "monokai"` after the pygments_style line -7. Optionally update `exclude_patterns: List[str] = []` to `exclude_patterns: list[str] = []` and remove the `from typing import List` import -8. Run `uv run sphinx-build -M html docs/source docs/build` to verify - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/docs/source/conf.py` - -### Patterns to Follow - -- Match the inewave Furo configuration for consistency -- Keep all existing extensions except `sphinx_rtd_theme` -- Furo does not need to be listed in extensions; it is configured purely via `html_theme` - -### Pitfalls to Avoid - -- Do NOT remove `sphinx.ext.githubpages` from extensions (it creates the `.nojekyll` file needed for GitHub Pages) -- Do NOT remove `html_logo` -- Furo supports logos via the same `html_logo` config -- Do NOT add Furo to extensions -- it is a theme, not an extension; adding it causes a warning -- The `html_theme_options` keys are completely different between RTD and Furo; do not try to adapt the old keys - -## Testing Requirements - -### Unit Tests - -- Not applicable (configuration file) - -### Integration Tests - -- Run `uv run sphinx-build -M html docs/source docs/build` and verify it exits with code 0 -- Verify `docs/build/html/index.html` exists and references Furo CSS - -### E2E Tests - -- Open the built docs locally and verify the dark mode toggle works - -## Dependencies - -- **Blocked By**: ticket-001-modernize-pyproject-toml.md (furo must be in the docs dependency group) -- **Blocks**: ticket-007-update-sphinx-gallery-examples.md - -## Effort Estimate - -**Points**: 3 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md b/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md deleted file mode 100644 index 37a7810..0000000 --- a/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-007-update-sphinx-gallery-examples.md +++ /dev/null @@ -1,135 +0,0 @@ -# ticket-007 Update Sphinx-Gallery Examples for Furo Compatibility - -## Context - -### Background - -idecomp has 4 example scripts in `/examples/` that are rendered by sphinx-gallery into the documentation. After migrating to the Furo theme (ticket-006), these examples must be verified to build correctly. The inewave overhaul revealed that sphinx-gallery can fail due to cfinterface import errors during example execution, and example scripts may use deprecated API patterns. - -### Relation to Epic - -Second and final ticket of Epic 2. Depends on ticket-006 (Furo must be configured before verifying gallery builds). - -### Current State - -Directory: `/home/rogerio/git/idecomp/examples/` - -- `plot_dadger.py` - Example reading/plotting dadger data -- `plot_dec_oper_sist.py` - Example reading/plotting dec_oper_sist data -- `plot_edit_dadger.py` - Example editing dadger data -- `plot_relato.py` - Example reading/plotting relato data -- `README.rst` - Gallery index page -- `decomp/` - Subdirectory (likely contains mock data for examples) - -Sphinx-gallery config in `conf.py`: - -```python -sphinx_gallery_conf = { - "examples_dirs": "../../examples", - "gallery_dirs": "examples", - "backreferences_dir": "gen_modules/generated", -} -``` - -## Specification - -### Requirements - -1. Review each of the 4 example scripts for: - - Deprecated API patterns (e.g., `le_arquivo`/`escreve_arquivo` which were deprecated in v1.0.0) - - Import errors that would cause sphinx-gallery to fail during build - - Plotly renderer compatibility with sphinx-gallery (the `pio.renderers.default = "sphinx_gallery"` is already set in conf.py) -2. Run `uv run sphinx-build -M html docs/source docs/build` and verify the gallery builds without errors -3. If any example script fails: - - Fix import paths or API usage - - If cfinterface import errors prevent execution, add `sphinx_gallery_conf["abort_on_example_error"] = False` to `conf.py` as a fallback -4. Verify the gallery renders correctly with Furo theme (thumbnail grid, example pages) -5. Update `README.rst` in the examples directory if its content references sphinx-rtd-theme or outdated information - -### Inputs/Props - -- Example scripts: `/home/rogerio/git/idecomp/examples/plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py` -- Gallery config: `/home/rogerio/git/idecomp/docs/source/conf.py` (sphinx_gallery_conf section) -- Example data: `/home/rogerio/git/idecomp/examples/decomp/` - -### Outputs/Behavior - -- All 4 example scripts execute during sphinx-build without errors (or gracefully handle import failures) -- The gallery page at `examples/index.html` shows thumbnails for all examples -- Individual example pages render correctly with Furo theme - -### Error Handling - -- If cfinterface import errors prevent example execution, set `abort_on_example_error = False` to allow partial gallery builds -- If plotly rendering fails in sphinx-gallery, add `plot_gallery = False` for affected examples or switch to matplotlib-only plots - -## Acceptance Criteria - -- [ ] Given the example scripts in `/home/rogerio/git/idecomp/examples/`, when inspecting each script, then no deprecated API patterns (`le_arquivo`, `escreve_arquivo`) are used -- [ ] Given a virtual environment with docs extras, when running `uv run sphinx-build -M html docs/source docs/build`, then the sphinx-gallery extension produces output in `docs/build/html/examples/` without errors (exit code 0) -- [ ] Given the built documentation, when inspecting `docs/build/html/examples/index.html`, then it contains thumbnail entries for all 4 example scripts -- [ ] Given the `examples/README.rst`, when inspecting its content, then it does not reference `sphinx-rtd-theme` or any removed/outdated concepts - -## Implementation Guide - -### Suggested Approach - -1. Read each example script in `/home/rogerio/git/idecomp/examples/`: - - `plot_dadger.py` - - `plot_dec_oper_sist.py` - - `plot_edit_dadger.py` - - `plot_relato.py` -2. Check for deprecated API usage (`le_arquivo`, `escreve_arquivo`) and fix if found -3. Check that imports resolve correctly (the examples likely import from `idecomp.decomp`) -4. Run the full sphinx-build: `uv run sphinx-build -M html docs/source docs/build` -5. If gallery errors occur: - - Check error messages for cfinterface import issues - - If needed, add `"abort_on_example_error": False` to `sphinx_gallery_conf` in `conf.py` -6. Inspect the generated gallery HTML to verify thumbnails and example pages render -7. Review and update `examples/README.rst` if needed - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/examples/plot_dadger.py` (if deprecated APIs found) -- `/home/rogerio/git/idecomp/examples/plot_dec_oper_sist.py` (if deprecated APIs found) -- `/home/rogerio/git/idecomp/examples/plot_edit_dadger.py` (if deprecated APIs found) -- `/home/rogerio/git/idecomp/examples/plot_relato.py` (if deprecated APIs found) -- `/home/rogerio/git/idecomp/docs/source/conf.py` (only if `abort_on_example_error` needed) - -### Patterns to Follow - -- Preserve existing example functionality; only fix deprecated patterns -- Use the current idecomp API (class-based file reading, not deprecated `le_arquivo`/`escreve_arquivo`) -- Follow the plotly sphinx-gallery renderer pattern already configured in `conf.py` - -### Pitfalls to Avoid - -- Do NOT rewrite examples entirely; make minimal changes to fix compatibility -- Do NOT remove the `pio.renderers.default = "sphinx_gallery"` line from `conf.py`; it is required for plotly charts in gallery -- Do NOT change `sphinx_gallery_conf["examples_dirs"]` or `"gallery_dirs"` paths -- cfinterface import errors at example execution time are a known issue; use `abort_on_example_error = False` rather than trying to fix cfinterface - -## Testing Requirements - -### Unit Tests - -- Not applicable - -### Integration Tests - -- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0 -- Verify `docs/build/html/examples/index.html` exists and is non-empty - -### E2E Tests - -- Open the built gallery page in a browser and verify thumbnails and example pages render - -## Dependencies - -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md -- **Blocks**: None (Epic 3 depends on Epic 2 being complete, but not specifically on this ticket) - -## Effort Estimate - -**Points**: 2 -**Confidence**: Medium (cfinterface import issues may require investigation) diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md b/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md deleted file mode 100644 index f470775..0000000 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md +++ /dev/null @@ -1,36 +0,0 @@ -# Epic 3: Documentation Content Expansion - -## Goal - -Expand the idecomp documentation with three new guide pages (architecture, FAQ, performance), improve the API reference with autosummary blocks, and update the index.rst toctree. All content in Brazilian Portuguese. - -## Scope - -- Create `arquitetura.rst` page describing idecomp's package structure, cfinterface framework, and data flow -- Create `faq.rst` page with 15+ frequently asked questions about DECOMP file handling -- Create `desempenho.rst` page with performance tips, import times, batch optimization -- Add autosummary blocks to module index RSTs for decomp/ and libs/ -- Update `index.rst` toctree with new "Guias" section - -## Tickets - -| Order | Ticket | Title | Points | -| ----- | ---------- | --------------------------------------------- | ------ | -| 1 | ticket-008 | Create Architecture Documentation Page | 3 | -| 2 | ticket-009 | Create FAQ Documentation Page | 3 | -| 3 | ticket-010 | Create Performance Guide Page | 3 | -| 4 | ticket-011 | Improve API Reference with Autosummary Blocks | 2 | -| 5 | ticket-012 | Update index.rst Toctree with Guias Section | 1 | - -## Dependencies - -- ticket-008 through ticket-011 are independent of each other -- ticket-012 depends on ticket-008, ticket-009, ticket-010 (needs the new pages to exist) -- All tickets depend on Epic 2 completion (Furo theme must be in place) - -## Completion Criteria - -- Three new .rst files exist in `docs/source/guias/` -- Sphinx build succeeds with no warnings for the new pages -- API reference pages show autosummary tables with class/method listings -- index.rst toctree includes the "Guias" section diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md deleted file mode 100644 index af7845d..0000000 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-architecture-page.md +++ /dev/null @@ -1,117 +0,0 @@ -# ticket-008 Create Architecture Documentation Page - -## Context - -### Background - -The idecomp documentation currently has an "Apresentacao" (overview) section and a "Geral" section with installation, tutorial, and contribution pages, but lacks a dedicated architecture page explaining the package's internal structure. New users and contributors have no guide to understand how idecomp maps DECOMP binary/text files to Python objects via the cfinterface framework. This ticket creates an `arquitetura.rst` page in a new `docs/source/guias/` directory to fill that gap. All content is written in Brazilian Portuguese. - -### Relation to Epic - -This is the first of three guide pages (architecture, FAQ, performance) that comprise Epic 3's content expansion. The architecture page provides foundational understanding that the FAQ and performance pages build upon. Ticket-012 will add this page to the index.rst toctree. - -### Current State - -- `docs/source/guias/` directory does not exist yet; it must be created. -- The Furo theme is active (`html_theme = "furo"` in `docs/source/conf.py`), with `language = "pt_BR"`. -- `idecomp/__init__.py` eagerly imports the `decomp` subpackage (`from . import decomp`). -- `idecomp/decomp/__init__.py` eagerly imports all 41 file classes (Dadger, Relato, DecOperSist, etc.). -- File classes in `idecomp/decomp/` inherit from cfinterface base classes: `RegisterFile` (for register-based files like dadger) and `BlockFile` (for block-based files like relato). CSV-based outputs use `ArquivoCSV`. -- Register/Block models live in `idecomp/decomp/modelos/` (45 files) and `idecomp/decomp/modelos/blocos/`. -- `idecomp/libs/` contains 2 modules: `restricoes.py` and `usinas_hidreletricas.py` with a `modelos/` subdirectory. -- intersphinx is configured for cfinterface at `https://rjmalves.github.io/cfinterface/`. - -## Specification - -### Requirements - -1. Create directory `docs/source/guias/` if it does not exist. -2. Create file `docs/source/guias/arquitetura.rst` containing: - - A title "Arquitetura do idecomp" - - A section "Visao Geral" (2-3 paragraphs) explaining that idecomp provides a Python interface to DECOMP files, built on the cfinterface framework. - - A section "Estrutura de Pacotes" describing the top-level layout: `idecomp/decomp/` (41 file classes for DECOMP files), `idecomp/libs/` (2 classes for auxiliary LIBS files), `idecomp/decomp/modelos/` (register/block models), `idecomp/decomp/modelos/blocos/` (block definitions), `idecomp/decomp/modelos/arquivoscsv/` (CSV-based output adapter). - - A section "Framework cfinterface" explaining the three file base classes: `RegisterFile` (e.g., Dadger), `BlockFile` (e.g., Relato), and `ArquivoCSV` (e.g., DecOperSist), with intersphinx cross-references to cfinterface documentation. - - A section "Fluxo de Dados" describing the data flow: raw DECOMP file on disk -> `ClassName.read(path)` -> parsed Python object -> `.property` accessors return `pd.DataFrame` or typed values -> optional `.write(path)` for input files. - - A section "Modelos e Registros" explaining how each file class delegates parsing to model classes in `modelos/` (Register subclasses for line-oriented formats, Block subclasses for section-oriented formats). -3. All prose must be in Brazilian Portuguese (pt_BR). -4. Use `.. code-block:: python` for all code examples (not anonymous `::` blocks) per Furo dark-mode convention from learnings. -5. Include at least one code example showing the read/write flow (e.g., `Dadger.read()` and property access). -6. Cross-reference cfinterface classes using intersphinx (e.g., `:class:`cfinterface.files.registerfile.RegisterFile``). - -### Inputs/Props - -- No runtime inputs. This is a static RST documentation file. - -### Outputs/Behavior - -- A new RST file at `docs/source/guias/arquitetura.rst` that renders correctly with `uv run sphinx-build -M html docs/source docs/build`. -- The page appears under the Furo sidebar when added to the toctree (done in ticket-012). - -### Error Handling - -- Not applicable (static documentation content). - -## Acceptance Criteria - -- [ ] Given the directory `docs/source/guias/` does not exist, when the ticket is implemented, then the directory `docs/source/guias/` exists and contains the file `arquitetura.rst`. -- [ ] Given the file `docs/source/guias/arquitetura.rst` exists, when running `uv run sphinx-build -M html docs/source docs/build 2>&1 | grep -c "arquitetura"`, then the output count is >= 1 (file is processed) and the build exits with code 0. -- [ ] Given the file `docs/source/guias/arquitetura.rst` is opened, when inspecting its content, then it contains exactly 5 sections: "Visao Geral", "Estrutura de Pacotes", "Framework cfinterface", "Fluxo de Dados", and "Modelos e Registros". -- [ ] Given the file `docs/source/guias/arquitetura.rst` is opened, when searching for `.. code-block:: python`, then at least 1 match is found (code example present with explicit directive). - -## Implementation Guide - -### Suggested Approach - -1. Create the `docs/source/guias/` directory. -2. Write `arquitetura.rst` with the RST structure. Use the existing `docs/source/geral/tutorial.rst` as a style reference for Brazilian Portuguese prose and `.. code-block:: python` usage. -3. For the package structure section, reference the actual module layout: `idecomp/decomp/__init__.py` imports 41 classes; `idecomp/libs/__init__.py` imports `UsinasHidreletricas`; `idecomp/decomp/modelos/` contains 45+ model files. -4. For the cfinterface section, use intersphinx cross-references. The mapping is already configured in `conf.py` line 69: `"cfinterface": ("https://rjmalves.github.io/cfinterface/", None)`. -5. For the data flow section, use a simple code example like: - ```python - from idecomp.decomp import Dadger - arq = Dadger.read("./dadger.rv0") - titulo = arq.te # acessa o registro TE - ``` -6. Verify the build locally: `uv run sphinx-build -M html docs/source docs/build`. - -### Key Files to Modify - -- `docs/source/guias/arquitetura.rst` (new file, ~80-120 lines) - -### Patterns to Follow - -- Follow the RST heading style used in `docs/source/geral/tutorial.rst`: `=` underline for title, `-` underline for sections. -- Follow the Brazilian Portuguese prose style from `tutorial.rst` and `apresentacao.rst`. -- Use `.. code-block:: python` (never anonymous `::` blocks) per Furo convention from learnings. - -### Pitfalls to Avoid - -- Do NOT add the page to `index.rst` toctree -- that is ticket-012's scope. -- Do NOT add Furo to the `extensions` list in `conf.py` -- it is a theme, not an extension (learnings). -- Do NOT use anonymous `::` code blocks -- Furo's monokai dark style only applies to `.. code-block::` directives. -- Do NOT deeply document cfinterface internals -- link to cfinterface docs via intersphinx instead. - -## Testing Requirements - -### Unit Tests - -- Not applicable (documentation content). - -### Integration Tests - -- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. -- Verify the built HTML file exists at `docs/build/html/guias/arquitetura.html`. - -### E2E Tests - -- Not applicable. - -## Dependencies - -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (completed -- Furo theme is active) -- **Blocks**: ticket-012-update-index-toctree.md - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md deleted file mode 100644 index f824cb6..0000000 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-faq-page.md +++ /dev/null @@ -1,128 +0,0 @@ -# ticket-009 Create FAQ Documentation Page - -## Context - -### Background - -The idecomp documentation has no FAQ page. Users encountering common issues with DECOMP file handling -- installation problems, read/write patterns, DataFrame manipulation, error messages, and version compatibility -- must search through scattered tutorial content or open GitHub issues. A dedicated FAQ page in Brazilian Portuguese consolidates answers to the most common questions in one discoverable location. - -### Relation to Epic - -This is the second of three guide pages in Epic 3. It is independent of ticket-008 (architecture) and ticket-010 (performance) and can be implemented in parallel. Ticket-012 will add this page to the index.rst toctree. - -### Current State - -- `docs/source/guias/` directory may or may not exist yet (ticket-008 creates it; this ticket must also create it if not present). -- The tutorial page (`docs/source/geral/tutorial.rst`) demonstrates the `ClassName.read()` / `.write()` pattern and DataFrame property access, serving as a style reference. -- `idecomp/decomp/__init__.py` exports 41 file classes. The most commonly used are: `Dadger`, `Relato`, `DecOperSist`, `Vazoes`, `Hidr`, `Caso`. -- File classes use `ClassName.read(path)` for reading and `instance.write(path)` for writing (input files only; output files have no `write` method). -- Some files support versioning via cfinterface's `VERSIONS` dict (e.g., `DecOperSist` has versions `"31.0.2"` and `"31.1.2"`). -- Mock data for examples lives at `examples/decomp/` (dadger.rv0, dec_oper_sist.csv, relato.rv0). - -## Specification - -### Requirements - -1. Create directory `docs/source/guias/` if it does not exist. -2. Create file `docs/source/guias/faq.rst` containing: - - A title "Perguntas Frequentes (FAQ)" - - Organized into 5 thematic sections using RST section headers: - 1. **Instalacao** (3 questions): pip install issues, version conflicts, cfinterface dependency. - 2. **Leitura de Arquivos** (4 questions): how to read a file, supported file formats, handling missing files, file encoding. - 3. **Escrita de Arquivos** (3 questions): how to write modified input files, why output files cannot be written, how to handle write errors. - 4. **DataFrames e Dados** (3 questions): how to access data as DataFrames, how to filter/modify DataFrames, how properties map to DECOMP blocks/registers. - 5. **Erros Comuns** (3 questions): common error messages and their solutions (e.g., cfinterface parse errors, version mismatch, missing registers). - - Minimum 16 Q&A entries total across all sections. - - Each question uses a bold RST inline markup or a subsection header. - - Each answer includes at least one `.. code-block:: python` example where applicable (minimum 8 code examples across all answers). -3. All prose must be in Brazilian Portuguese (pt_BR). -4. Use `.. code-block:: python` for all code examples (not anonymous `::` blocks). -5. Code examples must use the current class-based API (`ClassName.read()`), never deprecated patterns. - -### Inputs/Props - -- No runtime inputs. Static RST documentation file. - -### Outputs/Behavior - -- A new RST file at `docs/source/guias/faq.rst` that renders correctly with `uv run sphinx-build -M html docs/source docs/build`. - -### Error Handling - -- Not applicable (static documentation content). - -## Acceptance Criteria - -- [ ] Given the ticket is implemented, when checking the filesystem, then the file `docs/source/guias/faq.rst` exists. -- [ ] Given `docs/source/guias/faq.rst` exists, when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0 and `docs/build/html/guias/faq.html` is produced. -- [ ] Given the file `docs/source/guias/faq.rst` is opened, when counting RST section headers at the second level (underlined with `-`), then there are exactly 5 thematic sections. -- [ ] Given the file `docs/source/guias/faq.rst` is opened, when counting question entries (bold text lines or third-level headers), then there are at least 16 entries. -- [ ] Given the file `docs/source/guias/faq.rst` is opened, when counting occurrences of `.. code-block:: python`, then there are at least 8 occurrences. - -## Implementation Guide - -### Suggested Approach - -1. Create `docs/source/guias/` directory if it does not exist. -2. Write `faq.rst` with the 5-section structure. Use the following question structure for consistency: - - ```rst - Como ler um arquivo do DECOMP? - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Para ler qualquer arquivo do DECOMP, utilize o metodo ``read`` da classe correspondente: - - .. code-block:: python - - from idecomp.decomp import Dadger - arq = Dadger.read("./dadger.rv0") - ``` - -3. For the "Instalacao" section, cover: `pip install idecomp`, cfinterface version pin (`<=1.8.3`), and Python version compatibility. -4. For "Leitura de Arquivos", cover: basic read pattern, listing all 41 supported classes, handling `FileNotFoundError`, and file encoding (UTF-8 vs Latin-1 in older DECOMP files). -5. For "Escrita de Arquivos", cover: the `write()` method on input files, explain that output files (Relato, DecOperSist, etc.) are read-only, and handling write permissions. -6. For "DataFrames e Dados", cover: how properties like `arq.convergencia` return `pd.DataFrame`, how to filter DataFrames, and how properties map to internal Register/Block models. -7. For "Erros Comuns", cover: cfinterface parse errors (malformed DECOMP files), version detection with `VERSIONS` dict, and missing register/block handling. -8. Verify locally: `uv run sphinx-build -M html docs/source docs/build`. - -### Key Files to Modify - -- `docs/source/guias/faq.rst` (new file, ~150-200 lines) - -### Patterns to Follow - -- Use `^` underline for third-level subsection headers (questions) within `-`-underlined sections, matching RST conventions from `docs/source/geral/tutorial.rst`. -- Brazilian Portuguese prose style from existing docs. -- `.. code-block:: python` for all code (Furo dark-mode convention). - -### Pitfalls to Avoid - -- Do NOT add the page to `index.rst` toctree -- that is ticket-012's scope. -- Do NOT use deprecated API patterns (`le_arquivo`, `escreve_arquivo`) -- all examples must use `ClassName.read()` / `.write()`. -- Do NOT reference `tests/mocks/` directory in examples -- use generic file paths that users would have in their own environments. -- Do NOT use anonymous `::` code blocks. - -## Testing Requirements - -### Unit Tests - -- Not applicable (documentation content). - -### Integration Tests - -- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. -- Verify `docs/build/html/guias/faq.html` exists. - -### E2E Tests - -- Not applicable. - -## Dependencies - -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (completed) -- **Blocks**: ticket-012-update-index-toctree.md - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md deleted file mode 100644 index 6444b29..0000000 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-guide.md +++ /dev/null @@ -1,135 +0,0 @@ -# ticket-010 Create Performance Guide Page - -## Context - -### Background - -The idecomp documentation has no guidance on performance characteristics or optimization strategies. Users processing large DECOMP datasets or running batch operations have no reference for expected read/write times, memory considerations, or efficient patterns. This ticket creates a `desempenho.rst` page with practical performance guidance, written in Brazilian Portuguese. - -### Relation to Epic - -This is the third of three guide pages in Epic 3. It is independent of ticket-008 and ticket-009 and can be implemented in parallel. Ticket-012 will add this page to the index.rst toctree. - -### Current State - -- `docs/source/guias/` directory may or may not exist yet (created by ticket-008 or ticket-009). -- `idecomp/__init__.py` eagerly imports the `decomp` subpackage, which in turn eagerly imports all 41 file classes from `idecomp/decomp/__init__.py`. This means `import idecomp` triggers the import of all 41 class definitions. -- There is no `benchmarks/` directory in the repository. -- The codebase uses `pandas` DataFrames for all tabular data output. File classes like `DecOperSist` can produce large DataFrames with columns for estagio, cenario, patamar, etc. -- Some file classes support version detection via cfinterface's `VERSIONS` dict (e.g., `DecOperSist` checks versions `"31.0.2"` and `"31.1.2"`). -- Gallery examples in `examples/` demonstrate typical usage patterns with mock data. - -## Specification - -### Requirements - -1. Create directory `docs/source/guias/` if it does not exist. -2. Create file `docs/source/guias/desempenho.rst` containing: - - A title "Guia de Desempenho" - - A section "Importacao" (2-3 paragraphs) documenting the eager import behavior: `import idecomp` loads all 41 class definitions. Recommend importing specific classes when startup time matters (`from idecomp.decomp.dadger import Dadger` instead of `from idecomp.decomp import Dadger`). - - A section "Leitura e Escrita de Arquivos" describing typical I/O patterns: single-file read, batch processing of multiple files (e.g., reading all `dec_oper_sist.csv` files from multiple scenarios), and tips for efficient batch processing (reading files in a loop, collecting DataFrames, concatenating with `pd.concat`). - - A section "Uso de Memoria" explaining that each read file holds its parsed data in memory; for large batch operations, recommend reading, extracting the needed DataFrame, and discarding the file object. Include a code example showing this pattern. - - A section "Dicas de Otimizacao" with 3-5 practical tips: use specific imports, process files in batches with generators, filter DataFrames early, use `del` to free memory, and consider `gc.collect()` for very large batch operations. -3. All prose must be in Brazilian Portuguese (pt_BR). -4. Use `.. code-block:: python` for all code examples (not anonymous `::` blocks). -5. Include at least 3 code examples showing performance-relevant patterns. -6. Do NOT include actual benchmark numbers (the repository has no benchmarks directory). Instead, describe how users can profile their own workflows using Python's `time` module. - -### Inputs/Props - -- No runtime inputs. Static RST documentation file. - -### Outputs/Behavior - -- A new RST file at `docs/source/guias/desempenho.rst` that renders correctly with `uv run sphinx-build -M html docs/source docs/build`. - -### Error Handling - -- Not applicable (static documentation content). - -## Acceptance Criteria - -- [ ] Given the ticket is implemented, when checking the filesystem, then the file `docs/source/guias/desempenho.rst` exists. -- [ ] Given `docs/source/guias/desempenho.rst` exists, when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0 and `docs/build/html/guias/desempenho.html` is produced. -- [ ] Given the file `docs/source/guias/desempenho.rst` is opened, when inspecting section headers, then it contains exactly 4 sections: "Importacao", "Leitura e Escrita de Arquivos", "Uso de Memoria", and "Dicas de Otimizacao". -- [ ] Given the file `docs/source/guias/desempenho.rst` is opened, when counting occurrences of `.. code-block:: python`, then there are at least 3 occurrences. - -## Implementation Guide - -### Suggested Approach - -1. Create `docs/source/guias/` directory if it does not exist. -2. Write `desempenho.rst` following the 4-section structure. -3. For the "Importacao" section, demonstrate the two import styles: - - ```python - # Importa todos os 41 modulos (mais lento) - from idecomp.decomp import Dadger - - # Importa apenas o modulo necessario (mais rapido) - from idecomp.decomp.dadger import Dadger - ``` - -4. For the "Leitura e Escrita" section, show a batch processing pattern: - - ```python - from pathlib import Path - import pandas as pd - from idecomp.decomp.dec_oper_sist import DecOperSist - - dfs = [] - for caminho in Path("./resultados").glob("*/dec_oper_sist.csv"): - arq = DecOperSist.read(str(caminho)) - dfs.append(arq.tabela) - df_total = pd.concat(dfs, ignore_index=True) - ``` - -5. For "Uso de Memoria", show the read-extract-discard pattern: - ```python - arq = DecOperSist.read("./dec_oper_sist.csv") - df = arq.tabela - del arq # libera o objeto do arquivo da memoria - ``` -6. Verify locally: `uv run sphinx-build -M html docs/source docs/build`. - -### Key Files to Modify - -- `docs/source/guias/desempenho.rst` (new file, ~80-120 lines) - -### Patterns to Follow - -- RST heading style from `docs/source/geral/tutorial.rst`: `=` for title, `-` for sections. -- Brazilian Portuguese prose style from existing docs. -- `.. code-block:: python` for all code examples. - -### Pitfalls to Avoid - -- Do NOT add the page to `index.rst` toctree -- that is ticket-012's scope. -- Do NOT include actual benchmark numbers -- the repo has no benchmarks infrastructure, and numbers would become stale. -- Do NOT recommend lazy import migration -- that is a code change outside the scope of documentation. -- Do NOT use anonymous `::` code blocks. - -## Testing Requirements - -### Unit Tests - -- Not applicable (documentation content). - -### Integration Tests - -- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. -- Verify `docs/build/html/guias/desempenho.html` exists. - -### E2E Tests - -- Not applicable. - -## Dependencies - -- **Blocked By**: ticket-006-migrate-sphinx-theme-to-furo.md (completed) -- **Blocks**: ticket-012-update-index-toctree.md - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md b/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md deleted file mode 100644 index a73bd2a..0000000 --- a/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-improve-api-reference-autosummary.md +++ /dev/null @@ -1,127 +0,0 @@ -# ticket-011 Improve API Reference with Autosummary Blocks - -## Context - -### Background - -The API reference pages for `decomp/` and `libs/` currently use plain `.. toctree::` listings that link to individual class pages but provide no at-a-glance summary of what each class does. Sphinx's `.. autosummary::` directive can generate summary tables showing each class with its one-line docstring, making the API reference more navigable. The `autosummary_generate = True` setting is already enabled in `conf.py`, and the `sphinx.ext.autosummary` extension is already loaded. - -### Relation to Epic - -This ticket is independent of the three guide pages (tickets 008-010) and can be implemented in parallel. It improves the existing API reference section rather than creating new guide content. - -### Current State - -- `docs/source/referencia/decomp/index.rst` contains a `.. toctree::` with 41 entries pointing to individual class RST files in `arquivos/`. -- `docs/source/referencia/libs/index.rst` contains a `.. toctree::` with 2 entries (`restricoes`, `usinas_hidreletricas`). -- Individual class RST files (e.g., `docs/source/referencia/decomp/arquivos/dadger.rst`) use `.. autoclass::` with `:members:` to document each class and its register models. -- `conf.py` has `autosummary_generate = True` (line 30), `sphinx.ext.autosummary` in extensions (line 20), and `templates_path = ["_templates"]` (line 31). -- The `docs/source/_templates/autosummary/` directory does not exist yet. -- `numpydoc_show_class_members = False` (line 61) suppresses member tables on class pages; this ticket does NOT change that setting. - -## Specification - -### Requirements - -1. Add an `.. autosummary::` block to `docs/source/referencia/decomp/index.rst` listing all 41 file classes from `idecomp.decomp`. The block must appear before the existing `.. toctree::` block and use the `:nosignatures:` option to keep the table clean. -2. Add an `.. autosummary::` block to `docs/source/referencia/libs/index.rst` listing the 2 classes from `idecomp.libs` (`Restricoes`, `UsinasHidreletricas`). Same placement and options. -3. Each autosummary entry uses the fully qualified module path (e.g., `idecomp.decomp.dadger.Dadger`) so that autosummary can resolve the class and extract its docstring. -4. Keep the existing `.. toctree::` blocks intact -- the autosummary table supplements them, it does not replace them. -5. Add a brief introductory paragraph in Brazilian Portuguese above each autosummary block explaining that the table below shows all available classes with descriptions. - -### Inputs/Props - -- No runtime inputs. Modifications to existing RST files. - -### Outputs/Behavior - -- The `decomp/index.rst` page renders an autosummary table with 41 rows (one per file class) showing class name and one-line docstring. -- The `libs/index.rst` page renders an autosummary table with 2 rows. -- The existing toctree navigation is preserved unchanged. - -### Error Handling - -- Not applicable (static documentation content). - -## Acceptance Criteria - -- [ ] Given the file `docs/source/referencia/decomp/index.rst` is opened, when searching for `.. autosummary::`, then exactly 1 autosummary block is found containing 41 class entries. -- [ ] Given the file `docs/source/referencia/libs/index.rst` is opened, when searching for `.. autosummary::`, then exactly 1 autosummary block is found containing 2 class entries. -- [ ] Given both index files are modified, when running `uv run sphinx-build -M html docs/source docs/build`, then the build exits with code 0. -- [ ] Given the build succeeds, when opening `docs/build/html/referencia/decomp/index.html` in a browser, then a summary table is visible listing class names with their docstring descriptions. - -## Implementation Guide - -### Suggested Approach - -1. Edit `docs/source/referencia/decomp/index.rst`: - - After the title and label, add a paragraph in Portuguese: "A tabela abaixo lista todas as classes disponiveis para manipulacao dos arquivos do DECOMP." - - Add the autosummary block: - - ```rst - .. autosummary:: - :nosignatures: - - idecomp.decomp.dadger.Dadger - idecomp.decomp.relato.Relato - idecomp.decomp.dec_oper_sist.DecOperSist - ... - ``` - - - List all 41 classes using the fully qualified path from their source module (not from the `__init__.py` re-export), as autosummary needs the actual module path to resolve docstrings. - - Keep the existing `.. toctree::` block below the autosummary block. - -2. Edit `docs/source/referencia/libs/index.rst`: - - Add a similar paragraph and autosummary block with: - - ```rst - .. autosummary:: - :nosignatures: - - idecomp.libs.restricoes.Restricoes - idecomp.libs.usinas_hidreletricas.UsinasHidreletricas - ``` - -3. Build and verify: `uv run sphinx-build -M html docs/source docs/build`. - -### Key Files to Modify - -- `docs/source/referencia/decomp/index.rst` (add ~50 lines for autosummary block) -- `docs/source/referencia/libs/index.rst` (add ~10 lines for autosummary block) - -### Patterns to Follow - -- Use `:nosignatures:` option on autosummary to keep tables clean (class names only, no `__init__` signatures). -- Use fully qualified module paths for autosummary entries (e.g., `idecomp.decomp.dadger.Dadger`), not re-exported paths. - -### Pitfalls to Avoid - -- Do NOT remove or modify the existing `.. toctree::` blocks -- they control sidebar navigation. -- Do NOT change `numpydoc_show_class_members` in `conf.py` -- it is intentionally `False`. -- Do NOT create custom autosummary templates in `_templates/` -- the default templates are sufficient for class summary tables. -- Do NOT use `.. autosummary:: :toctree:` option -- the individual class pages already exist via the toctree; adding `:toctree:` to autosummary would generate duplicate stub pages. - -## Testing Requirements - -### Unit Tests - -- Not applicable (documentation content). - -### Integration Tests - -- Run `uv run sphinx-build -M html docs/source docs/build` and verify exit code 0. -- Verify `docs/build/html/referencia/decomp/index.html` contains `= 3.10, pip primary + uv secondary - -## Tickets - -| Order | Ticket | Title | Points | -| ----- | ---------- | ----------------------------------------------- | ------ | -| 1 | ticket-013 | Expand README with Badges and Sections | 2 | -| 2 | ticket-014 | Create CONTRIBUTING.md | 3 | -| 3 | ticket-015 | Reformat CHANGELOG to Keep a Changelog Standard | 2 | -| 4 | ticket-016 | Update Installation Documentation | 2 | - -## Dependencies - -- ticket-013 depends on ticket-002 (needs final CI workflow name for badge URL) -- ticket-014 depends on ticket-001 and ticket-005 (references dep groups and pre-commit) -- ticket-015 is independent -- ticket-016 is independent - -## Completion Criteria - -- README.md displays CI, codecov, PyPI version, license, and docs badges -- CONTRIBUTING.md covers full contributor workflow with uv-based setup -- CHANGELOG.md follows Keep a Changelog format with Portuguese categories -- Installation docs reference Python >= 3.10 and show both pip and uv commands diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md b/plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md deleted file mode 100644 index 6243b85..0000000 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/learnings.md +++ /dev/null @@ -1,74 +0,0 @@ -# Epic 04 Learnings: Repository Polish - -**Epic**: epic-04-repository-polish -**Date**: 2026-03-09 -**Tickets**: ticket-013 through ticket-016 -**Files changed**: `README.md`, `CONTRIBUTING.md` (new), `CHANGELOG.md`, `docs/source/geral/contribuicao.rst`, `docs/source/geral/instalacao.rst` - ---- - -## Patterns Established - -- **Dual contributor guidance pattern**: CONTRIBUTING.md at the repo root covers environment setup and workflow (GitHub-standard location for contributor onboarding), while `contribuicao.rst` retains the architectural reference content (cfinterface framework explanation, class naming guidelines, DataFrame conventions). The RST file opens with a `.. note::` admonition linking to CONTRIBUTING.md to direct contributors to the right file. See `/home/rogerio/git/idecomp/CONTRIBUTING.md` and `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst`. - -- **CONTRIBUTING.md tool table pattern**: The "Ferramentas de Qualidade" section uses a markdown table with columns for tool name, purpose, and command. This is more scannable than prose lists and makes the `uv run ` prefix convention explicit for all four quality tools (`ruff check`, `ruff format`, `mypy`, `pytest`). See `/home/rogerio/git/idecomp/CONTRIBUTING.md`. - -- **Keep a Changelog with Portuguese category names**: CHANGELOG.md uses `## [X.Y.Z] - YYYY-MM-DD` version headings with `### Adicionado`, `### Corrigido`, `### Modificado`, `### Depreciado` subheadings. Git compare links appear at the bottom as reference-style markdown links. The `[Nao Publicado]` section sits above the first versioned entry. See `/home/rogerio/git/idecomp/CHANGELOG.md`. - -- **`.. tip::` admonition for secondary tooling alternatives**: When a primary command has a modern alternative (e.g., pip vs uv), the alternative is presented in a `.. tip::` admonition rather than inline in the main prose. This keeps the primary path clear while surfacing the alternative without burying it. See `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` lines 30-36. - ---- - -## Architectural Decisions - -- **README does not use accented Portuguese in installation section headings**: The README uses `## Instalacao`, `## Contribuindo`, `## Licenca` without diacritics (matching the pre-existing `## Instalacao` convention in the original file). The body prose retains full accented Portuguese. **Rejected alternative**: using accented headings throughout (rejected for consistency with the existing file's convention and to avoid diff noise). - -- **`contribuicao.rst` cfinterface section preserved verbatim**: The architectural reference content (BlockFile/SectionFile/RegisterFile explanation with cross-references to cfinterface docs) was kept intact and not moved to CONTRIBUTING.md. This content is appropriate for the documentation site, not a contributor onboarding guide. **Rejected alternative**: merging all content into CONTRIBUTING.md (rejected because RST cross-references and intersphinx links in that section would be lost in markdown). - -- **v1.8.2 CHANGELOG date assigned as 2026-02-04**: Since v1.8.2 had no git tag at the time of the implementation (it was a hotfix on the current branch), the ticket spec prescribed using the v1.8.1 tag date. The implementation followed this exactly. Future releases should add a tag for v1.8.2 before comparing dates. See `/home/rogerio/git/idecomp/CHANGELOG.md` line 9. - -- **README installation section uses fenced code blocks without language specifier**: The `pip install idecomp` and `uv add idecomp` commands appear in ` ``` ` blocks without a language specifier (not ` ```bash `), matching markdown convention for simple single-line shell commands where syntax highlighting adds no value. This differs from the RST convention in `instalacao.rst` which uses `.. code-block:: bash` for all commands. - ---- - -## Files & Structures Created - -- `/home/rogerio/git/idecomp/CONTRIBUTING.md` — New contributor onboarding guide covering environment setup, pre-commit hooks, quality tools table, code conventions, test execution, docs build, and PR workflow. 8 `##` sections, ~100 lines. -- `/home/rogerio/git/idecomp/CHANGELOG.md` — Reformatted from ad-hoc `# vX.Y.Z` format to Keep a Changelog standard. 165 lines covering 16 versions from v1.0.0 to v1.8.2 with Portuguese category subheadings and GitHub compare links. -- `/home/rogerio/git/idecomp/README.md` — Expanded from 37 lines to 63 lines. Added 4 shields.io badges (PyPI version, Python versions, license, docs) alongside existing CI and codecov badges. Added "Exemplo Rapido", "Documentacao", "Projetos Relacionados", "Contribuindo", and "Licenca" sections. -- `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` — Updated from 43 lines to 65 lines. Python version requirement corrected to `>= 3.10`, all code blocks converted to `.. code-block:: bash`, `uv` alternatives added, `pip install --upgrade pip` preamble removed, "Verificando a instalacao" subsection added. -- `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` — Updated to add `.. note::` referencing CONTRIBUTING.md at the top, replace `black`/`pylama` references with `ruff`, and add `uv run` prefix to all tool commands in the "Procedimentos de teste" section. - ---- - -## Conventions Adopted - -- **All tool commands in contributor documentation use `uv run ` prefix**: CONTRIBUTING.md, `contribuicao.rst`, and `instalacao.rst` all show `uv run pytest ./tests`, `uv run mypy ./idecomp`, `uv run ruff check ./idecomp` rather than bare invocations. This is consistent with the CI convention from Epic 1. - -- **The `pre-commit install` step uses bare `pip install pre-commit`, not `uv run pre-commit`**: Pre-commit is a system-level tool that installs git hooks; it should be available globally. The CONTRIBUTING.md correctly uses `pip install pre-commit && pre-commit install` rather than attempting to install it via the project's virtual environment. See `/home/rogerio/git/idecomp/CONTRIBUTING.md` "Hooks de Pre-commit" section. - -- **GitHub compare links in CHANGELOG use unbracketed version numbers in the anchor text**: The reference-style links at the bottom of CHANGELOG.md use `[1.8.2]:` (no `v` prefix in the bracket) while the URL itself uses `v1.8.1...v1.8.2` with the `v` prefix. The version heading uses `## [1.8.2] - YYYY-MM-DD` (no `v` in heading). This is the Keep a Changelog standard. See `/home/rogerio/git/idecomp/CHANGELOG.md` lines 148-164. - -- **`.. tip::` for uv alternatives in RST, not `.. note::`**: Alternative installation methods via `uv` use `.. tip::` admonitions (not `.. note::`) to signal that these are improvements over the primary path rather than cautionary information. This is consistent with Furo's rendering of admonition types. - ---- - -## Surprises & Deviations - -- **README "Exemplo Rapido" imports from `idecomp.decomp` package, not `idecomp.decomp.dadger` module**: The ticket spec showed `from idecomp.decomp.dadger import Dadger`, but the implementation used `from idecomp.decomp import Dadger`. The package-level import is cleaner for a README code example and is valid since `Dadger` is exported from `idecomp.decomp.__init__`. The ticket's "Pitfalls to Avoid" section only prohibited the deprecated `le_arquivo` API, not the import path style. - -- **CONTRIBUTING.md contains 8 `##` sections, not the "at least 6" specified in acceptance criteria C1**: The implementation added a "Fluxo de Pull Requests" section beyond what the minimum acceptance criterion required. All acceptance criteria were met, and the additional section covers the PR workflow requirement from the spec body. - -- **`instalacao.rst` grew to 65 lines, within the specified 55-70 line estimate**: The ticket estimate was accurate. The removal of the `pip install --upgrade pip` block and the addition of the "Verificando a instalacao" subsection nearly cancelled out. - -- **No surprises on CHANGELOG reformatting**: All 16 version entries categorized cleanly using the classification rules in the ticket's implementation guide. No version had ambiguous entries requiring judgment calls beyond the prescribed categories. - ---- - -## Recommendations for Future Epics - -- This is the final epic of the `infra-docs-overhaul` plan. The full modernization is complete. -- When adding a new release, add the version to CHANGELOG.md following the `## [X.Y.Z] - YYYY-MM-DD` pattern under the existing `## [Nao Publicado]` section, then move the entries down and update the compare links at the bottom. File: `/home/rogerio/git/idecomp/CHANGELOG.md`. -- When adding a new contributor (or updating the contributor workflow), update both CONTRIBUTING.md (environment/workflow) and `contribuicao.rst` (architectural guidance) — they have complementary scope and both should stay current. -- The "Exemplo Rapido" in README.md uses `dadger.ct(estagio=1)` — if the `ct` property API changes, this example will need updating. File: `/home/rogerio/git/idecomp/README.md` lines 18-28. -- Any new Python minimum version bump must be updated in at least 4 places: `pyproject.toml` (`requires-python`), `README.md`, `docs/source/geral/instalacao.rst`, and the Python badge URL (shields.io `pyversions` badge is automatic from PyPI, but prose statements must be updated manually). diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md deleted file mode 100644 index 9670cd0..0000000 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-expand-readme.md +++ /dev/null @@ -1,120 +0,0 @@ -# ticket-013 Expand README with Badges and Sections - -## Context - -### Background - -The current `README.md` is a minimal 37-line file with only two badges (CI and codecov), a brief description paragraph, a short installation section referencing the outdated Python >= 3.8 requirement, and a documentation link. As the primary landing page for the GitHub repository, it needs to be expanded with a complete set of badges, structured sections in Brazilian Portuguese, a code example, and links to the newly created documentation site. - -### Relation to Epic - -This is the first ticket in Epic 4 (Repository Polish). It modernizes the repository's most visible public-facing file. The badge URLs depend on CI workflow names established in ticket-002 (already completed). The documentation links reference the Sphinx site built and deployed in Epics 2-3. - -### Current State - -The file `/home/rogerio/git/idecomp/README.md` contains: - -- Two badges: `tests` (from `main.yml`) and `codecov` -- A description paragraph in Portuguese -- A 3-item feature bullet list -- An installation section with `Python >= 3.8` (incorrect, should be `>= 3.10`) -- A `pip install` and `pip install git+...` command pair -- A documentation link to `https://rjmalves.github.io/idecomp` - -## Specification - -### Requirements - -1. Add badges in a single row at the top: CI (already present), codecov (already present), PyPI version (shields.io), Python version (shields.io), License (shields.io), Docs (shields.io linking to GitHub Pages) -2. Keep the existing description paragraph; expand the feature bullet list to 5-6 items covering: file read/write, DataFrame output, class-per-file mapping, cfinterface foundation, type safety -3. Add a "Exemplo Rapido" section with a code block showing `Dadger.read()` and accessing a property -4. Modernize the "Instalacao" section: Python >= 3.10, `pip install idecomp` as primary, `uv add idecomp` as secondary alternative -5. Add a "Projetos Relacionados" section linking to `inewave` and `cfinterface` GitHub repos -6. Add a "Contribuindo" section with a one-liner pointing to `CONTRIBUTING.md` -7. Add a "Licenca" section referencing MIT and the `LICENSE.md` file -8. All prose in Brazilian Portuguese (pt_BR) - -### Inputs/Props - -- Badge URLs use `https://img.shields.io/` for PyPI, Python, license, and docs badges -- CI badge URL: `https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg` (already correct in file) -- Codecov badge URL: already correct in file -- PyPI badge: `https://img.shields.io/pypi/v/idecomp` -- Python badge: `https://img.shields.io/pypi/pyversions/idecomp` -- License badge: `https://img.shields.io/pypi/l/idecomp` -- Docs badge: `https://img.shields.io/badge/docs-online-blue` linking to `https://rjmalves.github.io/idecomp/` - -### Outputs/Behavior - -A complete README.md file of approximately 80-120 lines with all sections described above, rendering correctly on GitHub with all badges displayed inline. - -### Error Handling - -Not applicable (static markdown file). - -## Acceptance Criteria - -- [ ] Given the file `/home/rogerio/git/idecomp/README.md`, when inspected, then it contains exactly 6 badge image links in the first content block: tests (main.yml), codecov, PyPI version, Python versions, license, and docs -- [ ] Given the README badge block, when the CI badge URL is checked, then it references `actions/workflows/main.yml/badge.svg` (not `tests.yml` or any other filename) -- [ ] Given the README "Instalacao" section, when the Python version text is checked, then it states `Python >= 3.10` (not `>= 3.8`) -- [ ] Given the README "Instalacao" section, when the installation commands are checked, then it shows `pip install idecomp` as primary and `uv add idecomp` as a secondary alternative -- [ ] Given the README "Exemplo Rapido" section, when inspected, then it contains a fenced Python code block using `Dadger` class with the class-based `read()` API pattern - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/README.md` -2. Replace the badge block at the top with all 6 badges, each as `[![name](image-url)](link-url)` on a single line separated by spaces -3. Keep the existing description paragraph (lines 6-7 of current file) unchanged -4. Expand the feature bullet list (currently 3 items) to include: leitura e escrita de arquivos, dados tabulares com pandas DataFrame, mapeamento classe-por-arquivo, base no framework cfinterface, tipagem estatica -5. Add the "Exemplo Rapido" section with a `python` fenced code block showing: - ```python - from idecomp.decomp.dadger import Dadger - dadger = Dadger.read("dadger.rv0") - ``` -6. Rewrite the "Instalacao" section with the correct Python version and both pip/uv commands -7. Add sections: "Documentacao" (link to site), "Projetos Relacionados" (inewave, cfinterface), "Contribuindo" (link to CONTRIBUTING.md), "Licenca" (MIT) -8. All section headings use `##` markdown format (already the convention in the current file) - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/README.md` (rewrite) - -### Patterns to Follow - -- Badge formatting: `[![alt](img-url)](link-url)` with two trailing spaces for line break between badge line and next content -- Section headings in Portuguese without accents in markdown (matching current file's `## Instalacao` pattern — note: the current README uses `## Instalacao` without cedilla, but proper Portuguese uses accents; follow the existing convention for heading text) -- Code blocks use triple-backtick fences with `python` language specifier (markdown convention, not RST) - -### Pitfalls to Avoid - -- Do NOT use `actions/workflows/tests.yml` for the CI badge — the workflow file is `main.yml` even though its display name is `tests` -- Do NOT reference `publish.yml` anywhere — it was renamed to `release.yml` -- Do NOT state Python >= 3.8 — the actual requirement in `pyproject.toml` is `>= 3.10` -- Do NOT add a `dev-requirements.txt` reference — the project uses `pyproject.toml` extras -- Do NOT use the deprecated `le_arquivo`/`escreve_arquivo` API in the code example — use the class-based `ClassName.read()` pattern - -## Testing Requirements - -### Unit Tests - -Not applicable (static markdown file). - -### Integration Tests - -Not applicable. - -### E2E Tests - -Verify the README renders correctly by visually inspecting `https://github.com/rjmalves/idecomp` after pushing, or by running a local markdown preview tool. - -## Dependencies - -- **Blocked By**: ticket-002-restructure-ci-workflow.md (needs final workflow name for badge URL; already completed) -- **Blocks**: None - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md deleted file mode 100644 index c1d6425..0000000 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-014-create-contributing.md +++ /dev/null @@ -1,127 +0,0 @@ -# ticket-014 Create CONTRIBUTING.md - -## Context - -### Background - -The repository currently has no `CONTRIBUTING.md` file. Contributor guidance exists only in `docs/source/geral/contribuicao.rst`, which is outdated: it references `pylama` (replaced by `ruff`), `black` (replaced by `ruff format`), `pip install -r dev-requirements.txt` (replaced by `uv sync --extra dev`), and `pip install git+...` for development installation. A modern `CONTRIBUTING.md` at the repository root is the standard location for contributor onboarding on GitHub. - -### Relation to Epic - -This is the second ticket in Epic 4 (Repository Polish). It creates the contributor workflow document that the README (ticket-013) will link to. It depends on the pre-commit configuration (ticket-005) and pyproject.toml extras (ticket-001), both already completed. - -### Current State - -- `/home/rogerio/git/idecomp/CONTRIBUTING.md` does not exist -- `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` (118 lines) contains: - - cfinterface framework explanation (BlockFile, SectionFile, RegisterFile) - - Outdated dev setup: `pip install -r dev-requirements.txt` - - Code conventions: PEP8, static typing, DataFrame patterns - - Testing: references `pylama` and `black` (deprecated tools) -- Pre-commit config at `/home/rogerio/git/idecomp/.pre-commit-config.yaml` uses `ruff` + `ruff-format` + `mypy` (manual stage) -- `pyproject.toml` defines 4 extras: `test`, `lint`, `docs`, `dev` - -## Specification - -### Requirements - -1. Create `/home/rogerio/git/idecomp/CONTRIBUTING.md` with the following sections (all in pt_BR): - - **Configuracao do Ambiente**: Clone, `uv sync --extra dev`, verify with `uv run pytest ./tests` - - **Hooks de Pre-commit**: `pip install pre-commit && pre-commit install`, explain `stages: [manual]` for mypy, document `pre-commit run --hook-stage manual --all-files` for full mypy check - - **Ferramentas de Qualidade**: ruff (linting + formatting), mypy (tipagem estatica), pytest (testes) - - **Convencoes de Codigo**: PEP8 via ruff, tipagem estatica obrigatoria, propriedades retornam `pd.DataFrame` para dados tabulares, nomenclatura em `snake_case` - - **Executando Testes**: `uv run pytest ./tests`, `uv run pytest --cov=idecomp ./tests` for coverage - - **Construindo a Documentacao**: `uv sync --extra docs`, `uv run sphinx-build -M html docs/source docs/build` - - **Fluxo de Pull Requests**: fork, branch, commit, push, open PR; CI runs lint/typecheck/test/docs automatically -2. Update `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` to: - - Keep the cfinterface framework explanation (lines 1-37 of current file) intact — this is docs-appropriate architectural content - - Keep the "Diretrizes de modelagem" section (lines 53-81) intact — class naming and DataFrame conventions are reference material - - Replace the outdated "Convencoes de codigo" section (lines 83-101) with updated tool references: replace `black` with `ruff format`, remove `pylama` reference - - Replace the outdated "Procedimentos de teste" section (lines 104-118) with: `uv run pytest ./tests`, `uv run mypy ./idecomp`, `uv run ruff check ./idecomp` - - Add a note at the top of the file referencing `CONTRIBUTING.md` for environment setup instructions - -### Inputs/Props - -- All commands use `uv run ` prefix (CI convention from learnings) -- Pre-commit hook configuration from `/home/rogerio/git/idecomp/.pre-commit-config.yaml` -- Dependency extras from `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43 - -### Outputs/Behavior - -- A new `CONTRIBUTING.md` file at the repository root (~80-120 lines) -- An updated `contribuicao.rst` with correct tool references and a pointer to `CONTRIBUTING.md` - -### Error Handling - -Not applicable (static documentation files). - -## Acceptance Criteria - -- [ ] Given the file `/home/rogerio/git/idecomp/CONTRIBUTING.md`, when inspected, then it exists and contains at least 6 markdown `##` section headings -- [ ] Given the CONTRIBUTING.md "Configuracao do Ambiente" section, when the setup commands are checked, then it shows `uv sync --extra dev` (not `pip install -r dev-requirements.txt`) -- [ ] Given the CONTRIBUTING.md "Hooks de Pre-commit" section, when inspected, then it documents `pre-commit run --hook-stage manual --all-files` for mypy and explains the `stages: [manual]` rationale (cfinterface import errors) -- [ ] Given the CONTRIBUTING.md "Ferramentas de Qualidade" section, when tool names are checked, then it references `ruff` for linting and formatting (not `pylama` or `black`) -- [ ] Given the file `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst`, when the "Procedimentos de teste" section is checked, then it shows `uv run pytest`, `uv run mypy`, and `uv run ruff check` commands (not bare `pytest`, `mypy`, or `pylama`) - -## Implementation Guide - -### Suggested Approach - -1. Create `/home/rogerio/git/idecomp/CONTRIBUTING.md` with the section structure from the Requirements -2. For the "Configuracao do Ambiente" section, show the complete flow: `git clone`, `cd idecomp`, `uv sync --extra dev`, `uv run pytest ./tests` -3. For the "Hooks de Pre-commit" section, explain: ruff and ruff-format run automatically on every commit; mypy uses `stages: [manual]` because cfinterface type stubs are incomplete and would block every commit; to run mypy manually use `pre-commit run --hook-stage manual --all-files` or directly `uv run mypy ./idecomp` -4. For "Ferramentas de Qualidade", list each tool with its command: - - `uv run ruff check ./idecomp` (linting) - - `uv run ruff format --check ./idecomp` (format check) - - `uv run mypy ./idecomp` (type check) - - `uv run pytest ./tests` (tests) -5. For "Construindo a Documentacao", show: `uv sync --extra docs` then `uv run sphinx-build -M html docs/source docs/build` -6. Open `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` and: - - Add a `.. note::` admonition after the title directing readers to `CONTRIBUTING.md` for environment setup - - Replace `black` references with `ruff format` - - Replace `pylama ./idecomp --ignore E203` with `uv run ruff check ./idecomp` - - Replace `pytest ./tests` with `uv run pytest ./tests` - - Replace `mypy ./idecomp` with `uv run mypy ./idecomp` - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/CONTRIBUTING.md` (new file) -- `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst` (update outdated sections) - -### Patterns to Follow - -- All tool invocations use `uv run ` prefix (established CI convention) -- Section headings in Portuguese without accents in markdown headings -- Use `.. code-block:: bash` in RST files (not anonymous `::` blocks) — consistent with the guide page convention from Epic 3 - -### Pitfalls to Avoid - -- Do NOT reference `dev-requirements.txt` — it no longer exists; use `uv sync --extra dev` -- Do NOT reference `pylama` or `black` — replaced by `ruff` -- Do NOT reference `publish.yml` — renamed to `release.yml` -- Do NOT delete the cfinterface framework explanation from `contribuicao.rst` — it is valuable architectural reference content that belongs in the docs site -- Do NOT use `--all-extras` in any command — CI convention is `--extra ` - -## Testing Requirements - -### Unit Tests - -Not applicable (static documentation files). - -### Integration Tests - -Not applicable. - -### E2E Tests - -Verify `contribuicao.rst` builds without Sphinx warnings by running `uv run sphinx-build -M html docs/source docs/build` and checking for no warnings related to `contribuicao.rst`. - -## Dependencies - -- **Blocked By**: ticket-001-modernize-pyproject-toml.md, ticket-005-add-pre-commit-hooks.md (both completed) -- **Blocks**: None - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md deleted file mode 100644 index b47eb25..0000000 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-015-reformat-changelog.md +++ /dev/null @@ -1,154 +0,0 @@ -# ticket-015 Reformat CHANGELOG to Keep a Changelog Standard - -## Context - -### Background - -The current `CHANGELOG.md` uses an ad-hoc format: each version is a top-level `# vX.Y.Z` heading followed by an unstructured bullet list. Entries mix different types of changes (new features, bug fixes, dependency updates) without categorization. The file has no dates, no `[Unreleased]` section, and no inter-version comparison links. Reformatting to the Keep a Changelog standard improves readability and enables automated tooling. - -### Relation to Epic - -This is the third ticket in Epic 4 (Repository Polish). It is independent of all other tickets — no blocking dependencies. - -### Current State - -The file `/home/rogerio/git/idecomp/CHANGELOG.md` contains 89 lines covering 15 versions from v1.0.0 to v1.8.2. Each version uses a `# vX.Y.Z` heading with bullet points below. No dates, no categories, no `[Unreleased]` section. - -Git tag dates are available for all versions: - -- v1.8.2: no tag yet (unreleased on current branch) -- v1.8.1: 2026-02-04 -- v1.8.0: 2026-02-02 -- v1.7.3: 2025-12-29 -- v1.7.2: 2025-12-29 -- v1.7.1: 2025-01-22 -- v1.7.0: 2025-01-10 -- v1.6.0: 2024-10-04 -- v1.5.0: 2024-08-16 -- v1.4.0: 2024-04-22 -- v1.3.0: 2024-02-23 -- v1.2.0: 2024-02-05 -- v1.1.1: 2024-01-04 -- v1.1.0: 2023-12-29 -- v1.0.1: 2023-12-21 -- v1.0.0: 2023-12-21 - -## Specification - -### Requirements - -1. Reformat `/home/rogerio/git/idecomp/CHANGELOG.md` to follow the [Keep a Changelog](https://keepachangelog.com/) standard -2. Use Portuguese category names as `###` subheadings under each version: - - **Adicionado** — new features and file support - - **Corrigido** — bug fixes - - **Modificado** — changes to existing functionality, dependency updates, refactors - - **Removido** — removed features or deprecated items - - **Depreciado** — newly deprecated features (if any) -3. Add a `## [Nao Publicado]` section at the top for unreleased changes (currently empty) -4. Version headings format: `## [X.Y.Z] - YYYY-MM-DD` using dates from git tags listed above -5. For v1.8.2, use `2026-02-04` as date (same as v1.8.1 release, since it was a hotfix with no separate tag yet) -6. Add GitHub compare links at the bottom of the file for each version pair -7. Preserve ALL existing content — only restructure, do not delete or rephrase entries -8. Add a file header with the project name and a reference to the Keep a Changelog standard - -### Inputs/Props - -- Current file: `/home/rogerio/git/idecomp/CHANGELOG.md` (89 lines) -- Git tag dates listed in Context section above -- GitHub repo URL: `https://github.com/rjmalves/idecomp` - -### Outputs/Behavior - -A reformatted CHANGELOG.md of approximately 140-180 lines with: - -- Header block explaining the format -- `[Nao Publicado]` section -- Each version with date and categorized entries -- Compare links at the bottom - -### Error Handling - -Not applicable (static markdown file). - -## Acceptance Criteria - -- [ ] Given the file `/home/rogerio/git/idecomp/CHANGELOG.md`, when version headings are checked, then every version from v1.0.0 to v1.8.2 uses the format `## [X.Y.Z] - YYYY-MM-DD` -- [ ] Given the CHANGELOG version v1.7.0, when its entries are checked, then entries are categorized under `### Adicionado`, `### Corrigido`, and `### Modificado` subheadings (v1.7.0 has all three types) -- [ ] Given the CHANGELOG, when the top of the file is checked, then it contains a `## [Nao Publicado]` section before the first versioned section -- [ ] Given the bottom of the CHANGELOG, when compare links are checked, then there is a link `[X.Y.Z]: https://github.com/rjmalves/idecomp/compare/vPREV...vX.Y.Z` for each version pair -- [ ] Given the CHANGELOG entry count, when original entries are compared to reformatted entries, then no content has been deleted — all original bullet points are present (possibly reclassified under category headings) - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/CHANGELOG.md` -2. Add a header: - - ```markdown - # Changelog - - Todas as mudancas relevantes deste projeto serao documentadas neste arquivo. - - O formato segue o [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/). - - ## [Nao Publicado] - ``` - -3. For each existing version entry, classify every bullet point: - - Entries starting with "Suporte a leitura/escrita", "Adicionado", "Novos dados" -> **Adicionado** - - Entries starting with "Fix", "Correcao" -> **Corrigido** - - Entries starting with "Atualiza", "Gestao do projeto", "Dependencia", "Uso de slots", "Padronizacao", "Simplifica", "Descontinuado" -> **Modificado** - - Entries about deprecation ("Metodos le_arquivo e escreve_arquivo deprecados") -> **Depreciado** -4. Rewrite each version heading from `# vX.Y.Z` to `## [X.Y.Z] - YYYY-MM-DD` using the dates from git tags -5. Under each version heading, group entries by category with `###` subheadings -6. At the bottom, add compare links: - ```markdown - [Nao Publicado]: https://github.com/rjmalves/idecomp/compare/v1.8.2...HEAD - [1.8.2]: https://github.com/rjmalves/idecomp/compare/v1.8.1...v1.8.2 - [1.8.1]: https://github.com/rjmalves/idecomp/compare/v1.8.0...v1.8.1 - - ... - [1.0.0]: https://github.com/rjmalves/idecomp/releases/tag/v1.0.0 - ``` - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/CHANGELOG.md` (reformat in place) - -### Patterns to Follow - -- Keep a Changelog standard: `## [version] - date` with `### Category` subheadings -- Portuguese category names: Adicionado, Corrigido, Modificado, Removido, Depreciado -- Compare links use GitHub `compare/vOLD...vNEW` URL format - -### Pitfalls to Avoid - -- Do NOT delete or rephrase any existing entry text — only move entries under category headings -- Do NOT add entries for pre-1.0 versions (they are not in the current CHANGELOG and adding them is out of scope) -- Do NOT use accented characters in section headings (e.g., use "Nao Publicado" not "Nao Publicado" — matching the project's heading convention of omitting accents) -- Do NOT invent dates — use only the git tag dates listed in the Context section - -## Testing Requirements - -### Unit Tests - -Not applicable (static markdown file). - -### Integration Tests - -Not applicable. - -### E2E Tests - -Verify the CHANGELOG renders correctly on GitHub by visual inspection or local markdown preview. - -## Dependencies - -- **Blocked By**: None (independent) -- **Blocks**: None - -## Effort Estimate - -**Points**: 2 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md b/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md deleted file mode 100644 index 0361cec..0000000 --- a/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-016-update-installation-docs.md +++ /dev/null @@ -1,138 +0,0 @@ -# ticket-016 Update Installation Documentation - -## Context - -### Background - -The Sphinx documentation page `docs/source/geral/instalacao.rst` contains outdated information: it states Python >= 3.8 (actual requirement is >= 3.10), recommends `python -m pip install --upgrade pip` as a preamble step, uses anonymous `::` code blocks instead of `.. code-block:: bash`, and does not mention `uv` as an installation alternative. The page needs to be modernized to match the current project configuration. - -### Relation to Epic - -This is the fourth and final ticket in Epic 4 (Repository Polish). It is independent of all other tickets. It updates the Sphinx documentation installation page to be consistent with the modernized `pyproject.toml` (ticket-001, completed) and the README installation section (ticket-013). - -### Current State - -The file `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` contains 43 lines with: - -- Title "Instalacao" with `=` underline (correct RST heading hierarchy) -- Python >= 3.8 compatibility statement (incorrect) -- A paragraph about virtual environments with a link to the `venv` docs -- A `pip install --upgrade pip` preamble section -- "Instalando a versao distribuida oficialmente" section: `pip install idecomp`, `pip install --upgrade idecomp`, `pip install --upgrade idecomp==x.y.z` -- "Instalando a versao de desenvolvimento" section: `pip uninstall idecomp` then `pip install git+...` -- All code blocks use anonymous `::` syntax (not `.. code-block:: bash`) - -## Specification - -### Requirements - -1. Update Python version requirement from `>= 3.8` to `>= 3.10` -2. Remove the `pip install --upgrade pip` preamble — it is unnecessary for modern Python -3. Keep the virtual environment recommendation paragraph (it is still valid) -4. Modernize "Instalando a versao distribuida oficialmente" section: - - Primary: `pip install idecomp` - - Show upgrade: `pip install --upgrade idecomp` - - Show version pin: `pip install idecomp==x.y.z` - - Secondary alternative: `uv add idecomp` (in a separate subsection or note) -5. Modernize "Instalando a versao de desenvolvimento" section: - - Primary: `git clone` + `cd idecomp` + `uv sync --extra dev` - - Alternative: `pip install git+https://github.com/rjmalves/idecomp` - - Remove the `pip uninstall idecomp` preamble step (unnecessary) -6. Add a "Verificando a instalacao" subsection: `python -c "import idecomp; print(idecomp.__version__)"` -7. Replace all anonymous `::` code blocks with explicit `.. code-block:: bash` directives (Furo theme convention from Epic 3 learnings) -8. All prose in Brazilian Portuguese (pt_BR) - -### Inputs/Props - -- Actual Python requirement: `requires-python = ">= 3.10"` from `/home/rogerio/git/idecomp/pyproject.toml` line 13 -- Development extras: `uv sync --extra dev` installs test + lint + docs (from pyproject.toml lines 32-43) -- RST heading hierarchy: `=` for page title, `-` for sections, `^` for subsections (from Epic 3 conventions) - -### Outputs/Behavior - -An updated `instalacao.rst` of approximately 55-70 lines that renders correctly in the Sphinx/Furo documentation site with syntax-highlighted bash code blocks. - -### Error Handling - -Not applicable (static RST documentation file). - -## Acceptance Criteria - -- [ ] Given the file `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst`, when the Python version text is checked, then it states `Python >= 3.10` (not `>= 3.8`) -- [ ] Given the file, when searched for `pip install --upgrade pip` or `pip install ---upgrade pip`, then no such preamble line exists -- [ ] Given the file, when code blocks are checked, then every code block uses `.. code-block:: bash` directive (no anonymous `::` blocks) -- [ ] Given the file, when the `uv` tool is searched, then at least one section mentions `uv add idecomp` or `uv sync --extra dev` as an alternative installation method -- [ ] Given the file, when a "Verificando a instalacao" subsection is searched, then it contains `python -c "import idecomp; print(idecomp.__version__)"` in a code block - -## Implementation Guide - -### Suggested Approach - -1. Open `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` -2. Update line 4: change `Python >= 3.8` to `Python >= 3.10` -3. Keep the virtual environment paragraph (lines 6-7) but simplify slightly -4. Remove the `pip install --upgrade pip` block entirely (lines 9-11) -5. Rewrite "Instalando a versao distribuida oficialmente" section: - - Use `.. code-block:: bash` for all code blocks - - Show `pip install idecomp`, upgrade, and version pin commands - - Add a `.. tip::` admonition for the `uv` alternative: `uv add idecomp` -6. Rewrite "Instalando a versao de desenvolvimento" section: - - Primary flow: `git clone`, `cd idecomp`, `uv sync --extra dev` - - Alternative: `pip install git+https://github.com/rjmalves/idecomp` - - Remove the `pip uninstall` preamble -7. Add "Verificando a instalacao" subsection with `-` underline: - - ```rst - Verificando a instalacao - ------------------------ - - Apos a instalacao, verifique se o pacote esta disponivel: - - .. code-block:: bash - - python -c "import idecomp; print(idecomp.__version__)" - ``` - -8. Verify RST heading hierarchy: `=` for title, `-` for sections, `^` for subsections (if any) - -### Key Files to Modify - -- `/home/rogerio/git/idecomp/docs/source/geral/instalacao.rst` (update) - -### Patterns to Follow - -- Use `.. code-block:: bash` for all shell commands (Furo monokai dark style only applies to syntax-highlighted blocks, per Epic 3 learnings) -- RST heading hierarchy: `=` for page title, `-` for sections (matching `/home/rogerio/git/idecomp/docs/source/geral/tutorial.rst` convention) -- Use `.. tip::` admonition for the uv alternative (non-intrusive, visually distinct) - -### Pitfalls to Avoid - -- Do NOT use anonymous `::` code blocks — Furo dark mode does not highlight them properly -- Do NOT remove the virtual environment recommendation paragraph — it is still valid guidance -- Do NOT use `--all-extras` in any uv command — use `--extra dev` specifically -- Do NOT add conda/mamba instructions — out of scope for this ticket -- Do NOT change the filename or its position in the toctree — `geral/instalacao` is already linked from `index.rst` - -## Testing Requirements - -### Unit Tests - -Not applicable (static RST documentation file). - -### Integration Tests - -Not applicable. - -### E2E Tests - -Verify the page builds without Sphinx warnings by running `uv run sphinx-build -M html docs/source docs/build` and checking for no warnings related to `instalacao.rst`. - -## Dependencies - -- **Blocked By**: None (independent) -- **Blocks**: None - -## Effort Estimate - -**Points**: 1 -**Confidence**: High diff --git a/plans/infra-docs-overhaul/learnings/epic-01-summary.md b/plans/infra-docs-overhaul/learnings/epic-01-summary.md deleted file mode 100644 index ff6c197..0000000 --- a/plans/infra-docs-overhaul/learnings/epic-01-summary.md +++ /dev/null @@ -1,86 +0,0 @@ -# Epic 01 Learnings: Packaging & CI Modernization - -**Epic**: epic-01-packaging-ci-modernization -**Date**: 2026-03-08 -**Tickets**: ticket-001 through ticket-005 -**Files changed**: `pyproject.toml`, `.github/workflows/main.yml`, `.github/workflows/docs.yml`, `.github/workflows/release.yml` (renamed from `publish.yml`), `.pre-commit-config.yaml` (new) - ---- - -## Patterns Established - -- **Dependency group split (4 extras)**: `pyproject.toml` uses `test = [pytest, pytest-cov]`, `lint = [ruff, mypy]`, `docs = [sphinx, furo, ...]`, `dev = ["idecomp[test,lint,docs]"]` as a self-referencing alias. Each CI job installs only its required extra via `uv sync --extra `. See `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. - -- **4-parallel-job CI pattern**: `main.yml` runs `lint`, `typecheck`, `test`, `docs` as fully independent jobs. `lint` and `typecheck` both install `--extra lint`. `test` matrix covers Python 3.10/3.11/3.12 with codecov upload. `docs` installs `--extra docs` and runs sphinx-build. No `needs:` dependencies between any of the 4 jobs. See `/home/rogerio/git/idecomp/.github/workflows/main.yml`. - -- **Official GitHub Pages 2-job deploy pattern**: `docs.yml` uses a `build` job (sphinx-build + `actions/upload-pages-artifact@v3`) followed by a `deploy` job (`needs: build`, `actions/deploy-pages@v4`, `environment: github-pages`). Top-level `permissions: pages: write, id-token: write` and `concurrency: group: "pages", cancel-in-progress: false` are mandatory for this pattern. See `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. - -- **Tag-triggered release with regex version validation**: `release.yml` triggers on `push: tags: ["v*"]`. Version is extracted with `grep -oP '__version__\s*=\s*"\K[^"]+'` on `idecomp/__init__.py`, not via `exec()` or Python import. GitHub release is created after PyPI publish via `gh release create ${{ github.ref_name }} --generate-notes`. See `/home/rogerio/git/idecomp/.github/workflows/release.yml`. - -- **mypy pre-commit hook at `stages: [manual]`**: The `.pre-commit-config.yaml` registers mypy as `language: system`, `entry: uv run mypy ./idecomp`, `pass_filenames: false`, `stages: [manual]`. This prevents cfinterface import errors from blocking every commit while keeping mypy available for deliberate invocation via `pre-commit run --hook-stage manual --all-files`. See `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. - -- **ruff hook with `args: [--fix]`**: The ruff pre-commit hook includes `args: [--fix]` so it auto-corrects fixable issues rather than just reporting them. Without this flag, the hook only reports and the commit still requires manual re-run. See `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. - ---- - -## Architectural Decisions - -- **`strict = true` replaces explicit mypy flags**: The ticket specified many redundant mypy flags (`disallow_untyped_defs`, `no_implicit_optional`, etc.) explicitly alongside `strict = true`. The implementation correctly omits the redundant flags because `strict = true` already enables all of them. Only the overrides that conflict with strict mode (`warn_return_any = false`) were made explicit. **Rejected alternative**: listing all flags verbosely for documentation purposes (rejected because `strict = true` is self-documenting and the redundancy creates maintenance risk). - -- **`uv python install` without version in docs.yml**: The `build` job in `docs.yml` uses `uv python install` with no explicit version, relying on uv to select from `.python-version` or `pyproject.toml`'s `requires-python`. This differs from `main.yml` which explicitly pins `3.12`. **Accepted because**: docs builds are not version-sensitive; using the project default is appropriate and avoids a hard pin that diverges from the matrix. - -- **`publish.yml` renamed to `release.yml` via git mv**: The file rename was done preserving git history through `git mv`, not a delete-and-create. This preserves the audit trail for the old trigger model. Future maintainers should verify `release.yml` exists and `publish.yml` does not before adding any references. - -- **Workflow name kept as `tests`**: `main.yml` retains `name: tests` even though it now runs lint, typecheck, test, and docs. This was a deliberate constraint: the README badge URL references `actions/workflows/main.yml/badge.svg` and renaming the workflow would visually change the badge label but not break it. Badge references must be audited in ticket-013 before changing. - ---- - -## Files & Structures Created - -- `/home/rogerio/git/idecomp/pyproject.toml` — Modernized with split dependency groups, full classifiers, `[tool.mypy]` with strict settings and cfinterface override, `[tool.ruff.lint]` with E/F/I/UP rule sets. -- `/home/rogerio/git/idecomp/.github/workflows/main.yml` — 4-parallel-job workflow replacing the former single sequential job. -- `/home/rogerio/git/idecomp/.github/workflows/docs.yml` — 2-job official Pages deployment replacing `peaceiris/actions-gh-pages@v3`. -- `/home/rogerio/git/idecomp/.github/workflows/release.yml` — Tag-triggered release workflow (renamed from `publish.yml`). -- `/home/rogerio/git/idecomp/.pre-commit-config.yaml` — New file with ruff (auto-fix) and mypy (manual stage) hooks. - ---- - -## Conventions Adopted - -- **All tool invocations in CI use `uv run `**: Never call `python -m pytest` or bare `ruff` in workflow steps; always prefix with `uv run`. This ensures the project's virtual environment is active and the correct tool version is used. - -- **CI installs only the minimal required extras per job**: `lint` job uses `--extra lint`, `test` job uses `--extra test`, `docs` job uses `--extra docs`. Never use `--all-extras` or `--dev` in CI — this would increase install time and mask missing dependency declarations. - -- **`cfinterface` pin `>=1.8,<=1.8.3` is immutable**: This constraint must not be removed or widened in any future ticket. It is at `/home/rogerio/git/idecomp/pyproject.toml` line 9 and exists because newer versions of cfinterface introduced breaking changes. - -- **`warn_return_any = false` must appear in both `[tool.mypy]` and `[[tool.mypy.overrides]]` for cfinterface**: `strict = true` re-enables `warn_return_any`. The top-level override relaxes it for all code; the per-module override is belt-and-suspenders for cfinterface specifically. - -- **Concurrency group `"pages"` with `cancel-in-progress: false`**: When migrating any future docs deployment workflow, always preserve this exact concurrency configuration. `cancel-in-progress: true` would abort a running deployment mid-way and leave Pages in an inconsistent state. - -- **Version extraction in release workflows uses grep regex, never exec/import**: `grep -oP '__version__\s*=\s*"\K[^"]+'` is the correct pattern. Python import (`from idecomp import __version__`) fails in fresh CI environments because relative imports in `__init__.py` require the package to be properly installed. `exec(open('__init__.py').read())` fails for the same reason. - ---- - -## Surprises & Deviations - -- **mypy strict flag verbosity**: Tickets specified all individual strict flags explicitly in the spec section for documentation clarity, but the final `pyproject.toml` correctly omits them as redundant under `strict = true`. This was the right call. Future ticket authors should note that specifying redundant flags in a spec is an anti-pattern — the spec should match what should be written, not serve as documentation for what `strict` implies. - -- **`docs.yml` Python version unspecified**: The ticket did not specify which Python version the docs `build` job should use (unlike `main.yml` which explicitly pins 3.12 for non-matrix jobs). The implementation used `uv python install` without a version, which is valid. Future tickets touching `docs.yml` should note this difference from `main.yml` and decide whether to align them. - -- **`release.yml` does not run `ruff format --check`**: The validation steps in `release.yml` include `ruff check` but not `ruff format --check`. This is consistent with the ticket spec (which only listed `ruff check` in the validation steps), but differs from `main.yml`'s `lint` job which checks both. This is an intentional asymmetry: format violations are caught in normal CI before a release is tagged. - -- **`publish.yml` deleted**: The git status shows the file was renamed (`RM .github/workflows/publish.yml -> .github/workflows/release.yml`). Any documentation in epics 3 or 4 that references `publish.yml` must be updated to `release.yml`. - ---- - -## Recommendations for Future Epics - -- **Epic 2 (Sphinx Modernization)**: The `docs` extra in `pyproject.toml` already includes `furo` (replacing `sphinx-rtd-theme`). The `docs.yml` workflow already builds with `--extra docs`. Epic 2 only needs to change `docs/source/conf.py` — no workflow or dependency changes required. Verify `furo` is resolvable before starting ticket-006. - -- **Epic 3 (Documentation Content)**: All new Sphinx pages must build successfully under the `docs` job in `main.yml` and the `build` job in `docs.yml`. Test sphinx-build locally with `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build` before writing tickets. If cfinterface causes sphinx-gallery import errors, the mitigation pattern from inewave (skipping problematic examples) should be documented in the ticket. - -- **Epic 4 (Repository Polish)**: CONTRIBUTING.md should document the pre-commit hook setup: `pip install pre-commit && pre-commit install` for regular hooks, and `pre-commit run --hook-stage manual --all-files` for mypy. The `stages: [manual]` design decision should be explained. Reference `/home/rogerio/git/idecomp/.pre-commit-config.yaml` directly in the contributing guide. - -- **Badge URL stability**: `main.yml` workflow name is `tests`. If ticket-013 updates the README, it should reference `[![Tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](...)`. Do not assume the workflow display name will match — it is `tests`, not `CI` or `main`. - -- **Release workflow trigger**: Any ticket that involves bumping `__version__` in `idecomp/__init__.py` must also consider that pushing a `v*` tag will trigger `release.yml` and attempt PyPI publication. Coordinate version bumps and tag pushes explicitly. The version validation step in `release.yml` will catch mismatches but the workflow will still be triggered. diff --git a/plans/infra-docs-overhaul/learnings/epic-02-summary.md b/plans/infra-docs-overhaul/learnings/epic-02-summary.md deleted file mode 100644 index 4ad2b0e..0000000 --- a/plans/infra-docs-overhaul/learnings/epic-02-summary.md +++ /dev/null @@ -1,69 +0,0 @@ -# Accumulated Learnings: Epics 01-02 - -**Plan**: infra-docs-overhaul -**Last updated**: 2026-03-08 -**Covers**: epic-01-packaging-ci-modernization, epic-02-sphinx-modernization - ---- - -## Dependency & Packaging Conventions - -- `pyproject.toml` uses 4 extras: `test`, `lint`, `docs`, `dev`; `docs` includes `furo` (not `sphinx-rtd-theme`). File: `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. -- `cfinterface` pin `>=1.8,<=1.8.3` is immutable — do not widen or remove. File: `/home/rogerio/git/idecomp/pyproject.toml` line 9. -- CI installs only the minimal required extra per job via `uv sync --extra `. Never use `--all-extras` in CI. -- All tool invocations in CI use `uv run ` — never bare `ruff`, `pytest`, or `sphinx-build`. - -## CI Workflow Patterns - -- `main.yml` runs 4 independent parallel jobs: `lint`, `typecheck`, `test`, `docs`. No `needs:` dependencies between them. File: `/home/rogerio/git/idecomp/.github/workflows/main.yml`. -- `docs.yml` uses the official 2-job Pages pattern: `build` job (sphinx-build + `upload-pages-artifact@v3`) then `deploy` job (`deploy-pages@v4`). Concurrency group `"pages"` with `cancel-in-progress: false` is mandatory. File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. -- `release.yml` triggers on `push: tags: ["v*"]`. Version extracted via `grep -oP` from `idecomp/__init__.py` — never via Python import or `exec()`. File: `/home/rogerio/git/idecomp/.github/workflows/release.yml`. -- `main.yml` workflow `name:` is `tests` — badge URLs reference this. Do not rename. -- `publish.yml` was renamed to `release.yml` via `git mv`. Any docs referencing `publish.yml` must be updated. - -## Pre-commit Hook Conventions - -- mypy hook uses `stages: [manual]` to prevent cfinterface import errors from blocking commits. Run with `pre-commit run --hook-stage manual --all-files`. File: `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. -- ruff hook uses `args: [--fix]` for auto-correction. Without this flag it only reports and requires manual re-run. - -## mypy Configuration - -- `strict = true` replaces all explicit strict flags — do not repeat `disallow_untyped_defs` etc. alongside it. -- `warn_return_any = false` must appear in `[tool.mypy]` to override what `strict = true` enables. File: `/home/rogerio/git/idecomp/pyproject.toml`. - -## Sphinx / Furo Theme Conventions - -- `html_theme = "furo"` is the only required theme setting; Furo must NOT appear in `extensions`. Adding it to extensions causes a warning. File: `/home/rogerio/git/idecomp/docs/source/conf.py` line 43. -- Dual pygments settings are required for Furo: `pygments_style = "friendly"` (light) and `pygments_dark_style = "monokai"` (dark). A single setting leaves dark mode unstyled. File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 39-40. -- `html_theme_options` must be a complete replacement when switching themes — no RTD keys are reusable with Furo. -- Brand colors match inewave sibling project verbatim: `#2962ff` (light) / `#5c8aff` (dark). Do not change without consulting inewave `conf.py`. -- `"sidebar_hide_name": True` prevents duplicate project name display when `html_logo` is set. -- `sphinx.ext.githubpages` must remain in `extensions` regardless of theme — it creates the `.nojekyll` file required by GitHub Pages. -- `pio.renderers.default = "sphinx_gallery"` at the top of `conf.py` is required for plotly charts in the gallery — do not remove. - -## Documentation Build Command - -- Local build: `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build` -- Built output: `docs/build/html/index.html` -- Gallery examples are cached after first build in `docs/source/examples/` — subsequent builds are faster. - -## Sphinx Gallery Structure - -- 4 example scripts in `/home/rogerio/git/idecomp/examples/`: `plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`. -- Mock data lives at `/home/rogerio/git/idecomp/examples/decomp/` (`dadger.rv0`, `dec_oper_sist.csv`, `relato.rv0`). New examples must add their mock data here. -- `sphinx_gallery_conf` paths (`examples_dirs`, `gallery_dirs`, `backreferences_dir`) must not be changed. File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 72-76. -- `abort_on_example_error = False` was anticipated as a cfinterface fallback but was NOT needed — all 4 examples run cleanly with the existing mock data. -- All examples use the current class-based API (`ClassName.read()`). No deprecated `le_arquivo`/`escreve_arquivo` patterns exist. - -## Documentation Content Conventions (for Epic 3) - -- Use explicit `.. code-block:: python` in RST pages — Furo's monokai dark style only applies to syntax-highlighted blocks, not anonymous `::` blocks. -- `autosummary_generate = True` auto-generates stub pages. Templates at `docs/source/_templates/autosummary/`. File: `/home/rogerio/git/idecomp/docs/source/conf.py` line 30. -- `numpydoc_show_class_members = False` suppresses member tables in class pages — adjust if ticket-011 should show members inline. -- intersphinx includes cfinterface at `https://rjmalves.github.io/cfinterface/` — cross-references to cfinterface types will resolve without explicit declarations. - -## Release & Version Conventions (for Epic 4) - -- Pushing a `v*` tag triggers `release.yml` and attempts PyPI publication. Coordinate version bumps and tag pushes explicitly. -- CONTRIBUTING.md must document: `pip install pre-commit && pre-commit install` for hooks, and `pre-commit run --hook-stage manual --all-files` for mypy. Explain the `stages: [manual]` design decision. -- Badge URL: `[![Tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](...)` — references `main.yml` by filename, not display name. diff --git a/plans/infra-docs-overhaul/learnings/epic-03-summary.md b/plans/infra-docs-overhaul/learnings/epic-03-summary.md deleted file mode 100644 index f98d2ef..0000000 --- a/plans/infra-docs-overhaul/learnings/epic-03-summary.md +++ /dev/null @@ -1,89 +0,0 @@ -# Accumulated Learnings: Epics 01-03 - -**Plan**: infra-docs-overhaul -**Last updated**: 2026-03-09 -**Covers**: epic-01-packaging-ci-modernization, epic-02-sphinx-modernization, epic-03-documentation-content - ---- - -## Dependency & Packaging Conventions - -- `pyproject.toml` uses 4 extras: `test`, `lint`, `docs`, `dev`; `docs` includes `furo` (not `sphinx-rtd-theme`). File: `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. -- `cfinterface` pin `>=1.8,<=1.8.3` is immutable — do not widen or remove. File: `/home/rogerio/git/idecomp/pyproject.toml` line 9. -- CI installs only the minimal required extra per job via `uv sync --extra `. Never use `--all-extras` in CI. -- All tool invocations in CI use `uv run ` — never bare `ruff`, `pytest`, or `sphinx-build`. - -## CI Workflow Patterns - -- `main.yml` runs 4 independent parallel jobs: `lint`, `typecheck`, `test`, `docs`. No `needs:` dependencies. File: `/home/rogerio/git/idecomp/.github/workflows/main.yml`. -- `docs.yml` uses the official 2-job Pages pattern: `build` (sphinx-build + `upload-pages-artifact@v3`) then `deploy` (`deploy-pages@v4`). Concurrency group `"pages"` with `cancel-in-progress: false` is mandatory. File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. -- `release.yml` triggers on `push: tags: ["v*"]`. Version extracted via `grep -oP` from `idecomp/__init__.py` — never via Python import or `exec()`. File: `/home/rogerio/git/idecomp/.github/workflows/release.yml`. -- `main.yml` workflow `name:` is `tests` — badge URLs reference this filename. Do not rename. - -## Pre-commit Hook Conventions - -- mypy hook uses `stages: [manual]` to prevent cfinterface import errors from blocking commits. Run with `pre-commit run --hook-stage manual --all-files`. File: `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. -- ruff hook uses `args: [--fix]` for auto-correction. Without this flag it only reports and requires manual re-run. - -## mypy Configuration - -- `strict = true` replaces all explicit strict flags — do not repeat `disallow_untyped_defs` etc. alongside it. -- `warn_return_any = false` must appear in `[tool.mypy]` to override what `strict = true` enables. File: `/home/rogerio/git/idecomp/pyproject.toml`. - -## Sphinx / Furo Theme Conventions - -- `html_theme = "furo"` is the only required theme setting; Furo must NOT appear in `extensions`. File: `/home/rogerio/git/idecomp/docs/source/conf.py` line 43. -- Dual pygments settings required: `pygments_style = "friendly"` (light) and `pygments_dark_style = "monokai"` (dark). File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 39-40. -- Brand colors: `#2962ff` (light) / `#5c8aff` (dark), matching inewave sibling project verbatim. -- `"sidebar_hide_name": True` prevents duplicate project name display when `html_logo` is set. -- `sphinx.ext.githubpages` must remain in `extensions` regardless of theme — creates `.nojekyll` for GitHub Pages. -- `pio.renderers.default = "sphinx_gallery"` at the top of `conf.py` is required for plotly charts — do not remove. -- Local build: `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build`. - -## Sphinx Gallery Structure - -- 4 example scripts in `/home/rogerio/git/idecomp/examples/`: `plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`. -- Mock data at `/home/rogerio/git/idecomp/examples/decomp/`. New examples must add mock data here. -- `sphinx_gallery_conf` paths must not change. File: `/home/rogerio/git/idecomp/docs/source/conf.py` lines 72-76. -- All examples use class-based API (`ClassName.read()`). No deprecated `le_arquivo`/`escreve_arquivo` patterns exist. -- Gallery files use `:orphan:` directive — `examples/index.rst` and `sg_execution_times.rst` are sphinx-gallery-generated and must not be added to a toctree manually. - -## Documentation Content Conventions - -- Use explicit `.. code-block:: python` in all RST pages — Furo's monokai dark style only applies to syntax-highlighted blocks, not anonymous `::` blocks. Files: `/home/rogerio/git/idecomp/docs/source/guias/`. -- RST heading hierarchy: `=` underline for page title, `-` underline for sections, `^` underline for subsections (questions in FAQ). Reference file: `/home/rogerio/git/idecomp/docs/source/geral/tutorial.rst`. -- All prose is in Brazilian Portuguese (pt_BR). All new guide pages follow this convention. -- Guide pages live in `docs/source/guias/` — three files created: `arquitetura.rst`, `faq.rst`, `desempenho.rst`. Each is 178-374 lines; the ticket size estimates (80-200 lines) were conservative. -- Do NOT add guide pages to `index.rst` in the same ticket that creates them. The toctree update is always a separate, dependent ticket (ticket-012 pattern). -- `docs/source/index.rst` toctree order: Apresentacao → Geral → Guias → Referencia. The "Guias" section uses `:maxdepth: 2` and no `.rst` extension in entries (matching "Geral" convention, not "Referencia" convention). - -## Autosummary Conventions - -- `autosummary_generate = True` is already set in `conf.py` line 30. Do not change. -- Use `.. autosummary:: :nosignatures:` (no `:toctree:` option) — individual class pages already exist via the toctree; `:toctree:` would generate duplicate stub pages. -- Autosummary entries must use fully qualified module paths (e.g., `idecomp.decomp.dadger.Dadger`), not re-exported paths from `__init__.py`. -- The autosummary block goes before the existing `.. toctree::` block in each index file. -- The default autosummary templates are sufficient — do NOT create custom templates in `docs/source/_templates/autosummary/`. Files: `/home/rogerio/git/idecomp/docs/source/referencia/decomp/index.rst`, `/home/rogerio/git/idecomp/docs/source/referencia/libs/index.rst`. - -## Documentation Structure: :orphan: Directive Pattern - -- Pages not linked from any toctree produce Sphinx warnings about unreferenced documents. The `:orphan:` directive suppresses this warning. -- sphinx-gallery auto-generates `examples/index.rst` and `sg_execution_times.rst` with `:orphan:` already set; these files must NOT be manually managed or added to toctrees. -- New guide pages (in `guias/`) must NOT use `:orphan:` — they are properly linked via the "Guias" toctree in `index.rst`. Using `:orphan:` on linked pages is an error. -- If a page needs to exist without being in a toctree (e.g., a draft or a utility page), place `:orphan:` on line 1, before the title underline. - -## API & Codebase Reference Facts - -- `idecomp` exports 41 DECOMP file classes via `idecomp.decomp` (eager import of all 41 in `__init__.py`). -- `idecomp.libs` exports 2 auxiliary classes: `Restricoes` and `UsinasHidreletricas`. -- Three base class types: `RegisterFile` (line-oriented, e.g., Dadger), `BlockFile` (section-oriented, e.g., Relato), `ArquivoCSV` (CSV outputs, e.g., DecOperSist). -- Output files (Relato, DecOperSist, etc.) have no `write` method — they are read-only. -- Properties returning tabular data return `pd.DataFrame`; scalar properties return typed values; missing data returns `None`. -- intersphinx for cfinterface is configured at `conf.py` line 69: `"cfinterface": ("https://rjmalves.github.io/cfinterface/", None)`. - -## Release & Version Conventions (Epic 4) - -- Pushing a `v*` tag triggers `release.yml` and attempts PyPI publication. Coordinate version bumps and tag pushes explicitly. -- CONTRIBUTING.md must document: `pip install pre-commit && pre-commit install` for hooks, and `pre-commit run --hook-stage manual --all-files` for mypy. Explain the `stages: [manual]` design decision. -- Badge URL: `[![Tests](https://github.com/rjmalves/idecomp/actions/workflows/main.yml/badge.svg)](...)` — references `main.yml` by filename, not display name. -- `publish.yml` was renamed to `release.yml`. Any README or CONTRIBUTING docs referencing `publish.yml` must be updated. diff --git a/plans/infra-docs-overhaul/learnings/epic-04-summary.md b/plans/infra-docs-overhaul/learnings/epic-04-summary.md deleted file mode 100644 index 1c89b76..0000000 --- a/plans/infra-docs-overhaul/learnings/epic-04-summary.md +++ /dev/null @@ -1,94 +0,0 @@ -# Accumulated Learnings: Epics 01-04 (Full Plan) - -**Plan**: infra-docs-overhaul -**Last updated**: 2026-03-09 -**Covers**: epic-01 through epic-04 — complete plan retrospective - ---- - -## Dependency & Packaging Conventions - -- `pyproject.toml` uses 4 extras: `test`, `lint`, `docs`, `dev`; `dev` self-references the other three. File: `/home/rogerio/git/idecomp/pyproject.toml` lines 32-43. -- `cfinterface` pin `>=1.8,<=1.8.3` is immutable — do not widen or remove. File: `/home/rogerio/git/idecomp/pyproject.toml` line 9. -- CI installs only the minimal required extra per job via `uv sync --extra `. Never use `--all-extras` in CI. -- All tool invocations in CI and contributor docs use `uv run ` — never bare `ruff`, `pytest`, or `sphinx-build`. -- Python minimum requirement is `>= 3.10` — stated in `pyproject.toml`, `README.md`, and `instalacao.rst`. All three must be kept in sync. - -## CI Workflow Patterns - -- `main.yml` runs 4 independent parallel jobs: `lint`, `typecheck`, `test`, `docs`. No `needs:` dependencies. File: `/home/rogerio/git/idecomp/.github/workflows/main.yml`. -- `docs.yml` uses the official 2-job Pages pattern: `build` then `deploy`. Concurrency group `"pages"` with `cancel-in-progress: false` is mandatory. File: `/home/rogerio/git/idecomp/.github/workflows/docs.yml`. -- `release.yml` triggers on `push: tags: ["v*"]`. Version extracted via `grep -oP` — never via Python import or `exec()`. File: `/home/rogerio/git/idecomp/.github/workflows/release.yml`. -- `main.yml` workflow `name:` is `tests` — badge URLs reference this filename. Do not rename. -- `publish.yml` was renamed to `release.yml`. Any reference to `publish.yml` must be updated everywhere. - -## Pre-commit Hook Conventions - -- mypy hook uses `stages: [manual]` to prevent cfinterface import errors from blocking commits. Run with `pre-commit run --hook-stage manual --all-files`. File: `/home/rogerio/git/idecomp/.pre-commit-config.yaml`. -- ruff hook uses `args: [--fix]` for auto-correction; without it, ruff only reports and requires manual re-run. -- `pip install pre-commit && pre-commit install` uses bare `pip`, not `uv run pre-commit` — pre-commit is a system-level git hook installer. - -## mypy Configuration - -- `strict = true` replaces all explicit strict flags — do not repeat individual flags alongside it. -- `warn_return_any = false` must appear in `[tool.mypy]` to override what `strict = true` enables. File: `/home/rogerio/git/idecomp/pyproject.toml`. - -## Sphinx / Furo Theme Conventions - -- `html_theme = "furo"` is the only required theme setting; Furo must NOT appear in `extensions`. File: `/home/rogerio/git/idecomp/docs/source/conf.py`. -- Dual pygments settings required: `pygments_style = "friendly"` (light) and `pygments_dark_style = "monokai"` (dark). -- Brand colors: `#2962ff` (light) / `#5c8aff` (dark), matching inewave sibling project. -- `sphinx.ext.githubpages` must remain in `extensions` — creates `.nojekyll` for GitHub Pages. -- `pio.renderers.default = "sphinx_gallery"` at the top of `conf.py` is required for plotly charts. -- Local build: `uv sync --extra docs && uv run sphinx-build -M html docs/source docs/build`. - -## RST Documentation Conventions - -- Use explicit `.. code-block:: python` or `.. code-block:: bash` in all RST pages — Furo monokai dark style only applies to syntax-highlighted blocks, not anonymous `::` blocks. -- RST heading hierarchy: `=` underline for page title, `-` for sections, `^` for subsections. Reference: `/home/rogerio/git/idecomp/docs/source/geral/tutorial.rst`. -- All prose is in Brazilian Portuguese (pt_BR). Markdown headings omit diacritics (convention from pre-existing files); RST body prose retains full accented Portuguese. -- Do NOT add new guide pages (`guias/`) to `index.rst` in the same ticket that creates them — toctree updates are always a separate dependent ticket. -- Pages not linked from any toctree should use `:orphan:` on line 1. Pages properly linked via a toctree must NOT have `:orphan:`. - -## Autosummary Conventions - -- Use `.. autosummary:: :nosignatures:` (no `:toctree:` option) — individual class pages already exist; `:toctree:` generates duplicate stubs. -- Autosummary entries use fully qualified module paths (e.g., `idecomp.decomp.dadger.Dadger`), not re-exported paths from `__init__.py`. -- The autosummary block goes before the existing `.. toctree::` block in each reference index file. Files: `/home/rogerio/git/idecomp/docs/source/referencia/decomp/index.rst`, `/home/rogerio/git/idecomp/docs/source/referencia/libs/index.rst`. - -## Sphinx Gallery Structure - -- 4 example scripts in `/home/rogerio/git/idecomp/examples/`: `plot_dadger.py`, `plot_dec_oper_sist.py`, `plot_edit_dadger.py`, `plot_relato.py`. -- Mock data at `/home/rogerio/git/idecomp/examples/decomp/`. New examples must add mock data here. -- sphinx-gallery auto-generates `examples/index.rst` and `sg_execution_times.rst` — do NOT add these to a toctree manually. -- All examples use class-based API (`ClassName.read()`). No deprecated `le_arquivo`/`escreve_arquivo` patterns exist. - -## API Reference Facts - -- `idecomp` exports 41 DECOMP file classes via `idecomp.decomp` and 2 auxiliary classes via `idecomp.libs` (`Restricoes`, `UsinasHidreletricas`). -- Three base class types: `RegisterFile` (line-oriented), `BlockFile` (section-oriented), `ArquivoCSV` (CSV outputs, read-only). -- Properties returning tabular data return `pd.DataFrame`; scalar properties return typed values; missing data returns `None`. -- intersphinx for cfinterface: `conf.py` line 69: `"cfinterface": ("https://rjmalves.github.io/cfinterface/", None)`. - -## Contributor Documentation Pattern - -- CONTRIBUTING.md (root, markdown) handles environment setup, pre-commit hooks, quality tools, code conventions, PR workflow. -- `contribuicao.rst` (docs site) handles cfinterface architecture, class naming guidelines, DataFrame conventions. -- `contribuicao.rst` opens with `.. note::` admonition linking to CONTRIBUTING.md for environment setup. -- Files: `/home/rogerio/git/idecomp/CONTRIBUTING.md`, `/home/rogerio/git/idecomp/docs/source/geral/contribuicao.rst`. - -## CHANGELOG Conventions - -- Keep a Changelog format: `## [X.Y.Z] - YYYY-MM-DD` headings, `### Adicionado/Corrigido/Modificado/Depreciado` subheadings in Portuguese. -- `## [Nao Publicado]` section sits above the first versioned entry. -- GitHub compare links appear as reference-style links at the bottom of the file. -- Version anchor text uses no `v` prefix; URL uses `v` prefix: `[1.8.2]: .../compare/v1.8.1...v1.8.2`. -- File: `/home/rogerio/git/idecomp/CHANGELOG.md`. - -## Plan-wide Quality Observations - -- All 16 tickets scored 1.0 quality except ticket-006 (Furo theme migration, 0.85) where lint/type scoring was neutral due to conf.py-only changes. -- Mean quality score across all 16 tickets: ~0.99. No ticket fell below the 0.75 gate. -- Pure documentation and configuration tickets (markdown, RST, YAML, TOML) consistently score 1.0 because lint, type safety, and test delta dimensions default to 1.0 for non-code files. -- The `open-source-documentation-writer` agent handled all Epic 2-4 tickets; `python-task-automation-developer` handled Epic 1. -- Documentation-only tickets benefit from explicit pitfall lists in ticket specs — every case where an agent avoided a known error (e.g., not using `publish.yml`, not using `--all-extras`, not deleting cfinterface section from RST) traced back to a "Pitfalls to Avoid" entry in the ticket. From 58638d47bbb8cc6c94b3f2fedf92e528f3695854 Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 21:14:31 -0300 Subject: [PATCH 8/9] fix(docs): remove build-time files --- docs/source/sg_execution_times.rst | 46 ------------------------------ 1 file changed, 46 deletions(-) delete mode 100644 docs/source/sg_execution_times.rst diff --git a/docs/source/sg_execution_times.rst b/docs/source/sg_execution_times.rst deleted file mode 100644 index 11317b1..0000000 --- a/docs/source/sg_execution_times.rst +++ /dev/null @@ -1,46 +0,0 @@ - -:orphan: - -.. _sphx_glr_sg_execution_times: - - -Computation times -================= -**00:00.568** total execution time for 4 files **from all galleries**: - -.. container:: - - .. raw:: html - - - - - - - - .. list-table:: - :header-rows: 1 - :class: table table-striped sg-datatable - - * - Example - - Time - - Mem (MB) - * - :ref:`sphx_glr_examples_plot_relato.py` (``../../examples/plot_relato.py``) - - 00:00.415 - - 0.0 - * - :ref:`sphx_glr_examples_plot_dec_oper_sist.py` (``../../examples/plot_dec_oper_sist.py``) - - 00:00.067 - - 0.0 - * - :ref:`sphx_glr_examples_plot_dadger.py` (``../../examples/plot_dadger.py``) - - 00:00.046 - - 0.0 - * - :ref:`sphx_glr_examples_plot_edit_dadger.py` (``../../examples/plot_edit_dadger.py``) - - 00:00.040 - - 0.0 From e1c1f5e28dbb9dea932f77c7fe62899047a36243 Mon Sep 17 00:00:00 2001 From: Rogerio Alves Date: Mon, 9 Mar 2026 21:17:50 -0300 Subject: [PATCH 9/9] chore: bump version to 1.9.0 Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ idecomp/__init__.py | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 088aad1..1c592d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,34 @@ O formato segue o [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/). ## [Nao Publicado] +## [1.9.0] - 2026-03-10 + +### Adicionado + +- Documentação de arquitetura, FAQ e guia de desempenho (pt_BR) +- Blocos autosummary na referência de API para 41 classes do DECOMP e 2 de libs +- Arquivo `CONTRIBUTING.md` com instruções de desenvolvimento +- Workflow de release com publicação automática no PyPI via tag +- Hooks de pre-commit com ruff e mypy +- Suporte a Python 3.13 e 3.14 na matriz de CI + +### Modificado + +- Dependência cfinterface atualizada para `>=1.9.0` +- Dependência pandas atualizada para `>=3.0.0` +- Dependência numpy atualizada para `>=2.2.1` +- Versão mínima de Python alterada de 3.10 para 3.11 +- Tipagem estrita mypy em todos os 97 arquivos fonte +- Tema da documentação migrado de RTD para Furo com suporte a dark mode +- CI reestruturado em 4 jobs paralelos (lint, typecheck, test, docs) +- Deploy da documentação migrado para GitHub Pages oficial +- README expandido com badges, exemplos e seções de contribuição +- CHANGELOG reformatado para o padrão Keep a Changelog + +### Removido + +- Suporte a Python 3.10 + ## [1.8.2] - 2026-02-04 ### Modificado diff --git a/idecomp/__init__.py b/idecomp/__init__.py index 7edfdb4..1cd1457 100644 --- a/idecomp/__init__.py +++ b/idecomp/__init__.py @@ -6,6 +6,6 @@ e saída do DECOMP. """ -__version__ = "1.8.2" +__version__ = "1.9.0" from . import decomp # noqa