Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ O Data Boar **não substitui** assessoria jurídica; produz **sinais técnicos**

---

## Caminho 0 — Zero-config (recomendado no Windows após `pip install data-boar`)

Sem `config.yaml`, sem Docker, sem YAML — corpus **sintético** embutido:

```powershell
pip install data-boar
data-boar --demo
```

Abra [http://127.0.0.1:8088/pt-br/](http://127.0.0.1:8088/pt-br/) — achados de demonstração já carregados.

**No clone (desenvolvimento):** `uv sync` na raiz, depois `uv run python main.py --demo` ou `.\scripts\demo.sh`.

---

## Caminho A — Docker (menos fricção para não desenvolvedores)

Execute na **raiz do clone** (ajuste o caminho do repositório):
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Data Boar

> **Try it in 30 seconds (no real data) — any OS (Docker):** `docker run --rm -p 8088:8088 fabioleitao/data_boar:latest demo` → open `http://127.0.0.1:8088/pt-br/`. **Linux/macOS shell (local `uv`, no Docker):** `./scripts/demo.sh` (requires `uv` — [install](https://docs.astral.sh/uv/getting-started/installation/)). **Windows or step-by-step:** [5-min QuickStart](QUICKSTART.md). All demo paths use synthetic data and plaintext loopback (`--allow-insecure-http`).
> **Try it in 30 seconds (no real data):** `data-boar --demo` (or `python main.py --demo`) → open `http://127.0.0.1:8088/pt-br/`. **Docker:** `docker run --rm -p 8088:8088 fabioleitao/data_boar:latest demo`. **Shell wrapper:** `./scripts/demo.sh` (requires `uv`). **Windows step-by-step:** [5-min QuickStart](QUICKSTART.md). Synthetic data only; loopback plaintext (`--allow-insecure-http`).

**Data Boar** — enterprise data discovery and risk governance: compliance-aware mapping of personal and sensitive data across your data soup (intelligence engine, not a single-jurisdiction “audit app”).

Expand Down
4 changes: 3 additions & 1 deletion api/templates/help.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ <h3>{{ t('help.run_web_h3') }}</h3>
<h3>{{ t('help.run_auto_h3') }}</h3>
<p class="muted">{{ t('help.run_cli_p1') }}</p>
<p><strong>{{ t('help.run_cli_oneshot') }}</strong></p>
<pre><code>uv run python main.py --config config.yaml
<pre><code>uv run python main.py --demo
data-boar --demo
uv run python main.py --config config.yaml
uv run python main.py --config config.yaml --validate-config
uv run python main.py --config config.yaml --diff &lt;session_a&gt; &lt;session_b&gt;
uv run python main.py --config config.yaml --diff &lt;session_a&gt; &lt;session_b&gt; --fail-on-new-high
Expand Down
21 changes: 21 additions & 0 deletions core/demo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Installable demo corpus and workspace helpers (#1113)."""

from core.demo.runtime import (
prepare_demo_workspace,
print_demo_banner,
register_demo_cleanup,
)
from core.demo.synthetic_corpus import (
ALL_SCENARIOS,
generate_corpus,
main as generate_corpus_cli,
)

__all__ = [
"ALL_SCENARIOS",
"generate_corpus",
"generate_corpus_cli",
"prepare_demo_workspace",
"print_demo_banner",
"register_demo_cleanup",
]
111 changes: 111 additions & 0 deletions core/demo/runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Demo workspace preparation for ``data-boar --demo`` (#1113, #834)."""

from __future__ import annotations

import atexit
import os
import tempfile
from pathlib import Path
from typing import Any

from core.demo.synthetic_corpus import ALL_SCENARIOS, generate_corpus

_DEFAULT_SCENARIOS = "happy,unhappy,false_positive"
_DEMO_DIRNAME = "data_boar_demo"
_registered_cleanup: Path | None = None


def _default_demo_root() -> Path:
return Path(tempfile.gettempdir()) / _DEMO_DIRNAME


def _write_demo_config(demo_dir: Path, port: int) -> Path:
corpus = demo_dir / "corpus"
reports = demo_dir / "reports"
reports.mkdir(parents=True, exist_ok=True)
config_path = demo_dir / "demo.config.yaml"
config_path.write_text(
(
"targets:\n"
" - name: demo-corpus\n"
" type: filesystem\n"
f" path: {corpus}\n"
" recursive: true\n"
"\n"
"report:\n"
f" output_dir: {reports}\n"
"\n"
f"sqlite_path: {demo_dir / 'audit_results.db'}\n"
"\n"
"api:\n"
f" port: {port}\n"
" host: 127.0.0.1\n"
" allow_insecure_http: true\n"
),
encoding="utf-8",
)
return config_path


def _cleanup_demo_dir(demo_dir: Path) -> None:
import shutil

if demo_dir.exists():
shutil.rmtree(demo_dir, ignore_errors=True)


def register_demo_cleanup(demo_dir: Path) -> None:
"""Register atexit cleanup for a single-process ``--demo`` run."""
global _registered_cleanup
if _registered_cleanup is not None:
return
_registered_cleanup = demo_dir

Check notice

Code scanning / CodeQL

Unused global variable Note

The global variable '_registered_cleanup' is not used.

def _on_exit() -> None:
_cleanup_demo_dir(demo_dir)

atexit.register(_on_exit)


def print_demo_banner(port: int, demo_dir: Path) -> None:
print("")
print("╔══════════════════════════════════════════════════════════╗")
print("║ Data Boar — Demo (synthetic corpus, zero real data) ║")
print("╚══════════════════════════════════════════════════════════╝")
print(f"[demo] Workspace: {demo_dir}")
print(f"[demo] Dashboard: http://127.0.0.1:{port}/pt-br/")
print("[demo] Press Ctrl+C to stop (temp files removed on exit).")
print("")


def prepare_demo_workspace(
*,
port: int = 8088,
scenarios: str = _DEFAULT_SCENARIOS,
demo_root: Path | None = None,
register_cleanup: bool = True,
) -> tuple[Path, Path, dict[str, Any]]:
"""
Generate synthetic corpus + minimal config under a temp directory.

Returns ``(demo_dir, config_path, config_dict)`` where ``config_dict`` is
ready to pass to ``load_config``-equivalent flows (after YAML load).
"""
from config.loader import load_config

demo_dir = (demo_root or _default_demo_root()).resolve()
demo_dir.mkdir(parents=True, exist_ok=True)
corpus_dir = demo_dir / "corpus"
corpus_dir.mkdir(parents=True, exist_ok=True)

selected = [s.strip() for s in scenarios.split(",") if s.strip()]
generate_corpus(corpus_dir, selected or ALL_SCENARIOS[:3])

config_path = _write_demo_config(demo_dir, port)
os.environ["CONFIG_PATH"] = str(config_path)
config = load_config(str(config_path))

if register_cleanup:
register_demo_cleanup(demo_dir)

return demo_dir, config_path, config
Loading