Merge pull request 'MIL-001: Project foundation' (#26) from mil-001-project-foundation into main
This commit was merged in pull request #26.
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
# Continuous integration for the Weather Data project (day 25).
|
||||||
|
#
|
||||||
|
# Run by Gitea Actions. The file lives in .gitea/workflows, not in
|
||||||
|
# .github/workflows: GitHub refuses a push that touches .github/workflows from a
|
||||||
|
# token without the workflow scope, and that blocks the push mirror to GitHub.
|
||||||
|
# Runs the same checks as the README, "Run the tests".
|
||||||
|
#
|
||||||
|
# The tests need no display and write only into pytest's temporary folders.
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checks:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out the repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python 3.13
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.13"
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install -e ".[dev]"
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: python -m pytest
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: python -m ruff check src tests
|
||||||
|
|
||||||
|
- name: Format check
|
||||||
|
run: python -m ruff format --check src tests
|
||||||
|
|
||||||
|
- name: Types
|
||||||
|
run: python -m mypy
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# Personal tokens: never commit or import this file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Virtual environment
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
|
|
||||||
|
# Byte-compiled and cached files
|
||||||
|
__pycache__
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# Packaging and build output (build/ also holds the Doxygen HTML)
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.egg-info/
|
||||||
|
*.egg
|
||||||
|
.eggs/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
|
||||||
|
# Test, lint and type-check caches
|
||||||
|
.pytest_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Editors and operating systems
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.codex/agents/
|
||||||
|
|
||||||
|
# Local output of the program: the exported table of the lecture
|
||||||
|
new_data.csv
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Doxygen configuration for the weather data project (Python sources in src/).
|
||||||
|
#
|
||||||
|
# Build: doxygen Doxyfile
|
||||||
|
# Output: build/doxygen/index.html
|
||||||
|
#
|
||||||
|
# Only the settings that differ from the Doxygen defaults are listed. A warning
|
||||||
|
# (for example an undocumented public item) fails the build.
|
||||||
|
|
||||||
|
DOXYFILE_ENCODING = UTF-8
|
||||||
|
PROJECT_NAME = "Weather Data"
|
||||||
|
PROJECT_BRIEF = "Reading CSV weather data with csv and pandas, Udemy's 100 Days of Code (day 25)"
|
||||||
|
OUTPUT_DIRECTORY = build
|
||||||
|
HTML_OUTPUT = doxygen
|
||||||
|
|
||||||
|
# Python sources are documented with Doxygen commands in docstrings and in
|
||||||
|
# "##" comments.
|
||||||
|
OPTIMIZE_OUTPUT_JAVA = YES
|
||||||
|
EXTRACT_ALL = NO
|
||||||
|
EXTRACT_PRIVATE = NO
|
||||||
|
|
||||||
|
INPUT = src
|
||||||
|
FILE_PATTERNS = *.py
|
||||||
|
RECURSIVE = YES
|
||||||
|
SOURCE_BROWSER = YES
|
||||||
|
|
||||||
|
GENERATE_HTML = YES
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
HAVE_DOT = NO
|
||||||
|
|
||||||
|
QUIET = YES
|
||||||
|
WARNINGS = YES
|
||||||
|
WARN_IF_UNDOCUMENTED = YES
|
||||||
|
WARN_IF_DOC_ERROR = YES
|
||||||
|
WARN_IF_INCOMPLETE_DOC = YES
|
||||||
|
# NO: with YES, Doxygen also asks for an @return on every "-> None" function.
|
||||||
|
WARN_NO_PARAMDOC = NO
|
||||||
|
WARN_AS_ERROR = FAIL_ON_WARNINGS
|
||||||
@@ -1,2 +1,151 @@
|
|||||||
# 025-weather
|
# Weather Data (Day 25)
|
||||||
|
|
||||||
|
Reading CSV weather data in Python, first with the built-in `csv` library and then
|
||||||
|
with `pandas`. It is the day-25 assignment of Udemy's *100 Days of Code: The
|
||||||
|
Complete Python Pro Bootcamp*. The lectures read the weather of one week from
|
||||||
|
`weather_data.csv`, collect the temperatures as whole numbers, and then show how
|
||||||
|
`pandas` does the same in one line: a table is a `DataFrame` and a column of it is
|
||||||
|
a `Series`. They convert both to a dictionary and a list, compute the average and
|
||||||
|
the maximum temperature, select rows by a condition (the row of Monday, the row of
|
||||||
|
the hottest day, Monday's temperature in Fahrenheit), create a `DataFrame` from a
|
||||||
|
dictionary, and export it to a CSV file.
|
||||||
|
|
||||||
|
The `csv` part has no runtime dependencies; `pandas` is an optional extra. The code
|
||||||
|
keeps the names of the lectures (`data`, `temperatures`, `temp_list`, `data_dict`,
|
||||||
|
`average_temp`, `monday`, `to_dict`, `to_list`, `mean`, `max`, `to_csv`) so that you
|
||||||
|
can compare it with your own solution.
|
||||||
|
|
||||||
|
> **Status:** in progress. The project foundation (configuration, constants,
|
||||||
|
> documentation settings and continuous integration) is in place; the readers and
|
||||||
|
> the analysis follow in the next milestones, see `docs/project-plan.md`.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.13 or newer.
|
||||||
|
- `git`, to clone the repository.
|
||||||
|
- Optional: [Doxygen](https://www.doxygen.nl/) to build the source documentation.
|
||||||
|
- No runtime dependencies. `pandas` (the optional extra `pandas`) is needed for the
|
||||||
|
second half of the lectures and is installed by the `dev` extra, together with
|
||||||
|
the development tools (`pytest`, `ruff`, `mypy`).
|
||||||
|
|
||||||
|
## Set up
|
||||||
|
|
||||||
|
Clone the repository, then create a local virtual environment `.venv` in its
|
||||||
|
root, upgrade `pip` and install the project with its development tools. The
|
||||||
|
`.venv` folder is ignored by git.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather.git
|
||||||
|
cd 025-weather
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows powershell
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m venv .venv
|
||||||
|
.\.venv\Scripts\Activate.ps1
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
If PowerShell refuses to run the activation script, allow scripts for this
|
||||||
|
window only with `Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned`
|
||||||
|
and activate again.
|
||||||
|
|
||||||
|
### Linux debian
|
||||||
|
|
||||||
|
Debian 13 or newer ships Python 3.13. On older releases install Python 3.13
|
||||||
|
separately.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install python3 python3-venv
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install python@3.13
|
||||||
|
python3.13 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
To install only what the `csv` part needs, leave out the extras:
|
||||||
|
`python -m pip install -e .`. To add `pandas` without the development tools use
|
||||||
|
`python -m pip install -e ".[pandas]"`.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
With the virtual environment active, the program that prints the results of the
|
||||||
|
lectures is added in a later milestone (`python -m weather`); until then the
|
||||||
|
package can be imported:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -c "from weather import constants; print(constants.DATA_FILE)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run the tests
|
||||||
|
|
||||||
|
The tests write only into temporary folders.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
The same checks that continuous integration runs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest
|
||||||
|
python -m ruff check src tests
|
||||||
|
python -m ruff format --check src tests
|
||||||
|
python -m mypy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Continuous integration
|
||||||
|
|
||||||
|
`.gitea/workflows/ci.yml` runs on Gitea Actions on every push and on every pull
|
||||||
|
request. It sets up Python 3.13, upgrades `pip`, installs `.[dev]`, then runs
|
||||||
|
`pytest`, `ruff` (lint and format check) and `mypy`. It does not build the source
|
||||||
|
documentation: run `doxygen Doxyfile` yourself, as the next section shows.
|
||||||
|
|
||||||
|
The workflow lives in `.gitea/workflows` and not in `.github/workflows`, so
|
||||||
|
GitHub does not run it and pushing to a GitHub mirror needs no workflow
|
||||||
|
permission.
|
||||||
|
|
||||||
|
## Build the source documentation
|
||||||
|
|
||||||
|
The source is documented with Doxygen comments and the `Doxyfile` in the root.
|
||||||
|
Install Doxygen (`winget install DimitriVanHeesch.Doxygen` on Windows,
|
||||||
|
`sudo apt install doxygen` on Debian, `brew install doxygen` on macOS), then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
doxygen Doxyfile
|
||||||
|
```
|
||||||
|
|
||||||
|
The HTML is written to `build/doxygen/index.html`. A warning fails the build.
|
||||||
|
|
||||||
|
## Project layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├── .gitea/workflows/ci.yml continuous integration (Gitea Actions)
|
||||||
|
├── docs/ business case, plan, milestones, reviews
|
||||||
|
├── src/weather/ the package
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── constants.py every constant of the lectures
|
||||||
|
├── tests/ pytest tests (conftest.py runs every test in a
|
||||||
|
│ temporary folder)
|
||||||
|
├── Doxyfile source documentation settings
|
||||||
|
├── LICENSE
|
||||||
|
├── pyproject.toml project configuration
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
GNU Affero General Public License v3.0 only. See [LICENSE](LICENSE).
|
||||||
|
|||||||
+59
-59
@@ -1,59 +1,59 @@
|
|||||||
# Artifact Registry
|
# Artifact Registry
|
||||||
|
|
||||||
This project's artifact state. Types, short names and `CrossReference
|
This project's artifact state. Types, short names and `CrossReference
|
||||||
Candidates` come from the framework catalog
|
Candidates` come from the framework catalog
|
||||||
(`framework/registry/artifact-catalog.md`); this file only records where
|
(`framework/registry/artifact-catalog.md`); this file only records where
|
||||||
each document lives in *this* project and the next version to use.
|
each document lives in *this* project and the next version to use.
|
||||||
|
|
||||||
Delete rows for types you don't use. Add a row the first time you create a
|
Delete rows for types you don't use. Add a row the first time you create a
|
||||||
document of a type. `Primary File` may contain a glob (e.g.
|
document of a type. `Primary File` may contain a glob (e.g.
|
||||||
`docs/uc-*/uc.md`); `framework/scripts/find-crossreferences.sh` reads it.
|
`docs/uc-*/uc.md`); `framework/scripts/find-crossreferences.sh` reads it.
|
||||||
|
|
||||||
| Short Name | Artifact Type | Primary File | Next Available Version |
|
| Short Name | Artifact Type | Primary File | Next Available Version |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| BC | Business Case | docs/business-case.md | 002 |
|
| BC | Business Case | docs/business-case.md | 002 |
|
||||||
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
|
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
|
||||||
| PP | Project Plan | docs/project-plan.md | 002 |
|
| PP | Project Plan | docs/project-plan.md | 002 |
|
||||||
| MIL | Milestone / Gateway | docs/milestones/*.md | 006 |
|
| MIL | Milestone / Gateway | docs/milestones/*.md | 006 |
|
||||||
| DICT | Domain Dictionary | docs/dictionary.md | 002 |
|
| DICT | Domain Dictionary | docs/dictionary.md | 002 |
|
||||||
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 009 |
|
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 010 |
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
Set the PO language when the project starts; `project-planning` asks for it
|
Set the PO language when the project starts; `project-planning` asks for it
|
||||||
if it is missing. An artifact of a type marked "Written in the PO language"
|
if it is missing. An artifact of a type marked "Written in the PO language"
|
||||||
exists once, in that language, under its normal name (`business-case.md`); the
|
exists once, in that language, under its normal name (`business-case.md`); the
|
||||||
`artifact` skill ("One file per artifact") has the rule.
|
`artifact` skill ("One file per artifact") has the rule.
|
||||||
|
|
||||||
| Setting | Value |
|
| Setting | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| PO language | en |
|
| PO language | en |
|
||||||
| PO domain | it |
|
| PO domain | it |
|
||||||
| High-level register | IT Executive English |
|
| High-level register | IT Executive English |
|
||||||
| Technical register | IT Professional English |
|
| Technical register | IT Professional English |
|
||||||
|
|
||||||
Every artifact of a type marked "Yes" below states its language and domain in
|
Every artifact of a type marked "Yes" below states its language and domain in
|
||||||
its `Language` and `Domain` Metadata rows; `new-artifact.sh` fills them from
|
its `Language` and `Domain` Metadata rows; `new-artifact.sh` fills them from
|
||||||
`PO language` and `PO domain`. `Language` is a BCP 47 code (`da`, `en`).
|
`PO language` and `PO domain`. `Language` is a BCP 47 code (`da`, `en`).
|
||||||
`Domain` is a value from this list; add a row to introduce a domain, so a
|
`Domain` is a value from this list; add a row to introduce a domain, so a
|
||||||
reviewer can see at once which professional vocabulary a document uses.
|
reviewer can see at once which professional vocabulary a document uses.
|
||||||
|
|
||||||
| Domain | Meaning |
|
| Domain | Meaning |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| it | Software and IT |
|
| it | Software and IT |
|
||||||
| medical | Healthcare and medical devices |
|
| medical | Healthcare and medical devices |
|
||||||
| construction | Construction and civil engineering |
|
| construction | Construction and civil engineering |
|
||||||
|
|
||||||
| Artifact types | Register | Written in the PO language |
|
| Artifact types | Register | Written in the PO language |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| BC, KPI, PP, MIL | IT Executive English | Yes |
|
| BC, KPI, PP, MIL | IT Executive English | Yes |
|
||||||
| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | Yes |
|
| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | Yes |
|
||||||
| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No |
|
| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No |
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- "Next Available Version" is the zero-padded (3-digit) version to use the
|
- "Next Available Version" is the zero-padded (3-digit) version to use the
|
||||||
*next* time a new document of that type is created. Increment it only when
|
*next* time a new document of that type is created. Increment it only when
|
||||||
a brand-new document is created, not when an existing document's
|
a brand-new document is created, not when an existing document's
|
||||||
`## Version History` gets a row.
|
`## Version History` gets a row.
|
||||||
- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types.
|
- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types.
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# RC-009: Code review of MIL-001 (project foundation: constants, package, tests)
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
| Key | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| ID | RC-009 |
|
||||||
|
| CrossReference | [MIL-001], [QC-PY-001] |
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
| Date | Status | Author | Reviewer | Change | Commit |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 2026-10-13 | Proposed | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | [38867cc] |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Artifact Under Review
|
||||||
|
|
||||||
|
- Instance reviewed: the source and tests that [MIL-001] delivers (project foundation: constants, package, tests)
|
||||||
|
- Checklist used: [QC-PY-001]
|
||||||
|
- Scope: full review of the files changed on the branch of the milestone
|
||||||
|
- Language and domain: n/a
|
||||||
|
- Language reviewer: none
|
||||||
|
- Mode: autonomous, not independent (the reviewer is `AI as stand-in for S01`; the assistant also wrote the code; the review of the files was done by the `python-developer` agent of the session)
|
||||||
|
|
||||||
|
## Checklist Results
|
||||||
|
|
||||||
|
| # | Criterion | Status | Evidence/Notes |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | Pass | `PACKAGE_DIR`, `DATA_FILE_NAME` and the other constants are `UPPER_SNAKE`; test functions and the fixture are `snake_case`; the package is `weather`. |
|
||||||
|
| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | Pass | Names state purpose (`FAHRENHEIT_NUMERATOR`, `TEMP_COLUMN_INDEX`, `DATA_FILE`); `temp` is the column name of the lecture's file. |
|
||||||
|
| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | `python -m ruff check src tests` and `python -m ruff format --check src tests` exit 0 with the selection E, F, W, I, N, UP, B, SIM; no `noqa` or ignore. |
|
||||||
|
| 4 | Every function and method signature is type-annotated, including `-> None` | Pass | Every test function and the fixture are annotated `-> None`; the constants are `Final[...]` annotated. |
|
||||||
|
| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | N-A | The changed files contain no exception handling. |
|
||||||
|
| 6 | No mutable default arguments and no shadowed builtins | Pass | No function has a mutable default and no builtin is shadowed. |
|
||||||
|
| 7 | Files, locks and connections are managed with context managers | N-A | The changed files open no file. |
|
||||||
|
| 8 | Public modules, classes and functions have docstrings that say what, not how | Pass | Every constant has a Doxygen `##` comment; the package, `constants.py` and the test and fixture modules have docstrings; `doxygen Doxyfile` ends with 0 warnings. |
|
||||||
|
| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | N-A | The changed files neither log nor print. |
|
||||||
|
| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | Pass | No class exists in these files; the project has no Design Class Diagram (a learning project of this size, recorded in the open issues of [PP-001]). |
|
||||||
|
| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | `tests/test_constants.py` has tests named for the value they fix; none depends on order or the network; the autouse fixture runs each test in a temporary folder. |
|
||||||
|
| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment | Pass | `python -m mypy` runs with `strict = true` and reports no issues; no `Any`. |
|
||||||
|
| 13 | Dependencies are declared and pinned in the project's dependency file, none unused | Pass | `dependencies = []`; the `pandas` extra and the `dev` extra declare `pandas`, `pandas-stubs`, pytest, ruff and mypy with lower bounds; pandas is used by the later milestones. |
|
||||||
|
| 14 | Each piece of knowledge (a business rule, constant, format, validation or query) is defined in one place; code is merged only where it expresses the same knowledge, not where it merely looks alike | Pass | Every value of the lectures is defined once in `constants.py`; `DATA_FILE` is derived from `PACKAGE_DIR` and `DATA_FILE_NAME`; the Fahrenheit factors are separate constants. |
|
||||||
|
| 15 | Imports point inward: business-rule modules import no framework, database, UI or delivery-mechanism code, and there are no import cycles | Pass | `constants.py` imports only `pathlib.Path` and `typing.Final`; no import cycle. |
|
||||||
|
| 16 | SOLID holds: a class or module has one reason to change; new behaviour is added by extension, not by editing a type test in several places; subclasses honour the contract of their base; protocols are narrow; high-level code depends on protocols or abstract types and receives its implementations | Pass | A module of constants has one reason to change; there is no class to extend. |
|
||||||
|
|
||||||
|
## Overall Verdict
|
||||||
|
|
||||||
|
Go — all Mandatory criteria pass or are not applicable. Assessed on 2026-10-13 by the assistant that wrote the code, as stand-in for S01 in autonomous mode. Mode: autonomous, not independent. The pull request is the second look.
|
||||||
|
|
||||||
|
## Action Items
|
||||||
|
|
||||||
|
| Action | Owner | Due |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Challenge this `Go` in the run summary or on the pull request if the stand-in decision is not wanted | S01 | 2026-10-23 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[MIL-001]: ../../milestones/mil-001-project-foundation.md
|
||||||
|
[QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md
|
||||||
|
[PP-001]: ../../project-plan.md
|
||||||
|
[38867cc]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather/commit/38867cccd1f04cba39283b1300bcbdcff68d9724
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=77"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "weather"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Reading CSV weather data with the csv library and pandas, from Udemy's 100 Days of Code (day 25): DataFrame and Series, average, maximum, row selection, create and export."
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
license-files = ["LICENSE"]
|
||||||
|
authors = [{ name = "Jens Tirsvad Nielsen" }]
|
||||||
|
keywords = ["csv", "pandas", "weather", "udemy", "100-days-of-code", "dataframe"]
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Topic :: Education",
|
||||||
|
]
|
||||||
|
# The csv part runs on the standard library only; pandas is an optional extra.
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
pandas = [
|
||||||
|
"pandas>=2.2",
|
||||||
|
]
|
||||||
|
dev = [
|
||||||
|
"pandas>=2.2",
|
||||||
|
"pandas-stubs>=2.2",
|
||||||
|
"pytest>=8",
|
||||||
|
"ruff>=0.6",
|
||||||
|
"mypy>=1.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Repository = "https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/025-weather"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
|
|
||||||
|
[tool.setuptools.package-data]
|
||||||
|
weather = ["weather_data.csv"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
pythonpath = ["src", "tests"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 88
|
||||||
|
target-version = "py313"
|
||||||
|
src = ["src", "tests"]
|
||||||
|
extend-exclude = ["framework", "build"]
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "W", "I", "N", "UP", "B", "SIM"]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.13"
|
||||||
|
strict = true
|
||||||
|
files = ["src", "tests"]
|
||||||
|
mypy_path = ["src", "tests"]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
## @package weather
|
||||||
|
# @brief Reading CSV weather data, from Udemy's 100 Days of Code (day 25).
|
||||||
|
#
|
||||||
|
# @mainpage Weather Data
|
||||||
|
#
|
||||||
|
# The day-25 lectures read the weather data of one week from the file
|
||||||
|
# weather_data.csv: first with Python's built-in csv library, then with the
|
||||||
|
# pandas library, which makes the same work a one-liner. The package keeps the
|
||||||
|
# two ways side by side: csv_reader.py uses the standard library only, and
|
||||||
|
# analysis.py and export.py need the optional pandas extra.
|
||||||
|
#
|
||||||
|
# The constants of the package are in constants.py.
|
||||||
|
|
||||||
|
"""Reading CSV weather data, from Udemy's 100 Days of Code (day 25)."""
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
## @file constants.py
|
||||||
|
# @brief Constants of the weather data project.
|
||||||
|
#
|
||||||
|
# Every value that the day-25 lectures fix lives here, so that no other module
|
||||||
|
# contains a magic string or number: the name of the data file, the names of
|
||||||
|
# its columns, the factors of the Fahrenheit conversion and the name of the
|
||||||
|
# exported file.
|
||||||
|
|
||||||
|
"""Constants of the weather data project, as fixed by the day-25 lectures."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
## @brief Folder of this package; the data file lies next to the modules.
|
||||||
|
PACKAGE_DIR: Final[Path] = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
## @brief Name of the weather data file of the lecture.
|
||||||
|
DATA_FILE_NAME: Final[str] = "weather_data.csv"
|
||||||
|
|
||||||
|
## @brief Full path of the weather data file, independent of the working folder.
|
||||||
|
DATA_FILE: Final[Path] = PACKAGE_DIR / DATA_FILE_NAME
|
||||||
|
|
||||||
|
## @brief Name of the file that the exported table is written to by default.
|
||||||
|
EXPORT_FILE_NAME: Final[str] = "new_data.csv"
|
||||||
|
|
||||||
|
## @brief Encoding of the CSV files that are read and written.
|
||||||
|
FILE_ENCODING: Final[str] = "utf-8"
|
||||||
|
|
||||||
|
## @brief Name of the column that holds the day of the week.
|
||||||
|
COLUMN_DAY: Final[str] = "day"
|
||||||
|
|
||||||
|
## @brief Name of the column that holds the temperature in degrees Celsius.
|
||||||
|
COLUMN_TEMP: Final[str] = "temp"
|
||||||
|
|
||||||
|
## @brief Name of the column that holds the weather condition, such as `Sunny`.
|
||||||
|
COLUMN_CONDITION: Final[str] = "condition"
|
||||||
|
|
||||||
|
## @brief Position of the temperature in a row that `csv.reader` returns.
|
||||||
|
TEMP_COLUMN_INDEX: Final[int] = 1
|
||||||
|
|
||||||
|
## @brief Numerator of the Celsius to Fahrenheit factor: `temp * 9 / 5 + 32`.
|
||||||
|
FAHRENHEIT_NUMERATOR: Final[int] = 9
|
||||||
|
|
||||||
|
## @brief Denominator of the Celsius to Fahrenheit factor: `temp * 9 / 5 + 32`.
|
||||||
|
FAHRENHEIT_DENOMINATOR: Final[int] = 5
|
||||||
|
|
||||||
|
## @brief Offset of the Celsius to Fahrenheit conversion: `temp * 9 / 5 + 32`.
|
||||||
|
FAHRENHEIT_OFFSET: Final[int] = 32
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
"""Shared pytest configuration for the weather data tests."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def isolated_working_directory(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""Run every test in an empty temporary working directory.
|
||||||
|
|
||||||
|
The export has a default file name relative to the working directory. Without
|
||||||
|
this fixture, a test that uses the default would write into the folder pytest
|
||||||
|
was started from.
|
||||||
|
"""
|
||||||
|
monkeypatch.chdir(tmp_path)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
"""Tests of the constants that the day-25 lectures fix."""
|
||||||
|
|
||||||
|
from weather import constants
|
||||||
|
|
||||||
|
|
||||||
|
def test_data_file_is_the_one_of_the_lecture() -> None:
|
||||||
|
assert constants.DATA_FILE_NAME == "weather_data.csv"
|
||||||
|
|
||||||
|
|
||||||
|
def test_data_file_lies_next_to_the_modules_not_in_the_working_folder() -> None:
|
||||||
|
assert constants.DATA_FILE.is_absolute()
|
||||||
|
assert constants.DATA_FILE.parent == constants.PACKAGE_DIR
|
||||||
|
assert constants.DATA_FILE.name == constants.DATA_FILE_NAME
|
||||||
|
|
||||||
|
|
||||||
|
def test_column_names_match_the_header_of_the_lecture() -> None:
|
||||||
|
assert constants.COLUMN_DAY == "day"
|
||||||
|
assert constants.COLUMN_TEMP == "temp"
|
||||||
|
assert constants.COLUMN_CONDITION == "condition"
|
||||||
|
|
||||||
|
|
||||||
|
def test_temperature_is_the_second_value_of_a_row() -> None:
|
||||||
|
assert constants.TEMP_COLUMN_INDEX == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_fahrenheit_factors_give_the_lecture_formula() -> None:
|
||||||
|
celsius = 100
|
||||||
|
fahrenheit = (
|
||||||
|
celsius * constants.FAHRENHEIT_NUMERATOR / constants.FAHRENHEIT_DENOMINATOR
|
||||||
|
+ constants.FAHRENHEIT_OFFSET
|
||||||
|
)
|
||||||
|
assert fahrenheit == 212
|
||||||
|
|
||||||
|
|
||||||
|
def test_exported_file_has_the_name_of_the_lecture() -> None:
|
||||||
|
assert constants.EXPORT_FILE_NAME == "new_data.csv"
|
||||||
Reference in New Issue
Block a user