Organize documentation into topic folders (Claude, Python, Zscaler, Project template)
This commit is contained in:
@@ -0,0 +1,83 @@
|
|||||||
|
# AI Agents - Project Rules
|
||||||
|
|
||||||
|
**Document Version:** v4 (independent, incremented on structural changes)
|
||||||
|
|
||||||
|
Rules and instructions for AI assistants (Claude Code, Cursor, Copilot, etc.)
|
||||||
|
|
||||||
|
## First-time setup
|
||||||
|
|
||||||
|
- **On first read of this file, immediately read all other `.md` files in the project root** (e.g. `PROJECT.md`, `CHANGELOG.md`, `DESIGN_DOCUMENT.md`) to get full project context before starting any task.
|
||||||
|
|
||||||
|
## Language
|
||||||
|
|
||||||
|
- **Always write all documentation in English**
|
||||||
|
|
||||||
|
## Dependency Management
|
||||||
|
|
||||||
|
- **Always use `poetry add`** to add dependencies, **never edit `pyproject.toml` directly**
|
||||||
|
```bash
|
||||||
|
poetry add requests
|
||||||
|
poetry add --group dev pytest
|
||||||
|
```
|
||||||
|
- Use `poetry remove` to remove dependencies — **never edit `pyproject.toml` manually**
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
- Entry points are in the project root (named after project or by purpose: `project_name.py`, `cli.py`, `gui.py`, `server.py`)
|
||||||
|
- A project can have multiple entry points
|
||||||
|
- All modules belong in the `src/` folder
|
||||||
|
- Tests belong in the `tests/` folder
|
||||||
|
- Virtual environment is in `.venv/` (do not copy, do not generate)
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
- Always use type annotations
|
||||||
|
- Follow PEP8 and format with Ruff (88 characters per line)
|
||||||
|
- Before commit run `poetry run ruff check` and `poetry run mypy`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- **Use pytest exclusively** - never use the `unittest` module
|
||||||
|
- No `unittest.TestCase` classes, no `self.assert*` methods
|
||||||
|
- Use plain `assert` statements and pytest fixtures
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
- Use `poetry run` to run scripts:
|
||||||
|
```bash
|
||||||
|
poetry run python project_name.py
|
||||||
|
poetry run pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
- Use **loguru** for logging - never use `print()` for debugging
|
||||||
|
- Never log secrets, passwords, tokens, or API keys
|
||||||
|
|
||||||
|
## Environment and Secrets
|
||||||
|
|
||||||
|
- Store secrets in `.env` file with `ENV_DEBUG=true/false` variable
|
||||||
|
- Load secrets using `python-dotenv` and `os.getenv()`
|
||||||
|
- **Never commit `.env` file**
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
- `.gitignore` should contain: `.venv/`, `__pycache__/`, `*.pyc`, `.mypy_cache/`, `.env`
|
||||||
|
- Do not commit `poetry.lock` only if it's a library (for applications, commit it)
|
||||||
|
- **Never commit this documentation** (`DESIGN_DOCUMENT.md`, `AGENTS.md`, `.claudeignore`)
|
||||||
|
- `PROJECT.md` **should be committed** - it's project-specific
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
- **Always ask user before bumping version** - never increase version automatically
|
||||||
|
- **Keep `CHANGELOG.md` updated** - document all significant changes as they are made
|
||||||
|
- Update `CHANGELOG.md` with changes before version bump
|
||||||
|
- Version is defined in `pyproject.toml` under `[project]` section
|
||||||
|
- Follow semantic versioning (MAJOR.MINOR.PATCH)
|
||||||
|
|
||||||
|
## Task Management
|
||||||
|
|
||||||
|
- **When completing tasks, mark them as done** - if you finish any task with a checkbox anywhere in project documentation, check it off as completed `[ ]` → `[x]`
|
||||||
|
- **Track all work** - this applies to tasks in `PROJECT.md` (TODO section, Development Roadmap, any checklists) and other documentation
|
||||||
|
- **Update documentation** - when completing changes, update relevant sections in `PROJECT.md`, `CHANGELOG.md`, and architecture diagrams
|
||||||
|
- **Keep task lists current** - completed items with `[x]` stay visible to show progress history
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
## First-time setup
|
||||||
|
|
||||||
|
**At the start of every new session, read all of the following files before doing anything else:**
|
||||||
|
|
||||||
|
- `CLAUDE.md` (this file)
|
||||||
|
- `AGENTS.md`
|
||||||
|
- `PROJECT.md`
|
||||||
|
- `CHANGELOG.md`
|
||||||
|
- `DESIGN_DOCUMENT.md`
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
name: changelog
|
||||||
|
description: Updates changelog in project
|
||||||
|
---
|
||||||
|
|
||||||
|
Take known info and make changes to CHANGELOG.md to make them up-to-date.
|
||||||
|
|
||||||
|
Ignore changes to: `docs/`, `CONTEXT.md`, `Documentation_AI.md`, `Documentation_human.md` — documentation and context files are not logged in the changelog.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
name: check
|
||||||
|
description: Check for failed tests
|
||||||
|
---
|
||||||
|
Try running tests and investigate failed results
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: commit
|
||||||
|
description: Updates changelog and proposes a concise verbal git commit message
|
||||||
|
---
|
||||||
|
|
||||||
|
1. Run the changelog skill: read git log and staged changes, then update CHANGELOG.md accordingly (same as /changelog).
|
||||||
|
|
||||||
|
2. Propose a single-line verbal commit message:
|
||||||
|
- Concise, verbal style — e.g. "Add X", "Fix Y", "Refactor Z"
|
||||||
|
- NOT filename-style — never "update_something.py" or "changes to foo"
|
||||||
|
- In English
|
||||||
|
- Do NOT run git commit — only propose the message and wait for user confirmation.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: context
|
||||||
|
description: Compress docs/ into CONTEXT.md at project root — minimum tokens, lossless
|
||||||
|
---
|
||||||
|
|
||||||
|
Read every `.md` file in `docs/`. Generate `CONTEXT.md` at the project root.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Lossless compression of `docs/` into a single file optimised for LLM context window use.
|
||||||
|
Every public function, class, constant, invariant, file path, and edge case from `docs/` must be present.
|
||||||
|
Target: CONTEXT.md ≈ 40–50% byte size of combined `docs/` input.
|
||||||
|
|
||||||
|
## Format rules
|
||||||
|
|
||||||
|
- Symbol legend at top: `Legend: → returns * required ? optional @ located at`
|
||||||
|
- Tables over paragraphs — always
|
||||||
|
- Compact signature notation: `f(a:T, b:T) → T @ src/foo.py`
|
||||||
|
- Pipe unions: `A | B | C`
|
||||||
|
- Section references: §0, §1, … — state each fact once, reference elsewhere
|
||||||
|
- Inline conditions: `if X: Y; else: Z`
|
||||||
|
- No prose, no motivation, no examples that just illustrate a stated rule
|
||||||
|
- No headings deeper than 3 levels
|
||||||
|
|
||||||
|
## Sections
|
||||||
|
|
||||||
|
- §0 Module map — path + 1-line role
|
||||||
|
- §1 Data types — every class: fields, types, invariants
|
||||||
|
- §2 Public API — every public function/method: compact signature + constraints
|
||||||
|
- §3 Constants — table: name, value, file, where used
|
||||||
|
- §4 Workflows — compact arrow notation
|
||||||
|
- §5 Configuration — env vars, defaults
|
||||||
|
- §6 Edge cases & invariants — one row per rule, exhaustive
|
||||||
|
- §7 Build / run / test — exact commands
|
||||||
|
|
||||||
|
## When to skip
|
||||||
|
|
||||||
|
If `CONTEXT.md` exists and no file in `docs/` is newer than it: print `context: in sync` and stop.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
name: documentation
|
||||||
|
description: Updates documentation in project
|
||||||
|
---
|
||||||
|
Create or update documentation of project in docs/ folder. Documentation should contain description of each part of program and documentation of each function. Separate documents for each module. Write in markdown format.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
Document all notable changes to the project, grouped by version and release date.
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
Each version entry uses these sections (include only those that apply):
|
||||||
|
|
||||||
|
- **Added** — new features
|
||||||
|
- **Changed** — changes to existing functionality
|
||||||
|
- **Fixed** — bug fixes
|
||||||
|
- **Removed** — removed features
|
||||||
|
- **Dependencies** — added, updated, or removed dependencies
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Follow semantic versioning: `MAJOR.MINOR.PATCH`
|
||||||
|
- Newest version goes at the top
|
||||||
|
- Always update this file before bumping the version in `pyproject.toml`
|
||||||
|
- Document changes as they are made, not all at once at release time
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# PROJECT.md
|
||||||
|
|
||||||
|
This file is project-specific. Only include information directly related to the concrete project — goals, current state, architecture decisions, known issues, and tasks.
|
||||||
|
|
||||||
|
Do not put general guidelines or reusable documentation here.
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
# Python Development Guidelines
|
||||||
|
|
||||||
|
**Document Version:** v8
|
||||||
|
|
||||||
|
> **Note on Versioning:**
|
||||||
|
> - This document version is independent — reused across projects
|
||||||
|
> - **Project version** source of truth: `pyproject.toml`
|
||||||
|
> - Version propagates: `pyproject.toml` → `constants.py` → code
|
||||||
|
> - `CHANGELOG.md` uses project version from `pyproject.toml`
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- **README.md** — Project overview, tool descriptions, build instructions
|
||||||
|
- **AGENTS.md** — Rules for AI assistants
|
||||||
|
- **PROJECT.md** — Project goals and current state
|
||||||
|
- **CHANGELOG.md** — Version history
|
||||||
|
|
||||||
|
### Documentation Organization
|
||||||
|
|
||||||
|
All detailed documentation of features and systems belongs in the `docs/` folder, not in the project root.
|
||||||
|
|
||||||
|
The root directory contains only the core documents: `DESIGN_DOCUMENT.md`, `AGENTS.md`, `PROJECT.md`, `CHANGELOG.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Code Style
|
||||||
|
|
||||||
|
- **PEP8** with 150-character lines (Ruff)
|
||||||
|
- **4 spaces** indentation
|
||||||
|
- **snake_case** functions/variables, **PascalCase** classes, **SCREAMING_SNAKE_CASE** constants
|
||||||
|
- **Type hints** required on all functions
|
||||||
|
- **Import order**: stdlib → third-party → local
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. SOLID Principles
|
||||||
|
|
||||||
|
- **SRP** — One class = one responsibility
|
||||||
|
- **OCP** — Open for extension, closed for modification
|
||||||
|
- **LSP** — Subclasses substitutable for parents
|
||||||
|
- **ISP** — Small interfaces over large ones
|
||||||
|
- **DIP** — Depend on abstractions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Dependency Injection
|
||||||
|
|
||||||
|
Pass dependencies via constructor. Never instantiate dependencies inside a class.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Protocols Over Inheritance
|
||||||
|
|
||||||
|
Prefer `typing.Protocol` and composition over class inheritance.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Data Classes
|
||||||
|
|
||||||
|
Use `@dataclass` for internal data structures, `pydantic.BaseModel` for data that requires validation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Logging and Console Output
|
||||||
|
|
||||||
|
### Logging — loguru
|
||||||
|
|
||||||
|
Use **loguru** for all internal logging. Never log secrets, passwords, tokens, or API keys.
|
||||||
|
|
||||||
|
#### Log sinks
|
||||||
|
|
||||||
|
| Sink | Level | Format |
|
||||||
|
|------|-------|--------|
|
||||||
|
| File `logs/{AppName}_{time}.log` | `DEBUG` | full (timestamp + level + message) |
|
||||||
|
| stdout | `INFO` | full |
|
||||||
|
|
||||||
|
File sink retains **max 10 log files** (`retention=10`). No rotation by size — each run creates a new file via `{time}` in the filename.
|
||||||
|
|
||||||
|
The `DEBUG` sink is only active when `constants.DEBUG` is `True` (controlled by `ENV_DEBUG=true` in `.env`).
|
||||||
|
|
||||||
|
Additional sinks (e.g. GUI log panels) may be added per project as needed.
|
||||||
|
|
||||||
|
#### Log levels
|
||||||
|
|
||||||
|
| Level | When to use |
|
||||||
|
|-------|-------------|
|
||||||
|
| `DEBUG` | Per-item detail: individual file reads/writes, cache hits, per-row operations |
|
||||||
|
| `INFO` | User-visible milestones: file loaded, process started/finished, file saved |
|
||||||
|
| `WARNING` | Recoverable issues: fallback used, unexpected but non-fatal state |
|
||||||
|
| `ERROR` | Failures the user must know about: missing file, failed save, missing config — operation cannot continue |
|
||||||
|
|
||||||
|
### Console output — print()
|
||||||
|
|
||||||
|
`print()` is **allowed** for direct user-facing console communication: input prompts, progress lines, and result summaries. This applies to console tools that interact with the user through stdin/stdout.
|
||||||
|
|
||||||
|
`print()` is **not** a substitute for loguru. It must not be used for debugging or internal event tracking.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Environment and Secrets
|
||||||
|
|
||||||
|
- Secrets in `.env` file; controlled by `ENV_DEBUG=true/false`
|
||||||
|
- Load via `python-dotenv` and `os.getenv()`
|
||||||
|
- Never commit `.env`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Error Handling
|
||||||
|
|
||||||
|
Define specific exception types. Use a fail-fast approach — surface errors early rather than silently continuing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Testing
|
||||||
|
|
||||||
|
- **pytest** only — no `unittest`, no `TestCase` classes, no `self.assert*`
|
||||||
|
- Arrange-Act-Assert pattern
|
||||||
|
- Test naming: `test_<action>_<context>`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Tooling
|
||||||
|
|
||||||
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| **Ruff** | Formatting and linting |
|
||||||
|
| **mypy** | Static type checking |
|
||||||
|
| **pytest** | Testing |
|
||||||
|
|
||||||
|
Run before every commit:
|
||||||
|
```bash
|
||||||
|
poetry run ruff check
|
||||||
|
poetry run mypy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Poetry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry install # Install all dependencies
|
||||||
|
poetry add <pkg> # Add runtime dependency
|
||||||
|
poetry add --group dev <pkg> # Add dev dependency
|
||||||
|
poetry remove <pkg> # Remove dependency
|
||||||
|
poetry run <cmd> # Run command in virtualenv
|
||||||
|
```
|
||||||
|
|
||||||
|
Never edit `pyproject.toml` directly to add or remove dependencies.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
project/
|
||||||
|
├── entry_point.py # Entry point(s) — named by purpose or tool name
|
||||||
|
├── src/ # All application modules
|
||||||
|
├── tests/ # Tests
|
||||||
|
├── docs/ # Detailed documentation
|
||||||
|
├── .venv/ # Virtual environment (managed by Poetry)
|
||||||
|
└── pyproject.toml # Project config and dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
A project may have multiple entry points (e.g. `cli.py`, `gui.py`, or per-tool scripts).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Distribution and Deployment
|
||||||
|
|
||||||
|
When a project is distributed as a standalone executable (no Python required on target machine):
|
||||||
|
|
||||||
|
- Use **PyInstaller** to compile each entry point into a single `.exe`
|
||||||
|
- Each tool has its own `.spec` file in the project root
|
||||||
|
- All console tools must use `console=True` in the `.spec` — tools rely on `input()` and `print()` for user interaction
|
||||||
|
- Compiled executables are stored in `dist/` and **committed to the repository** — the repository serves as the distribution channel for internal teams
|
||||||
|
- `.gitignore` must **not** exclude `dist/` in projects that use this deployment model
|
||||||
|
|
||||||
|
Build command:
|
||||||
|
```bash
|
||||||
|
poetry run pyinstaller ToolName.spec
|
||||||
|
```
|
||||||
|
|
||||||
|
> This section applies only to projects that produce standalone executables. Skip for libraries or web services.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Versioning
|
||||||
|
|
||||||
|
- Follow **semantic versioning**: `MAJOR.MINOR.PATCH`
|
||||||
|
- Version is defined in `pyproject.toml` under `[project]`
|
||||||
|
- Always ask before bumping the version — never increment automatically
|
||||||
|
- Update `CHANGELOG.md` before bumping the version
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Documentation and Task Management
|
||||||
|
|
||||||
|
- Keep `PROJECT.md` and `CHANGELOG.md` up to date when making changes
|
||||||
|
- Document architectural changes in this file or in `docs/`
|
||||||
|
|
||||||
|
### Task notation
|
||||||
|
|
||||||
|
Tasks are written as single-line comments directly in code, or in `PROJECT.md` for cross-cutting concerns:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# TODO: one-liner description of a task to be done
|
||||||
|
# FIXME: one-liner description of a known bug to be fixed
|
||||||
|
```
|
||||||
|
|
||||||
|
No other task format is used — no checkboxes, no numbered lists in documentation.
|
||||||
|
|
||||||
|
If a `# TODO:` comment already exists at a specific location in code, do not repeat it in `PROJECT.md`.
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
# Python Library Development Guidelines
|
||||||
|
|
||||||
|
**Document Version:** v1
|
||||||
|
|
||||||
|
> **Note on Versioning:**
|
||||||
|
> - This document version is independent — reused across projects
|
||||||
|
> - **Project version** source of truth: `pyproject.toml` under `[project]`
|
||||||
|
> - `CHANGELOG.md` uses project version from `pyproject.toml`
|
||||||
|
|
||||||
|
## Related Documents
|
||||||
|
|
||||||
|
- **README.md** — Project overview, public API description, installation and usage examples
|
||||||
|
- **AGENTS.md** — Rules for AI assistants
|
||||||
|
- **PROJECT.md** — Project goals and current state
|
||||||
|
- **CHANGELOG.md** — Version history
|
||||||
|
|
||||||
|
### Documentation Organization
|
||||||
|
|
||||||
|
All detailed documentation of features and systems belongs in the `docs/` folder, not in the project root.
|
||||||
|
|
||||||
|
The root directory contains only the core documents: `DESIGN_DOCUMENT_MODULE.md`, `AGENTS.md`, `PROJECT.md`, `CHANGELOG.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Code Style
|
||||||
|
|
||||||
|
- **PEP8** with 150-character lines (Ruff)
|
||||||
|
- **4 spaces** indentation
|
||||||
|
- **snake_case** functions/variables, **PascalCase** classes, **SCREAMING_SNAKE_CASE** constants
|
||||||
|
- **Type hints** required on all functions
|
||||||
|
- **Import order**: stdlib → third-party → local
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. SOLID Principles
|
||||||
|
|
||||||
|
- **SRP** — One class = one responsibility
|
||||||
|
- **OCP** — Open for extension, closed for modification
|
||||||
|
- **LSP** — Subclasses substitutable for parents
|
||||||
|
- **ISP** — Small interfaces over large ones
|
||||||
|
- **DIP** — Depend on abstractions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Dependency Injection
|
||||||
|
|
||||||
|
Pass dependencies via constructor. Never instantiate dependencies inside a class.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Protocols Over Inheritance
|
||||||
|
|
||||||
|
Prefer `typing.Protocol` and composition over class inheritance.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Data Classes
|
||||||
|
|
||||||
|
Use `@dataclass` for internal data structures, `pydantic.BaseModel` for data that requires validation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Logging
|
||||||
|
|
||||||
|
This is a library. Libraries must **never configure logging sinks** — that is the responsibility of the consuming application.
|
||||||
|
|
||||||
|
### Usage in library code
|
||||||
|
|
||||||
|
```python
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
def some_function() -> None:
|
||||||
|
logger.debug("Detail message")
|
||||||
|
logger.info("Milestone message")
|
||||||
|
```
|
||||||
|
|
||||||
|
Always use the module-level `logger` from loguru directly. Never call `logger.add()` or `logger.remove()` inside library code.
|
||||||
|
|
||||||
|
### Behavior for consumers
|
||||||
|
|
||||||
|
Loguru has a default sink to `stderr` enabled. Consumers who also use loguru get library logs automatically in their configured sinks. Consumers who want to suppress or filter library logs use the package name as the identifier:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
logger.disable("<package_name>") # suppress all
|
||||||
|
logger.add(sys.stderr, filter={"<package_name>": "WARNING"}) # WARNING and above only
|
||||||
|
logger.enable("<package_name>") # re-enable
|
||||||
|
```
|
||||||
|
|
||||||
|
This must be documented in `README.md`.
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
- Never call `logger.add()` inside library code — no file sinks, no stdout sinks
|
||||||
|
- Never call `logger.remove()` inside library code
|
||||||
|
- Never log secrets, passwords, tokens, or API keys
|
||||||
|
- Never use `print()` inside library code — not for debugging, not for output
|
||||||
|
|
||||||
|
#### Log levels
|
||||||
|
|
||||||
|
| Level | When to use |
|
||||||
|
|-------|-------------|
|
||||||
|
| `DEBUG` | Per-item detail: individual operations, cache hits, internal state |
|
||||||
|
| `INFO` | Significant milestones visible to the consuming application |
|
||||||
|
| `WARNING` | Recoverable issues: fallback used, unexpected but non-fatal state |
|
||||||
|
| `ERROR` | Failures the caller must know about — operation cannot continue |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Environment and Secrets
|
||||||
|
|
||||||
|
- Libraries do not use `.env` files or `python-dotenv`
|
||||||
|
- Configuration is passed by the caller via arguments or constructor parameters
|
||||||
|
- Never read `os.getenv()` inside library code unless explicitly documented as a supported configuration mechanism
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Error Handling
|
||||||
|
|
||||||
|
Define specific exception types in `src/<package>/exceptions.py`. Use a fail-fast approach — surface errors early rather than silently continuing. All public exceptions must be exported from the top-level `__init__.py`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Testing
|
||||||
|
|
||||||
|
- **pytest** only — no `unittest`, no `TestCase` classes, no `self.assert*`
|
||||||
|
- Arrange-Act-Assert pattern
|
||||||
|
- Test naming: `test_<action>_<context>`
|
||||||
|
- Do not commit `poetry.lock` — this is a library; consumers pin their own dependencies
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Tooling
|
||||||
|
|
||||||
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| **Ruff** | Formatting and linting |
|
||||||
|
| **mypy** | Static type checking |
|
||||||
|
| **pytest** | Testing |
|
||||||
|
|
||||||
|
Run before every commit:
|
||||||
|
```bash
|
||||||
|
poetry run ruff check
|
||||||
|
poetry run mypy
|
||||||
|
poetry run pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Poetry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry install # Install all dependencies
|
||||||
|
poetry add <pkg> # Add runtime dependency
|
||||||
|
poetry add --group dev <pkg> # Add dev dependency
|
||||||
|
poetry remove <pkg> # Remove dependency
|
||||||
|
poetry run <cmd> # Run command in virtualenv
|
||||||
|
poetry build # Build sdist and wheel into dist/
|
||||||
|
poetry publish # Publish to PyPI
|
||||||
|
```
|
||||||
|
|
||||||
|
Never edit `pyproject.toml` directly to add or remove dependencies.
|
||||||
|
|
||||||
|
### pyproject.toml configuration for a library
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[project]
|
||||||
|
name = "your-library"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Short description"
|
||||||
|
requires-python = ">=3.x"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[tool.poetry]
|
||||||
|
packages = [{include = "your_library", from = "src"}]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
```
|
||||||
|
|
||||||
|
Do **not** add `[tool.poetry.scripts]` — libraries have no entry points.
|
||||||
|
|
||||||
|
### poetry.lock
|
||||||
|
|
||||||
|
Do **not** commit `poetry.lock` for libraries. It is only committed for applications. Add `poetry.lock` to `.gitignore`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
project/
|
||||||
|
├── src/
|
||||||
|
│ └── your_library/
|
||||||
|
│ ├── __init__.py # Public API — export everything the caller needs
|
||||||
|
│ ├── exceptions.py # All public exception types
|
||||||
|
│ └── ... # Internal modules
|
||||||
|
├── tests/ # Tests
|
||||||
|
├── docs/ # Detailed documentation
|
||||||
|
├── .venv/ # Virtual environment (managed by Poetry)
|
||||||
|
└── pyproject.toml # Project config and dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
- No entry point scripts in the project root — this is a library, not an application
|
||||||
|
- `__init__.py` defines the public API; callers import from the top-level package only
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Public API
|
||||||
|
|
||||||
|
- Everything intended for external use must be exported from `src/<package>/__init__.py`
|
||||||
|
- Use `__all__` to explicitly declare the public surface
|
||||||
|
- Internal modules are prefixed with `_` or kept unexported
|
||||||
|
- Public API must be stable across patch versions; breaking changes require a major version bump
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Distribution
|
||||||
|
|
||||||
|
Build and publish with Poetry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
poetry build # Creates dist/*.tar.gz and dist/*.whl
|
||||||
|
poetry publish # Publishes to PyPI (requires credentials)
|
||||||
|
```
|
||||||
|
|
||||||
|
`dist/` is **not committed** to the repository — add it to `.gitignore`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Versioning
|
||||||
|
|
||||||
|
- Follow **semantic versioning**: `MAJOR.MINOR.PATCH`
|
||||||
|
- Version is defined in `pyproject.toml` under `[project]`
|
||||||
|
- Always ask before bumping the version — never increment automatically
|
||||||
|
- Update `CHANGELOG.md` before bumping the version
|
||||||
|
- Breaking changes to the public API require a major version bump
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Documentation and Task Management
|
||||||
|
|
||||||
|
- Keep `PROJECT.md` and `CHANGELOG.md` up to date when making changes
|
||||||
|
- `README.md` must contain installation instructions and usage examples for the public API
|
||||||
|
- Document architectural changes in this file or in `docs/`
|
||||||
|
|
||||||
|
### Task notation
|
||||||
|
|
||||||
|
Tasks are written as single-line comments directly in code, or in `PROJECT.md` for cross-cutting concerns:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# TODO: one-liner description of a task to be done
|
||||||
|
# FIXME: one-liner description of a known bug to be fixed
|
||||||
|
```
|
||||||
|
|
||||||
|
No other task format is used — no checkboxes, no numbered lists in documentation.
|
||||||
|
|
||||||
|
If a `# TODO:` comment already exists at a specific location in code, do not repeat it in `PROJECT.md`.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Template Generation Request
|
||||||
|
|
||||||
|
Create a `template/` folder in project root with reusable files for new Python projects.
|
||||||
|
|
||||||
|
## Required Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
template/
|
||||||
|
├── .env # Environment variables (sample)
|
||||||
|
├── .gitignore # Git ignore rules
|
||||||
|
├── AGENTS.md # AI assistant rules
|
||||||
|
├── CHANGELOG.md # Changelog template
|
||||||
|
├── DESIGN_DOCUMENT.md # Development guidelines
|
||||||
|
├── PROJECT.md # Project documentation template
|
||||||
|
├── main.py # Entry point with loguru
|
||||||
|
├── pyproject.toml # Poetry config (ruff, mypy, pytest)
|
||||||
|
├── src/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── core/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── _version.py # Version fallback for PyInstaller
|
||||||
|
│ └── constants.py # Version extraction from toml + DEBUG mode
|
||||||
|
└── tests/
|
||||||
|
├── __init__.py
|
||||||
|
└── test_constants.py # Basic test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Version extraction** from `pyproject.toml` with `_version.py` fallback for PyInstaller builds
|
||||||
|
- **DEBUG mode** via `ENV_DEBUG=true` in `.env` (adds " DEV" suffix to version)
|
||||||
|
- **loguru** for logging (never print)
|
||||||
|
- **Poetry** for dependency management
|
||||||
|
- **pytest** for testing (no unittest)
|
||||||
|
- **ruff + mypy** for linting and type checking
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- No `.example` suffixes - the folder itself is the separator
|
||||||
|
- Generic/reusable format
|
||||||
|
- Keep files simple and minimal
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from src.constants import VERSION
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
print("=" * 50)
|
||||||
|
print("PREBUILD CONFIGURATION")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# Check if running in virtual environment
|
||||||
|
project_root = Path(__file__).parent
|
||||||
|
expected_venv_path = project_root / ".venv"
|
||||||
|
current_executable = Path(sys.executable)
|
||||||
|
|
||||||
|
print(f"\nPython executable: {sys.executable}")
|
||||||
|
|
||||||
|
is_correct_venv = False
|
||||||
|
try:
|
||||||
|
current_executable.relative_to(expected_venv_path)
|
||||||
|
is_correct_venv = True
|
||||||
|
except ValueError:
|
||||||
|
is_correct_venv = False
|
||||||
|
|
||||||
|
if is_correct_venv:
|
||||||
|
print("✓ Correct environment selected for building")
|
||||||
|
else:
|
||||||
|
print("✗ Wrong environment selected")
|
||||||
|
print(f" Expected: {expected_venv_path}")
|
||||||
|
print(f" Current: {current_executable.parent.parent}")
|
||||||
|
|
||||||
|
print(f"✓ Version: {VERSION}")
|
||||||
|
|
||||||
|
env_debug = os.getenv("ENV_DEBUG", "false").lower() == "true"
|
||||||
|
console_mode = env_debug
|
||||||
|
default_spec = Path(__file__).parent.name + ".spec"
|
||||||
|
spec_filename = os.getenv("ENV_BUILD_SPEC", default_spec)
|
||||||
|
|
||||||
|
print(f"\n{'-' * 50}")
|
||||||
|
print("BUILD SETTINGS")
|
||||||
|
print(f"{'-' * 50}")
|
||||||
|
print(f"ENV_DEBUG: {env_debug}")
|
||||||
|
print(f"Console mode: {console_mode}")
|
||||||
|
print(f"Spec file: {spec_filename}")
|
||||||
|
|
||||||
|
spec_path = Path(__file__).parent / spec_filename
|
||||||
|
if spec_path.exists():
|
||||||
|
with open(spec_path, "r", encoding="utf-8") as f:
|
||||||
|
spec_content = f.read()
|
||||||
|
|
||||||
|
if f"console={not console_mode}" in spec_content:
|
||||||
|
new_spec_content = spec_content.replace(
|
||||||
|
f"console={not console_mode}",
|
||||||
|
f"console={console_mode}"
|
||||||
|
)
|
||||||
|
with open(spec_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(new_spec_content)
|
||||||
|
print(f"✓ Updated {spec_filename}: console={console_mode}")
|
||||||
|
else:
|
||||||
|
print(f"✓ {spec_filename} already configured: console={console_mode}")
|
||||||
|
else:
|
||||||
|
print(f"✗ {spec_filename} not found!")
|
||||||
|
|
||||||
|
print(f"{'-' * 50}\n")
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""Auto-generated version - DO NOT EDIT MANUALLY!
|
||||||
|
|
||||||
|
This file is automatically generated from pyproject.toml.
|
||||||
|
Serves as fallback for cases when TOML is not available.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
"""
|
||||||
|
Generic application constants template.
|
||||||
|
|
||||||
|
Usage in your project:
|
||||||
|
1. Copy this file to src/constants.py
|
||||||
|
2. Fill in APP_NAME and APP_FULL_NAME
|
||||||
|
3. Import VERSION, APP_TITLE, DEFAULT_DEBUG where needed
|
||||||
|
|
||||||
|
Version loading priority:
|
||||||
|
1. pyproject.toml [project] version (preferred)
|
||||||
|
2. src/_version.py __version__ (generated fallback for frozen builds)
|
||||||
|
3. "0.0.0" (last resort)
|
||||||
|
|
||||||
|
Debug mode:
|
||||||
|
Controlled exclusively via .env: ENV_DEBUG=true
|
||||||
|
Accepted true-values: true, 1, yes (case-insensitive)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_ROOT = Path(__file__).parent.parent
|
||||||
|
_PYPROJECT = _ROOT / "pyproject.toml"
|
||||||
|
_VERSION_FILE = Path(__file__).parent / "_version.py"
|
||||||
|
|
||||||
|
|
||||||
|
def _load_version() -> str:
|
||||||
|
# 1. pyproject.toml
|
||||||
|
try:
|
||||||
|
with open(_PYPROJECT, "rb") as f:
|
||||||
|
version = tomllib.load(f)["project"]["version"]
|
||||||
|
# Write fallback for frozen/PyInstaller builds
|
||||||
|
_VERSION_FILE.write_text(
|
||||||
|
f'"""Auto-generated — do not edit manually."""\n__version__ = "{version}"\n',
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
return version
|
||||||
|
except (FileNotFoundError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2. _version.py
|
||||||
|
try:
|
||||||
|
from src._version import __version__ # type: ignore[import]
|
||||||
|
return __version__
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 3. last resort
|
||||||
|
return "0.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Debug mode
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _load_debug() -> bool:
|
||||||
|
return os.getenv("ENV_DEBUG", "false").lower() in ("true", "1", "yes")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Public constants ← fill in APP_NAME / APP_FULL_NAME for each project
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
APP_NAME: str = "MyApp"
|
||||||
|
APP_FULL_NAME: str = "My Application"
|
||||||
|
|
||||||
|
_VERSION_NUMBER: str = _load_version()
|
||||||
|
DEFAULT_DEBUG: bool = _load_debug()
|
||||||
|
|
||||||
|
VERSION: str = f"v{_VERSION_NUMBER}" + ("DEV" if DEFAULT_DEBUG else "")
|
||||||
|
APP_TITLE: str = f"{APP_FULL_NAME} {VERSION}"
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
"""Tests for constants module."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import mock_open, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.constants import (
|
||||||
|
APP_NAME,
|
||||||
|
APP_TITLE,
|
||||||
|
APP_VERSION,
|
||||||
|
ENV_DEBUG,
|
||||||
|
get_debug_mode,
|
||||||
|
get_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_version()
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_version_returns_string() -> None:
|
||||||
|
"""get_version() should return a string."""
|
||||||
|
assert isinstance(get_version(), str)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_version_semver_format() -> None:
|
||||||
|
"""get_version() should return a semver-like string X.Y.Z."""
|
||||||
|
version = get_version()
|
||||||
|
assert re.match(r"^\d+\.\d+\.\d+", version), f"Not semver: {version!r}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_version_fallback_when_toml_missing(tmp_path: Path) -> None:
|
||||||
|
"""get_version() returns '0.0.0-unknown' when pyproject.toml and _version.py are both missing."""
|
||||||
|
missing = tmp_path / "nonexistent.toml"
|
||||||
|
with patch("src.constants._PYPROJECT_PATH", missing):
|
||||||
|
result = get_version()
|
||||||
|
# Either fallback _version.py exists (from previous run) or returns unknown
|
||||||
|
assert isinstance(result, str)
|
||||||
|
assert len(result) > 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_version_unknown_fallback(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""get_version() returns '0.0.0-unknown' when all sources are unavailable."""
|
||||||
|
missing = tmp_path / "nonexistent.toml"
|
||||||
|
monkeypatch.setattr("src.constants._PYPROJECT_PATH", missing)
|
||||||
|
|
||||||
|
# Patch _version import to also fail
|
||||||
|
with patch("src.constants.Path.write_text", side_effect=OSError):
|
||||||
|
with patch.dict("sys.modules", {"src._version": None}):
|
||||||
|
result = get_version()
|
||||||
|
|
||||||
|
assert isinstance(result, str)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_debug_mode()
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_debug_mode_returns_bool() -> None:
|
||||||
|
"""get_debug_mode() should always return a bool."""
|
||||||
|
assert isinstance(get_debug_mode(), bool)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_debug_mode_true(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""get_debug_mode() returns True when ENV_DEBUG=true."""
|
||||||
|
monkeypatch.setenv("ENV_DEBUG", "true")
|
||||||
|
assert get_debug_mode() is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_debug_mode_true_variants(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""get_debug_mode() accepts '1' and 'yes' as truthy values."""
|
||||||
|
for value in ("1", "yes", "YES", "True", "TRUE"):
|
||||||
|
monkeypatch.setenv("ENV_DEBUG", value)
|
||||||
|
assert get_debug_mode() is True, f"Expected True for ENV_DEBUG={value!r}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_debug_mode_false(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""get_debug_mode() returns False when ENV_DEBUG=false."""
|
||||||
|
monkeypatch.setenv("ENV_DEBUG", "false")
|
||||||
|
assert get_debug_mode() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_debug_mode_false_when_unset(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""get_debug_mode() returns False when ENV_DEBUG is not set."""
|
||||||
|
monkeypatch.delenv("ENV_DEBUG", raising=False)
|
||||||
|
assert get_debug_mode() is False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Module-level constants
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_env_debug_is_bool() -> None:
|
||||||
|
"""ENV_DEBUG should be a bool."""
|
||||||
|
assert isinstance(ENV_DEBUG, bool)
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_version_is_string() -> None:
|
||||||
|
"""APP_VERSION should be a string."""
|
||||||
|
assert isinstance(APP_VERSION, str)
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_version_semver_format() -> None:
|
||||||
|
"""APP_VERSION should follow semver format X.Y.Z."""
|
||||||
|
assert re.match(r"^\d+\.\d+\.\d+", APP_VERSION), f"Not semver: {APP_VERSION!r}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_name_value() -> None:
|
||||||
|
"""APP_NAME should be 'X4 SavEd'."""
|
||||||
|
assert APP_NAME == "X4 SavEd"
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_title_contains_name_and_version() -> None:
|
||||||
|
"""APP_TITLE should contain APP_NAME and APP_VERSION."""
|
||||||
|
assert APP_NAME in APP_TITLE
|
||||||
|
assert APP_VERSION in APP_TITLE
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_title_dev_suffix_when_debug(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""APP_TITLE ends with '-DEV' when ENV_DEBUG is True."""
|
||||||
|
import importlib
|
||||||
|
import src.constants as consts
|
||||||
|
|
||||||
|
monkeypatch.setenv("ENV_DEBUG", "true")
|
||||||
|
monkeypatch.setattr(consts, "ENV_DEBUG", True)
|
||||||
|
title = f"{consts.APP_NAME} v{consts.APP_VERSION}" + ("-DEV" if True else "")
|
||||||
|
assert title.endswith("-DEV")
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_title_no_dev_suffix_when_not_debug(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""APP_TITLE does not end with '-DEV' when ENV_DEBUG is False."""
|
||||||
|
import src.constants as consts
|
||||||
|
|
||||||
|
monkeypatch.setattr(consts, "ENV_DEBUG", False)
|
||||||
|
title = f"{consts.APP_NAME} v{consts.APP_VERSION}" + ("-DEV" if False else "")
|
||||||
|
assert not title.endswith("-DEV")
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
# 🏷️ Tagger
|
||||||
|
|
||||||
|
Desktopová aplikace pro správu a organizaci souborů pomocí hierarchických tagů (štítků).
|
||||||
|
|
||||||
|
## ✨ Hlavní funkce
|
||||||
|
|
||||||
|
- 📁 Rekurzivní procházení složek
|
||||||
|
- 🏷️ Hierarchické tagy (kategorie/název)
|
||||||
|
- 🔍 Filtrování podle tagů a textu
|
||||||
|
- 💾 Metadata v JSON souborech (.!tag)
|
||||||
|
- 🎬 Automatická detekce rozlišení videí (ffprobe)
|
||||||
|
- 🎨 Dvě verze GUI: klasické a moderní (qBittorrent-style)
|
||||||
|
|
||||||
|
## 🚀 Rychlý start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Instalace závislostí
|
||||||
|
poetry install
|
||||||
|
|
||||||
|
# Spuštění (moderní GUI)
|
||||||
|
poetry run python Tagger_modern.py
|
||||||
|
|
||||||
|
# Nebo klasické GUI
|
||||||
|
poetry run python Tagger.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📸 Screenshot
|
||||||
|
|
||||||
|
### Moderní GUI (qBittorrent-style)
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ 📁 Otevřít │ 🔄 │ 🏷️ │ 📅 🔍 [____] Toolbar
|
||||||
|
├────────────┬────────────────────────────────────────┤
|
||||||
|
│ 📂 Štítky │ Název │Datum│Štítky│Velikost │
|
||||||
|
│ ├─📁 Stav │ file1.txt│2025 │HD │1.2 MB │
|
||||||
|
│ │ ☑ Nové │ file2.mp4│ │4K │15 MB │
|
||||||
|
│ ├─📁 Video│ file3.jpg│ │RAW │845 KB │
|
||||||
|
│ │ ☐ HD │ │
|
||||||
|
├────────────┴────────────────────────────────────────┤
|
||||||
|
│ Připraven 3 vybráno │ 125 souborů │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Použití
|
||||||
|
|
||||||
|
1. **Otevři složku** - Načti soubory ze složky (rekurzivně)
|
||||||
|
2. **Vytvoř tagy** - Hierarchická struktura (kategorie → tagy)
|
||||||
|
3. **Přiřaď tagy** - Označ soubory, vyber tagy
|
||||||
|
4. **Filtruj** - Klikni na tagy pro filtrování souborů
|
||||||
|
5. **Vyhledávej** - Textové vyhledávání v názvech
|
||||||
|
|
||||||
|
## ⌨️ Keyboard Shortcuts (moderní GUI)
|
||||||
|
|
||||||
|
- `Ctrl+O` - Otevřít složku
|
||||||
|
- `Ctrl+T` - Přiřadit tagy
|
||||||
|
- `Ctrl+D` - Nastavit datum
|
||||||
|
- `F5` - Refresh
|
||||||
|
- `Del` - Smazat z indexu
|
||||||
|
|
||||||
|
## 🏗️ Architektura
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────┐
|
||||||
|
│ Presentation (UI) │ ← Tkinter GUI
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ Business Logic │ ← FileManager, TagManager
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ Data Layer │ ← File, Tag models
|
||||||
|
├─────────────────────────────────┤
|
||||||
|
│ Persistence │ ← JSON .!tag soubory
|
||||||
|
└─────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Struktura projektu
|
||||||
|
|
||||||
|
```
|
||||||
|
Tagger/
|
||||||
|
├── Tagger.py # Entry point (klasické GUI)
|
||||||
|
├── Tagger_modern.py # Entry point (moderní GUI)
|
||||||
|
├── PROJECT_NOTES.md # ⭐ Kompletní dokumentace
|
||||||
|
├── src/
|
||||||
|
│ ├── core/ # Business logika
|
||||||
|
│ │ ├── file.py
|
||||||
|
│ │ ├── tag.py
|
||||||
|
│ │ ├── file_manager.py
|
||||||
|
│ │ └── tag_manager.py
|
||||||
|
│ └── ui/
|
||||||
|
│ ├── gui.py # Klasické GUI
|
||||||
|
│ └── gui_modern.py # Moderní GUI
|
||||||
|
└── tests/ # 116 testů
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧪 Testování
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Všechny testy (116 testů, 100% core coverage)
|
||||||
|
poetry run pytest tests/ -v
|
||||||
|
|
||||||
|
# S coverage report
|
||||||
|
poetry run pytest tests/ --cov=src/core --cov-report=html
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Dokumentace
|
||||||
|
|
||||||
|
**Veškerá dokumentace je v jednom souboru:**
|
||||||
|
|
||||||
|
👉 **[PROJECT_NOTES.md](PROJECT_NOTES.md)** ⭐
|
||||||
|
|
||||||
|
Obsahuje:
|
||||||
|
- Kompletní dokumentaci projektu
|
||||||
|
- Architektonická rozhodnutí (ADR)
|
||||||
|
- Coding standards
|
||||||
|
- Git workflow
|
||||||
|
- Known issues & TODO
|
||||||
|
- Debugování tipy
|
||||||
|
- Pravidla pro AI asistenty
|
||||||
|
|
||||||
|
## 🛠️ Technologie
|
||||||
|
|
||||||
|
- **Python:** 3.12
|
||||||
|
- **GUI:** Tkinter (standard library)
|
||||||
|
- **Dependencies:** Pillow (PIL)
|
||||||
|
- **Package manager:** Poetry
|
||||||
|
- **Testing:** pytest
|
||||||
|
|
||||||
|
## 📊 Metriky
|
||||||
|
|
||||||
|
- **Řádky kódu:** ~1060 Python LOC
|
||||||
|
- **Testy:** 116 (všechny ✅)
|
||||||
|
- **Test coverage:** 100% core modulů
|
||||||
|
- **GUI verze:** 2 (klasická + moderní)
|
||||||
|
|
||||||
|
## 🎯 Design Decisions
|
||||||
|
|
||||||
|
### Proč JSON místo databáze?
|
||||||
|
- ✅ Jednoduchý backup (copy složky)
|
||||||
|
- ✅ Git-friendly (plain text)
|
||||||
|
- ✅ Portable (žádné DB dependencies)
|
||||||
|
- ✅ Metadata zůstanou při přesunu souboru
|
||||||
|
|
||||||
|
### Proč Tkinter?
|
||||||
|
- ✅ Standard library (žádné extra deps)
|
||||||
|
- ✅ Cross-platform
|
||||||
|
- ✅ Dobře dokumentované
|
||||||
|
|
||||||
|
### Proč Poetry?
|
||||||
|
- ✅ Deterministické buildy (poetry.lock)
|
||||||
|
- ✅ Dev dependencies oddělené
|
||||||
|
- ✅ Moderní nástroj
|
||||||
|
|
||||||
|
## 🐛 Known Issues
|
||||||
|
|
||||||
|
- Git merge konflikty s poetry.lock při merge devel→feature
|
||||||
|
- Dlouhé operace (ffprobe) blokují UI - TODO: threading
|
||||||
|
|
||||||
|
## 🚀 Plánované features
|
||||||
|
|
||||||
|
- [ ] Progress bar pro dlouhé operace
|
||||||
|
- [ ] Undo/Redo mechanismus
|
||||||
|
- [ ] Export do CSV/Excel
|
||||||
|
- [ ] Dark mode theme
|
||||||
|
- [ ] Drag & drop souborů
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## 👤 Autor
|
||||||
|
|
||||||
|
honza
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Pro detailní dokumentaci viz [PROJECT_NOTES.md](PROJECT_NOTES.md)**
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
## NODE_EXTRA_CA_CERTS
|
||||||
|
|
||||||
|
1. `Win + R` → `sysdm.cpl` → **Advanced → Environment Variables**
|
||||||
|
2. Pod **User variables** → **New**
|
||||||
|
- Name: `NODE_EXTRA_CA_CERTS`
|
||||||
|
- Value: `C:\cesta\k\zscaler.cer`
|
||||||
|
3. OK → restartovat VSCode
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIE0zCCA7ugAwIBAgIJANu+mC2Jt3uTMA0GCSqGSIb3DQEBCwUAMIGhMQswCQYD
|
||||||
|
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIU2FuIEpvc2Ux
|
||||||
|
FTATBgNVBAoTDFpzY2FsZXIgSW5jLjEVMBMGA1UECxMMWnNjYWxlciBJbmMuMRgw
|
||||||
|
FgYDVQQDEw9ac2NhbGVyIFJvb3QgQ0ExIjAgBgkqhkiG9w0BCQEWE3N1cHBvcnRA
|
||||||
|
enNjYWxlci5jb20wHhcNMTQxMjE5MDAyNzU1WhcNNDIwNTA2MDAyNzU1WjCBoTEL
|
||||||
|
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCFNhbiBK
|
||||||
|
b3NlMRUwEwYDVQQKEwxac2NhbGVyIEluYy4xFTATBgNVBAsTDFpzY2FsZXIgSW5j
|
||||||
|
LjEYMBYGA1UEAxMPWnNjYWxlciBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNzdXBw
|
||||||
|
b3J0QHpzY2FsZXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||||
|
qT7STSxZRTgEFFf6doHajSc1vk5jmzmM6BWuOo044EsaTc9eVEV/HjH/1DWzZtcr
|
||||||
|
fTj+ni205apMTlKBW3UYR+lyLHQ9FoZiDXYXK8poKSV5+Tm0Vls/5Kb8mkhVVqv7
|
||||||
|
LgYEmvEY7HPY+i1nEGZCa46ZXCOohJ0mBEtB9JVlpDIO+nN0hUMAYYdZ1KZWCMNf
|
||||||
|
5J/aTZiShsorN2A38iSOhdd+mcRM4iNL3gsLu99XhKnRqKoHeH83lVdfu1XBeoQz
|
||||||
|
z5V6gA3kbRvhDwoIlTBeMa5l4yRdJAfdpkbFzqiwSgNdhbxTHnYYorDzKfr2rEFM
|
||||||
|
dsMU0DHdeAZf711+1CunuQIDAQABo4IBCjCCAQYwHQYDVR0OBBYEFLm33UrNww4M
|
||||||
|
hp1d3+wcBGnFTpjfMIHWBgNVHSMEgc4wgcuAFLm33UrNww4Mhp1d3+wcBGnFTpjf
|
||||||
|
oYGnpIGkMIGhMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8G
|
||||||
|
A1UEBxMIU2FuIEpvc2UxFTATBgNVBAoTDFpzY2FsZXIgSW5jLjEVMBMGA1UECxMM
|
||||||
|
WnNjYWxlciBJbmMuMRgwFgYDVQQDEw9ac2NhbGVyIFJvb3QgQ0ExIjAgBgkqhkiG
|
||||||
|
9w0BCQEWE3N1cHBvcnRAenNjYWxlci5jb22CCQDbvpgtibd7kzAMBgNVHRMEBTAD
|
||||||
|
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAw0NdJh8w3NsJu4KHuVZUrmZgIohnTm0j+
|
||||||
|
RTmYQ9IKA/pvxAcA6K1i/LO+Bt+tCX+C0yxqB8qzuo+4vAzoY5JEBhyhBhf1uK+P
|
||||||
|
/WVWFZN/+hTgpSbZgzUEnWQG2gOVd24msex+0Sr7hyr9vn6OueH+jj+vCMiAm5+u
|
||||||
|
kd7lLvJsBu3AO3jGWVLyPkS3i6Gf+rwAp1OsRrv3WnbkYcFf9xjuaf4z0hRCrLN2
|
||||||
|
xFNjavxrHmsH8jPHVvgc1VD0Opja0l/BRVauTrUaoW6tE+wFG5rEcPGS80jjHK4S
|
||||||
|
pB5iDj2mUZH1T8lzYtuZy0ZPirxmtsk3135+CKNa2OCAhhFjE0xd
|
||||||
|
-----END CERTIFICATE-----
|
||||||
Reference in New Issue
Block a user