Files
redmine/docs/2026-07-14_localsearch_before_sanity.md
Jason Thistlethwaite 9ee9eb3ec6 Organized
2026-10-17 20:50:35 -04:00

219 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Local `search` in `redmine_tickets.py` (pre-sanity snapshot)
**Date:** 2026-07-14
**Status:** documentation of current behavior *before* any search sanity/cleanup pass
**Script:** `/home/jason/projects/redmine/redmine_tickets.py`
**Typical cache:** `/home/jason/projects/redmine/cache/redmine_tickets`
This note captures what local `search` does and does **not** do as of 2026-07-14, after remote sync filters and run-log work landed. It is intentionally frank: docs had been thin; this is the baseline.
---
## Mental model
```text
sync = talk to Redmine → update local JSON cache
search = filter/list what is already cached
```
`search` is a **cache browser / structured filterer**, not:
- a live Redmine issue query tool
- full-text corpus search
- the semantic Redmine search path (that lives elsewhere)
If a ticket is not already under the cache directory for the selected project, **`search` cannot see it.**
---
## Where data comes from
Cached issues live as one JSON file per ticket:
```text
<cache-dir>/<project>/<issue-id>.json
```
Example for the durable customer-service cache:
```text
/home/jason/projects/redmine/cache/redmine_tickets/customer-service/*.json
/home/jason/projects/redmine/cache/redmine_tickets/manifest.json
```
`search` loads via `TicketCache.iter_cached_issues()` (reads every `*.json` in the project subdir). It does **not** call Redmine and does **not** require `REDMINE_API_KEY`.
---
## CLI surface (current)
Parent options still apply:
- `--base-url` (unused by search, but accepted on the parent parser)
- `--project`
- `--cache-dir`
Subcommand:
```bash
./redmine_tickets.py \
--project customer-service \
--cache-dir /home/jason/projects/redmine/cache/redmine_tickets \
search [filters...] [output...]
```
### Filters (all local, AND’d)
| Flag | Matches on | Notes |
|------|------------|--------|
| `--updated-since` | issue `updated_on` ≥ value | via `parse_dt` (absolute date/datetime) |
| `--updated-before` | issue `updated_on` ≤ value | same |
| `--status` | status **name** | case-insensitive **exact** match (`Closed`, `New`, …) — **not** `status_id` |
| `--category` | category **name** | case-insensitive **exact** match |
Implementation: `filter_issues()` in `redmine_tickets.py`.
### Order + limit
| Flag | Behavior |
|------|----------|
| `--order newest-updated-first` | **default** |
| `--order oldest-updated-first` | |
| `--order id-asc` / `id-desc` | |
| `--order as-is` | filesystem glob order from `iter_cached_issues` |
| `--max-tickets N` | after filter + sort, keep first N |
Implementation: `select_issues()`.
### Output
| Mode | What you get |
|------|----------------|
| default table | `#id`, `updated_on`, status name, category name, subject (via `print_issue_rows`) |
| `--json` | full cached issue payloads as one JSON array |
| `--jsonl` | one full issue JSON object per line (good for piping) |
Human table is summary-only. Full subject/body/journals/helpdesk are only in `--json` / `--jsonl`.
---
## What it allows (summary)
1. Scope one project + one cache root.
2. Filter cached issues by `updated_on` window, status **name**, category **name**.
3. Sort and cap results.
4. Emit table, JSON, or JSONL.
5. Run offline against cache (no Redmine, no API key).
---
## What it does **not** allow (current gaps)
This is the important half of the snapshot:
- **No free-text search** (subject / description / journals / from-address)
- **No sender / email / contact filters**
- **No tracker, assignee, priority, tags**
- **No `status_id` / `open` / `closed` shorthand** on search (only status **name**)
- **No remote/API filters** on `search`
(`--remote-updated-on`, `--remote-updated-since`, `--remote-status-id` are **sync-only**)
- **No relative time helpers** like `30d` / `2 days ago` on local search flags
(those helpers exist for **remote sync** convenience flags; local `--updated-since` wants a real parseable date/datetime)
- **No boolean logic** (AND/OR/NOT nesting)
- **No query language** comparable to Redmine UI filters or `rg`
- **No ranking / relevance**
- **No “search then sync missing ones”**
- **No pagination UI** beyond `--max-tickets`
- **No writes** (search never mutates cache or Redmine)
### Practical content search today
Pipe full payloads and use external tools:
```bash
./redmine_tickets.py \
--project customer-service \
--cache-dir /home/jason/projects/redmine/cache/redmine_tickets \
search --jsonl | rg -i 'gaylord|rate sheet'
```
Or inspect per-ticket files under the project cache dir directly.
---
## Examples that work
```bash
cd /home/jason/projects/redmine
# Everything currently cached for customer-service (table)
./redmine_tickets.py \
--project customer-service \
--cache-dir /home/jason/projects/redmine/cache/redmine_tickets \
search
# Cached tickets updated since a date, newest first, cap 50
./redmine_tickets.py \
--project customer-service \
--cache-dir /home/jason/projects/redmine/cache/redmine_tickets \
search \
--updated-since 2026-06-14 \
--max-tickets 50
# Only closed, as full JSONL for downstream tools
./redmine_tickets.py \
--project customer-service \
--cache-dir /home/jason/projects/redmine/cache/redmine_tickets \
search \
--status Closed \
--jsonl
```
---
## Related commands people confuse with “search”
| Command | Relation to local search |
|---------|--------------------------|
| `sync` | fills the cache that `search` reads; supports **server-side** `status_id` + `updated_on` |
| `spam-check` | walks **cache**, scores spam; not general search |
| `known-spam-scan` | profile match over **cache** |
| `decompose` | one-ticket analysis, cache-first |
| semantic index / external search | separate stack; not this subcommand |
---
## Remote vs local filter naming (context for sanity later)
As of the 2026-07-14 sync work:
| Side | Flags (examples) | Used by |
|------|------------------|---------|
| **Remote (API)** | `--status-id` / `--remote-status-id`, `--remote-updated-on`, `--remote-updated-since`, `--remote-updated-before` | `sync` list call only |
| **Local (post)** | `--updated-since`, `--updated-before`, `--status`, `--category` | `sync` post-filter **and** `search` |
Help text for the shared local flags still says things like “applied after API list,” which is accurate for `sync` but slightly misleading when those same flags appear under `search` (where there is no API list). A future sanity pass may want search-specific wording.
---
## Implementation pointers
| Piece | Location |
|-------|----------|
| Filter logic | `filter_issues()` |
| Sort / max | `select_issues()` |
| Cache read | `TicketCache.iter_cached_issues()` |
| Table output | `print_issue_rows()` |
| CLI wiring | `build_parser()` → `search` subparser; `main()` branch `args.command == "search"` |
| Shared filter args | `add_common_filters()` |
---
## Bottom line (baseline for sanity)
**Does:** local structured filter/list over already-synced ticket JSON — date window, status name, category name, sort, limit, table/json/jsonl.
**Doesn’t:** live Redmine query, free-text, contact/sender, remote filters, relative-date helpers on local flags, or advanced query syntax.
Any “make search sane” work after this file should treat the above as the pre-change contract and call out intentional expansions (e.g. `--from`, `--subject-contains`, `--text`, relative dates on local filters) separately.