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

5.3 KiB

Contact search proposals

Situations redmine_contacts.py search does not cover, as of the current CLI. Do not treat these commands as existing. The working commands are in redmine_contacts_search.md.

None of these require changing ticket search. They belong on the contacts CLI, with a dry-run default and no write path.

1. Exact company or email domain

Need: "everyone at gatsbylady.com" or "company is UPS" without fuzzy misses and without a short string scoring unrelated names.

Today: company and email are haystack fields. search gatsbylady.com works when that string is inside an email. search UPS also fuzzy-matches other fields above 0.68. There is no --company or --domain.

Proposal:

./redmine_contacts.py --base-url https://rm.ldrprep.com \
  search --domain gatsbylady.com --refresh
./redmine_contacts.py --base-url https://rm.ldrprep.com \
  search --company UPS --company-exact --refresh

--domain matches the part after @, case-insensitive, across emails[]. --company-exact matches company after trim and case-fold, not a substring of the whole record. If either flag is set, the positional query becomes optional. Print the same text line search prints now, plus --json from proposal 5.

2. List by filter, with no fuzzy query

Need: every contact with no tags, or every contact updated since a date, or every customer tag, without inventing a query that happens to score.

Today: search always requires query, then drops anyone under --min-score. --no-tags and the date flags only narrow that fuzzy set. fetch --no-tags caches matches and prints a count, not rows.

Proposal: a list subcommand that reuses filter_contacts and does not call search_contacts.

./redmine_contacts.py --base-url https://rm.ldrprep.com \
  list --no-tags --updated-after 2026-09-01 --refresh --limit 50
./redmine_contacts.py --base-url https://rm.ldrprep.com \
  list --with-tag promotional-spam --refresh

Require at least one filter, same guard as batch-update, so list cannot dump the whole CRM by accident. Default --limit 50. Order by updated_on descending. Text lines should include id, name, email, and tag_list, because a bare fuzzy score is meaningless here.

Do not overload search by making query optional. Operators already have to know that search is fuzzy.

3. Tagged customers with an empty Account Number

Need: billed or tagged customers who have no NMI account id in custom field 70 (Account Number), so the jumpstart can see who is unmatched.

Today: a known account number can be fuzzy-searched if it is already stored. There is no empty-field filter. show is one id at a time.

Proposal:

./redmine_contacts.py --base-url https://rm.ldrprep.com \
  list --with-tag customer --missing-custom-field account-number --refresh

Resolve account-number with the existing custom-field slug (slugify_custom_field_name). Missing means the field is absent, null, or whitespace. Do not treat "0" as empty. Read-only. This is the list command from proposal 2 plus one predicate, not a new writer.

4. Open tickets from contacts with a tag

Need: New Customer Service tickets whose sender is tagged promotional-spam, without hand-joining ids.

Today: ticket JSON does not store tag_list. bin/rtl search --where 'helpdesk_ticket.contact.tag_list.includes(...)' matches nothing. The working path is tag search, then helpdesk_ticket.contact_id == N per id.

Proposal: a read-only join, not a ticket-cache field.

./redmine_contacts.py --base-url https://rm.ldrprep.com \
  tagged-issues --with-tag promotional-spam --issue-status New --open-only --refresh

Steps:

  1. Resolve contact ids with the list filter from proposal 2, live or --refresh.
  2. For each id, call the existing helpdesk-issues endpoint, or scan the ticket cache for helpdesk_ticket.contact_id.
  3. Keep issues that are New, open, and in customer-service.
  4. Print issue_id contact_id email subject, and stop after --limit issues (default 20).

Do not close anything. Do not add tag_list to the ticket cache in this change. A stale contacts cache must be called out in the header line (cache_refreshed=yes|no). Refuse to run if the contact cache host does not match --base-url, which the current cache reader already does.

5. Script output: ids and JSON

Need: feed contact ids into bin/rtl or a later join without scraping score=1.00 via email.

Today: search prints prose. show and helpdesk-issues print JSON for one id. There is no --ids-only on this CLI. bin/rtl contacts-search --ids-only is the other cache.

Proposal: --json and --ids-only on search and on list.

./redmine_contacts.py --base-url https://rm.ldrprep.com \
  search promotional-spam --with-tag promotional-spam --refresh --ids-only

--json prints one object per line: id, name, email, tag_list, and score when the command was fuzzy. --ids-only prints one integer per line. Neither flag changes which rows match.

Out of scope

Duplicate detection (same email or phone on two contacts) needs a separate scan and a human merge step. Do not fold it into search. The existing duplicate-merge note stays the place for that design.

Do not make batch-update printable as a search. It requires a mutation payload on purpose.