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

146 lines
6.0 KiB
Markdown

# Contact search (`redmine_contacts.py`)
`./redmine_contacts.py search --help` prints three short examples. This page is the situation list. Gaps are in [redmine_contacts_search_proposals.md](redmine_contacts_search_proposals.md).
This CLI was not changed to write this page. Commands below match the current `search` / `show` / `helpdesk-issues` behavior.
## What `search` actually does
1. Load the contact cache, or reload it with `--refresh`.
2. Apply exact filters: `--with-tag`, `--without-tag`, `--no-tags`, `--created-after`, `--created-before`, `--updated-after`, `--updated-before`.
3. Fuzzy-score the remaining contacts against the required positional query.
4. Print text lines, capped by `--limit` (default 20).
It does not search tickets. It does not print JSON. A normal search still requires `REDMINE_API_KEY` even when it only reads the cache. `--offline-cache` is the exception.
Default host is the LAN copy, `http://192.168.50.170`. Production:
```bash
cd /home/jason/projects/redmine
./redmine_contacts.py --base-url https://rm.ldrprep.com search "Sahar Daod"
```
Cache file: `.cache/redmine_contacts/<host>_<project>.json`, relative to the current directory. A cache built for one host is rejected for another. This is not `bin/rtl contacts-search`.
A hit is a substring of a scored field after lowercasing, or a fuzzy score of at least `0.68`. Scored fields: name, email, phone, company, job title, background, website, skype, tags, address, and custom-field values. A phone query is also matched on digits only, so `847-313-1342` hits `8473131342`.
Output shape: `id: name | company | email | phone | score=1.00 via email`. Use `show <id>` for the record.
`--refresh` pages through every project contact before scoring. Without it, a tag or field written since the last fetch is invisible.
## Supported
### 1. Find a person by name, email, or phone
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com search "Sahar Daod"
./redmine_contacts.py --base-url https://rm.ldrprep.com search sahardaod4@groovinfulfillment.com
./redmine_contacts.py --base-url https://rm.ldrprep.com search 847-313-1342
```
Then the record, including `tag_list`:
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com show 4589
```
`show` prints JSON. `--source api` skips a stale cache. `--source cache` does not call Redmine.
### 2. Find people at a company, or everyone on one email domain
Company and email are scored fields, not dedicated filters. A domain that appears in the address scores `1.00`.
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com search gatsbylady.com --refresh
./redmine_contacts.py --base-url https://rm.ldrprep.com search "Cyberstorm" --min-score 0.9
```
Raise `--min-score` when a short company name also fuzzy-matches unrelated contacts. There is no `--company` or `--email-domain` flag. An exact company filter is proposal 1.
### 3. Exact tag
`--with-tag` is exact and case-insensitive. The positional query is still required and still fuzzy, so repeat the tag as the query. Otherwise a contact with the tag can be filtered in and then dropped because the name did not fuzzy-match.
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com \
search promotional-spam --with-tag promotional-spam --refresh --limit 50
```
Offline, against the cache already on disk for that host:
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com \
search promotional-spam --with-tag promotional-spam --offline-cache
```
Repeat `--with-tag` to require every tag. Repeat `--without-tag` to exclude any of those tags.
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com \
search customer --with-tag customer --without-tag promotional-spam --limit 20
```
`--no-tags` cannot be combined with `--with-tag`. Listing every untagged contact is not this command. See proposal 2.
### 4. A tag, narrowed by when the contact was created or updated
Date flags are exact. The query is still required, so this finds "this text, inside this window," not "everyone touched this week."
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com \
search customer --with-tag customer --updated-after 2026-09-01 --refresh
```
Dates are `YYYY-MM-DD` or a full ISO datetime. A date-only `--updated-before` includes that whole UTC day. Listing by date with no text query is proposal 2.
### 5. A known account number or other custom-field value
Custom-field values are scored if they are already on the cached contact. Account Number is custom field 70.
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com search 7385Y6 --refresh
```
This finds a value you already know. It cannot list customers whose Account Number is empty. That is proposal 3.
### 6. Their tickets, once you have the contact id
`search` stops at the contact. These follow-ups are live reads, not the contact cache.
```bash
./redmine_contacts.py --base-url https://rm.ldrprep.com helpdesk-issues 4589 --limit 20
./redmine_contacts.py --base-url https://rm.ldrprep.com helpdesk-timeline 4589 --limit 20
```
Both print JSON from `/helpdesk_search/contacts/:id/...`.
Cached Customer Service tickets, any status:
```bash
bin/rtl search --order newest-updated-first --max-tickets 20 \
--where 'helpdesk_ticket.contact_id == 4589 AND project.name == "Customer Service"'
```
Open New only:
```bash
bin/rtl search --status New --open-only \
--where 'helpdesk_ticket.contact_id == 4589'
```
`bin/rtl search` cannot filter on `tag_list`. The ticket's contact stub is `id`, `name`, `email`, `company`. This returns nothing and does not error:
```bash
bin/rtl search --where 'helpdesk_ticket.contact.tag_list.includes("promotional-spam")'
```
One command that means "New tickets from contacts tagged promotional-spam" is proposal 4. `bin/rtl contacts-search --tag` is a different cache and can lag a live tag.
## Not a search
`fetch --with-tag customer` writes a cache and prints a count. It does not list contacts.
`batch-update` can select by the same tag and date filters, but it requires an update payload and exists to write. Do not use it as a lister.
`loop-run` is a triage worker, not a search.