Organized
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user