diff --git a/docs/plan.md b/docs/plan.md index 9b4156d..8b682b7 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -1,41 +1,40 @@ -# Plan: Secret Auction Program (Day 9) - -## Context - -Udemy 100 Days of Code blind-auction CLI. The repository uses a Python `.gitignore`, an AGPL `LICENSE` and a git-ignored `.env` (tokens, never printed or committed). Python 3.13 or newer is required. - -## Working method - -Work on ONE milestone at a time. Each milestone is one branch (`milestone/NN-name`) and one pull request. When a milestone is finished, stop, verify it and review before starting the next. Milestones and issues are also tracked on the Gitea repository (via API, token from `.env`). - -## Milestone 1 - Project scaffold and repo metadata (`milestone/01-scaffold`) - -- [x] Create `src/secret_auction/__init__.py`, `tests/`, `docs/` -- [x] `pyproject.toml`: `requires-python >=3.13`, src layout, no runtime deps, `dev` extra (`pytest`), pytest config, console script -- [x] `.env.example`; `.env` and `.venv/` ignored; add `docs/doxygen/` to `.gitignore` -- [x] `constants.py` -- [x] `Doxyfile` -- [x] Set repository description and topics (`python`, `udemy`, `100-days-of-code`, `blind-auction`, `beginner`) via API -- [x] Create milestones and issues on the host (milestones M1-M4, issues #1-#12) -- [x] Verify: create `.venv`, upgrade pip, `pip install -e ".[dev]"` -- [x] Save plan and milestones in `docs/plan.md` - -## Milestone 2 - Core auction logic and env loader (`milestone/02-core-logic`) - -- [x] `auction.py` (Doxygen comments): `add_bid`, `find_winner` (uses `max(bids, key=bids.get)`, `ValueError` on empty) -- [x] `env.py`: stdlib `.env` loader (no python-dotenv); does not override existing environment variables -- [x] `tests/test_auction.py` (`.env` loader is for private use and intentionally not tested) -- Verify: `pytest` passes - -## Milestone 3 - CLI program (`milestone/03-cli`) - -- [ ] `main.py`: prompt name and numeric bid (re-prompt on invalid), bids dict kept outside the loop, "other bidders? yes/no", clear screen with newlines between bidders, announce the winner -- [ ] `tests/test_main.py` with monkeypatched `input` -- Verify: `pytest`; scripted run `python -m secret_auction.main` with two bidders - -## Milestone 4 - Documentation (`milestone/04-docs`) - -- [ ] `README.md` from the template (Overview, Requirements, Setup incl. creating/activating `.venv` and `python -m pip install --upgrade pip`, Run, Tests, License AGPL, Links) -- [ ] `docs/flowchart.md` (Mermaid plus link to the diagrams.net chart) -- [ ] Generate Doxygen docs if `doxygen` is installed -- Verify: README commands work verbatim; `.env` stays untracked +# Plan: Secret Auction Program (Day 9) + +## Context + +Udemy 100 Days of Code blind-auction CLI. The repository uses a Python `.gitignore`, an AGPL `LICENSE` and a git-ignored `.env` (tokens, never printed or committed). Python 3.13 or newer is required. + +## Working method + +Work on ONE milestone at a time. Each milestone is one branch (`milestone/NN-name`) and one pull request. When a milestone is finished, stop, verify it and review before starting the next. Milestones and issues are also tracked on the Gitea repository (via API, token from `.env`). + +## Milestone 1 - Project scaffold and repo metadata (`milestone/01-scaffold`) + +- [x] Create `src/secret_auction/__init__.py`, `tests/`, `docs/` +- [x] `pyproject.toml`: `requires-python >=3.13`, src layout, no runtime deps, `dev` extra (`pytest`), pytest config, console script +- [x] `.env.example`; `.env` and `.venv/` ignored; add `docs/doxygen/` to `.gitignore` +- [x] `constants.py` +- [x] `Doxyfile` +- [x] Set repository description and topics (`python`, `udemy`, `100-days-of-code`, `blind-auction`, `beginner`) via API +- [x] Create milestones and issues on the host (milestones M1-M4, issues #1-#12) +- [x] Verify: create `.venv`, upgrade pip, `pip install -e ".[dev]"` +- [x] Save plan and milestones in `docs/plan.md` + +## Milestone 2 - Core auction logic (`milestone/02-core-logic`) + +- [x] `auction.py` (Doxygen comments): `add_bid`, `find_winner` (uses `max(bids, key=bids.get)`, `ValueError` on empty) +- [x] `tests/test_auction.py` +- Verify: `pytest` passes + +## Milestone 3 - CLI program (`milestone/03-cli`) + +- [ ] `main.py`: prompt name and numeric bid (re-prompt on invalid), bids dict kept outside the loop, "other bidders? yes/no", clear screen with newlines between bidders, announce the winner +- [ ] `tests/test_main.py` with monkeypatched `input` +- Verify: `pytest`; scripted run `python -m secret_auction.main` with two bidders + +## Milestone 4 - Documentation (`milestone/04-docs`) + +- [ ] `README.md` from the template (Overview, Requirements, Setup incl. creating/activating `.venv` and `python -m pip install --upgrade pip`, Run, Tests, License AGPL, Links) +- [ ] `docs/flowchart.md` (Mermaid plus link to the diagrams.net chart) +- [ ] Generate Doxygen docs if `doxygen` is installed +- Verify: README commands work verbatim; `.env` stays untracked diff --git a/src/secret_auction/constants.py b/src/secret_auction/constants.py index f199f24..437357a 100644 --- a/src/secret_auction/constants.py +++ b/src/secret_auction/constants.py @@ -1,39 +1,33 @@ -"""! @file constants.py -@brief Constants used by the Secret Auction program. -""" - -## Number of blank lines printed to hide previous bidders' entries. -CLEAR_SCREEN_LINES = 100 - -## Answers accepted as "yes" when asking for more bidders. -YES_ANSWERS = ("yes", "y") - -## Answers accepted as "no" when asking for more bidders. -NO_ANSWERS = ("no", "n") - -## Path of the file holding environment variables (tokens). -ENV_FILE = ".env" - -## Name of the environment variable holding the Gitea token. -GITEA_TOKEN_ENV_VAR = "GITEA_TOKEN" - -## Prompt asking for the bidder's name. -PROMPT_NAME = "What is your name? " - -## Prompt asking for the bid amount. -PROMPT_BID = "What is your bid? $" - -## Prompt asking whether more bidders exist. -PROMPT_MORE_BIDDERS = "Are there any other bidders? Type 'yes' or 'no'. " - -## Message shown when the bid is not a valid number. -MSG_INVALID_BID = "Please enter a valid, non-negative number." - -## Message shown when the yes/no answer is not understood. -MSG_INVALID_ANSWER = "Please type 'yes' or 'no'." - -## Template for the winner announcement. -MSG_WINNER = "The winner is {name} with a bid of ${amount:.2f}." - -## Welcome banner. -MSG_WELCOME = "Welcome to the secret auction program." +"""! @file constants.py +@brief Constants used by the Secret Auction program. +""" + +## Number of blank lines printed to hide previous bidders' entries. +CLEAR_SCREEN_LINES = 100 + +## Answers accepted as "yes" when asking for more bidders. +YES_ANSWERS = ("yes", "y") + +## Answers accepted as "no" when asking for more bidders. +NO_ANSWERS = ("no", "n") + +## Prompt asking for the bidder's name. +PROMPT_NAME = "What is your name? " + +## Prompt asking for the bid amount. +PROMPT_BID = "What is your bid? $" + +## Prompt asking whether more bidders exist. +PROMPT_MORE_BIDDERS = "Are there any other bidders? Type 'yes' or 'no'. " + +## Message shown when the bid is not a valid number. +MSG_INVALID_BID = "Please enter a valid, non-negative number." + +## Message shown when the yes/no answer is not understood. +MSG_INVALID_ANSWER = "Please type 'yes' or 'no'." + +## Template for the winner announcement. +MSG_WINNER = "The winner is {name} with a bid of ${amount:.2f}." + +## Welcome banner. +MSG_WELCOME = "Welcome to the secret auction program." diff --git a/src/secret_auction/env.py b/src/secret_auction/env.py deleted file mode 100644 index e88c935..0000000 --- a/src/secret_auction/env.py +++ /dev/null @@ -1,38 +0,0 @@ -"""! @file env.py -@brief Minimal `.env` file loader using only the standard library. -""" - -import os -from pathlib import Path - - -def parse_env(text: str) -> dict[str, str]: - """! @brief Parse `KEY=VALUE` lines. - @param text Contents of a `.env` file. Blank lines and `#` comments are ignored. - @return Dictionary of parsed variables; surrounding quotes are stripped. - """ - values: dict[str, str] = {} - for line in text.splitlines(): - line = line.strip() - if not line or line.startswith("#") or "=" not in line: - continue - key, value = line.split("=", 1) - value = value.strip() - if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'": - value = value[1:-1] - values[key.strip()] = value - return values - - -def load_env(path: str | Path) -> dict[str, str]: - """! @brief Load a `.env` file into `os.environ` without overriding existing variables. - @param path Path to the `.env` file; a missing file is not an error. - @return Dictionary of the variables read from the file. - """ - file = Path(path) - if not file.is_file(): - return {} - values = parse_env(file.read_text(encoding="utf-8")) - for key, value in values.items(): - os.environ.setdefault(key, value) - return values