From 46c4173440b49fe46a0699e772310b43311c8d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Doubravsk=C3=BD?= Date: Mon, 25 May 2026 17:37:23 +0200 Subject: [PATCH] Organize documentation into topic folders (Claude, Python, Zscaler, Project template) --- Claude/AGENTS.md | 83 ++++++ Claude/CLAUDE.md | 10 + Claude/skills/changelog/SKILL.md | 8 + Claude/skills/check/SKILL.md | 5 + Claude/skills/commit/SKILL.md | 12 + Claude/skills/context/SKILL.md | 38 +++ Claude/skills/documentation/SKILL.md | 5 + .gitignore => Project template/.gitignore | 0 Project template/CHANGELOG.md | 20 ++ Project template/PROJECT.md | 5 + Python/DESIGN_DOCUMENT.md | 212 ++++++++++++++ Python/DESIGN_DOCUMENT_MODULE.md | 260 ++++++++++++++++++ Python/TEMPLATE.md | 41 +++ Python/prebuild.py | 66 +++++ Python/src/__init__.py | 0 Python/src/_version.py | 7 + Python/src/constants.py | 81 ++++++ Python/tests/__init__.py | 0 Python/tests/test_constants.py | 141 ++++++++++ README.md | 174 ++++++++++++ Zscaler/NODE_EXTRA_CA_CERTS.md | 7 + .../ZscalerRootCertificate-2048-SHA256.crt | 28 ++ 22 files changed, 1203 insertions(+) create mode 100644 Claude/AGENTS.md create mode 100644 Claude/CLAUDE.md create mode 100644 Claude/skills/changelog/SKILL.md create mode 100644 Claude/skills/check/SKILL.md create mode 100644 Claude/skills/commit/SKILL.md create mode 100644 Claude/skills/context/SKILL.md create mode 100644 Claude/skills/documentation/SKILL.md rename .gitignore => Project template/.gitignore (100%) create mode 100644 Project template/CHANGELOG.md create mode 100644 Project template/PROJECT.md create mode 100644 Python/DESIGN_DOCUMENT.md create mode 100644 Python/DESIGN_DOCUMENT_MODULE.md create mode 100644 Python/TEMPLATE.md create mode 100644 Python/prebuild.py create mode 100644 Python/src/__init__.py create mode 100644 Python/src/_version.py create mode 100644 Python/src/constants.py create mode 100644 Python/tests/__init__.py create mode 100644 Python/tests/test_constants.py create mode 100644 README.md create mode 100644 Zscaler/NODE_EXTRA_CA_CERTS.md create mode 100644 Zscaler/ZscalerRootCertificate-2048-SHA256.crt diff --git a/Claude/AGENTS.md b/Claude/AGENTS.md new file mode 100644 index 0000000..b4f594a --- /dev/null +++ b/Claude/AGENTS.md @@ -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 diff --git a/Claude/CLAUDE.md b/Claude/CLAUDE.md new file mode 100644 index 0000000..c11f258 --- /dev/null +++ b/Claude/CLAUDE.md @@ -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` \ No newline at end of file diff --git a/Claude/skills/changelog/SKILL.md b/Claude/skills/changelog/SKILL.md new file mode 100644 index 0000000..de16535 --- /dev/null +++ b/Claude/skills/changelog/SKILL.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. diff --git a/Claude/skills/check/SKILL.md b/Claude/skills/check/SKILL.md new file mode 100644 index 0000000..db1395f --- /dev/null +++ b/Claude/skills/check/SKILL.md @@ -0,0 +1,5 @@ +--- +name: check +description: Check for failed tests +--- +Try running tests and investigate failed results \ No newline at end of file diff --git a/Claude/skills/commit/SKILL.md b/Claude/skills/commit/SKILL.md new file mode 100644 index 0000000..9f5fa14 --- /dev/null +++ b/Claude/skills/commit/SKILL.md @@ -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. diff --git a/Claude/skills/context/SKILL.md b/Claude/skills/context/SKILL.md new file mode 100644 index 0000000..980ec44 --- /dev/null +++ b/Claude/skills/context/SKILL.md @@ -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. diff --git a/Claude/skills/documentation/SKILL.md b/Claude/skills/documentation/SKILL.md new file mode 100644 index 0000000..6818979 --- /dev/null +++ b/Claude/skills/documentation/SKILL.md @@ -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. \ No newline at end of file diff --git a/.gitignore b/Project template/.gitignore similarity index 100% rename from .gitignore rename to Project template/.gitignore diff --git a/Project template/CHANGELOG.md b/Project template/CHANGELOG.md new file mode 100644 index 0000000..7cb4fc0 --- /dev/null +++ b/Project template/CHANGELOG.md @@ -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 diff --git a/Project template/PROJECT.md b/Project template/PROJECT.md new file mode 100644 index 0000000..3402a6d --- /dev/null +++ b/Project template/PROJECT.md @@ -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. diff --git a/Python/DESIGN_DOCUMENT.md b/Python/DESIGN_DOCUMENT.md new file mode 100644 index 0000000..5d859f1 --- /dev/null +++ b/Python/DESIGN_DOCUMENT.md @@ -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__` + +--- + +## 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 # Add runtime dependency +poetry add --group dev # Add dev dependency +poetry remove # Remove dependency +poetry run # 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`. diff --git a/Python/DESIGN_DOCUMENT_MODULE.md b/Python/DESIGN_DOCUMENT_MODULE.md new file mode 100644 index 0000000..2ec4855 --- /dev/null +++ b/Python/DESIGN_DOCUMENT_MODULE.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("") # suppress all +logger.add(sys.stderr, filter={"": "WARNING"}) # WARNING and above only +logger.enable("") # 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//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__` +- 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 # Add runtime dependency +poetry add --group dev # Add dev dependency +poetry remove # Remove dependency +poetry run # 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//__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`. diff --git a/Python/TEMPLATE.md b/Python/TEMPLATE.md new file mode 100644 index 0000000..56955bc --- /dev/null +++ b/Python/TEMPLATE.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 diff --git a/Python/prebuild.py b/Python/prebuild.py new file mode 100644 index 0000000..84de3ae --- /dev/null +++ b/Python/prebuild.py @@ -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") \ No newline at end of file diff --git a/Python/src/__init__.py b/Python/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Python/src/_version.py b/Python/src/_version.py new file mode 100644 index 0000000..637e9af --- /dev/null +++ b/Python/src/_version.py @@ -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" diff --git a/Python/src/constants.py b/Python/src/constants.py new file mode 100644 index 0000000..68d877a --- /dev/null +++ b/Python/src/constants.py @@ -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}" diff --git a/Python/tests/__init__.py b/Python/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Python/tests/test_constants.py b/Python/tests/test_constants.py new file mode 100644 index 0000000..7156474 --- /dev/null +++ b/Python/tests/test_constants.py @@ -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") diff --git a/README.md b/README.md new file mode 100644 index 0000000..277179a --- /dev/null +++ b/README.md @@ -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)** diff --git a/Zscaler/NODE_EXTRA_CA_CERTS.md b/Zscaler/NODE_EXTRA_CA_CERTS.md new file mode 100644 index 0000000..80f08d0 --- /dev/null +++ b/Zscaler/NODE_EXTRA_CA_CERTS.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 diff --git a/Zscaler/ZscalerRootCertificate-2048-SHA256.crt b/Zscaler/ZscalerRootCertificate-2048-SHA256.crt new file mode 100644 index 0000000..45e3a29 --- /dev/null +++ b/Zscaler/ZscalerRootCertificate-2048-SHA256.crt @@ -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-----