Files
Jason Thistlethwaite 9ee9eb3ec6 Organized
2026-10-17 20:50:35 -04:00

11 KiB

Having a look at the projects ~/projects/obsidian_vault_maintainers and '~/projects/redmine/`, I would like to have a set of scripts, cronjobs, or agents/prompts that does something like the following:

First, import the PROJECT.md from configs and apply it to ~/projects/redmine

This is to ensure that further work on this project is documented and worked on in the manner we're beginning to standardize for most of our projects.

Second, critical update to the redmine project script: redmine_contacts.py

  • Fetch and search should have an option to only show contacts based on optional filters (if possible):
    • Filter what contacts are fetched based on:
      • created_on and/or updated_on
      • presence or lack of a specific tag, "no tag at all", etc
  • Add command to fetch an entire ticket by issue_id. This should be similar to how the redMCP handles being asked to retrieve a complete ticket (it should include the journals/notes, helpdesk info, etc. as well as the basic ticket info).
  • Add an options for show, which outputs the full data/json object for a specific contact_id
    • Should be able to pull from the local cache if it exists, so we don't keep calling the API without good reason
    • Should also be able to pull directly from the API
  • Cleaner support for particular aspects of the update command:
    • tag_list:
      • It should be easy to add or remove a specific tag. As it stands, using update and --set tag_list= requires listing all of the desired tags (delimited by commas), and overwrites any pre-existing value.
        • Instead, we should just have a simple '--addTag ' and '--removeTag ' options for the update command
    • custom_fields:
      • We should be able to set a specific custom field to a new value, without deleting/overwriting other customer fields.
        • E.g. "Account Number" is customer field id 70. We should be able to easily set a particular contact's account number to something new without overwriting, deleting, or needing to re-send the values of other customer fields (like field 42, their Facebook profile/link)
      • Specify by kebab-case custom-field name, rather than numeric id:
        • Examples:
          • Custom field id 70 is "Account Number". Should be able to do --setCustomField account-number="whatever"
          • Custom field id 88 is "X/Twitter". Should be able to do --setCustomField x-twitter="foobar"
  • Conduct some related research: research the following and add it to documentation within the project:
    • Possible duplicates: The web interface for the helpdesk_contacts plugin has a feature which shows possible duplicate contacts, and supports a workflow for "merging them". We need to know if it's feasible to implement this feature via the API or through a bash script, so we can merge contacts if needed.
      • The existing web workflow ends up at a url like this: /contacts/<contact1_id>/duplicates?project_id=<project_slug>, and allows submitting a form which passes duplicate_id.
        • E.g. If we wanted to merge contact 1500 with contact 11, we'd end up at /contacts/1500/duplicates?project_id=customer-service and then submit the form with duplicate_id=11 as a POSTed data field. Doing so would:
          • Delete contact 1500
          • Any tickets, deals, etc. previously associated with 1500 would then be associated with 11
      • Subgoal 1: Check if the API seems to support a call for replicating this workflow.
        • If it does, spec out how easily it could be added as an option/tool for this script
        • If it does not, check if utilizing something like curl to achieve it would be safe and feasible. Do so by investigating how the normal browser path would work.

It's not super important to implement this exactly the way I'm saying. Please pay more attention to how I want to use the data.

Some context:

Local testing copy/instance of Redmine

192.168.50.170 hosts a local instance of Redmine that is synced from the production system daily, so it acts and behaves like the real website. This can be used for testing/verifying features/changes. To do so, in any of these commands which reference a --base-url option, use http://192.168.50.170 instead of https://rm.ldrprep.com.

How to fetch a JSON blob of all visible contacts:

~/projects/redmine/redmine_contacts.py --project customer-service --base-url https://rm.ldrprep.com fetch

The above will save a cached JSON blob of all visible contacts in ~/.cache/redmine_contacts/rm.ldrprep.com_customer-service.json

Fetch recent tickets for a specific contact

# Outdated
# ~/projects/redmine/redmine_contacts.py --base-url https://rm.ldrprep.com --project customer-service helpdesk-timeline --limit 5 <contact_id>


# Use this method
~/projects/redmine/redmine_contacts.py --base-url https://rm.ldrprep.com --project customer-service helpdesk-issues --limit 5 <contact_id>

This will show metadata about the five most recent tickets involving that contact (if there are any). The results will include the subject of the ticket, issue_id for pulling up more details, assigned_to_name (person or group assigned to, if any), updated_on (date and time the ticket last changed by anybody), created_on (date and time the email chain started), from_address (actual email address the contact used to email us), and other fields.

Overall project objectives

  • Support programmatic recall/fetch of Redmine contacts in these kinds of ways:
    • All contacts who have been created since date1 but have not been updated more recently than date2.
      • This is for the purpose of doing things such as cycling through all of our contacts to apply basic changes to them like fixing their name or possibly adding basic tags to them. The idea being, we could ask for "20 newest contacts not updated within the last 7 days" or something, update them, and then go again. Eventually, we would get through all of the contacts.
    • Contacts with a specific combination of tags (specific tag or couple tags, TagA but not TagB, no tags at all, etc)
      • E.g. "All contacts with the tag 'customer', but without the tag 'prep'"
    • No background entry recorded
  • Core outcomes we want to enable:
    • Tagging contacts as a guide for other automated tools: We want to be able to use tags to control how other automated tools may do things, such as triaging new tickets received, automatically adding additional context, etc.
    • Identify and link 'company' contacts with people who work at that company:
      • From context within tickets, we can tell that a contact works at a company, and we can infer the name of that company.
      • If it does not already exist, add the company as a contact in Redmine, then:
        • Identify contacts who are people that work at that company, and update their profile so the company field=the company they work at.
    • Support robust profiles across obsidian, Redmine, NMI, and other systems:
      • Outside the scope of this project right now, but we want to be able to do enrichment with our contacts, working to complete missing information like their LinkedIn profile or job title.
      • Obsidian should have a matching entry for any significant contact, such as a customer, employee, or vendor.
      • Each of these profiles should cross-reference each other, where possible. For instance:
        • Redmine profile contacts user_account.user_account_id value from NMI which corresponds to that contact's account in the NMI portal (if they are a customer or employee)
        • User account notes in NMI link to the highest-level contact entity in Redmine (typically the company the person works at/for)
        • Accurate phone number appears the same in all relevant places
    • Compile/calculated Gist:
      • Given a particular contact and a timespan, we should be able to fetch all customer-service tickets from Redmine that were updated within that timespan and which involve that contact.
      • If we specified a contact that is a company, this should fetch all tickets involving anybody at that company
      • We should then be able to generate an AI summary of the main topics or situations that contact was interested in or talking about during that timespan.
      • This kind of summary/gist can be displayed or referenced in various places, and would be saved within obsidian in a manner that ties it together with the contact and timespan.
  • Identify and mark contacts as customers as follows:
    • Fetch contacts that do not have the "customer" tag and have not been updated recently
    • Check for the 5 most recent tickets involving that contact, and do as folows:
      • Determine if they are likely a customer, and then document them:
        • The following things indicate the contact is a customer. The more of these which apply, the stronger the chance is they are a customer:
          • The ticket(s) are assigned to a specific person (a name);
          • Someone on our side replied to the ticket, appearing to answer their question or provide information;
          • The content of the ticket is talking about something like a package, shipment, order, prep, or pictures;
          • The contact does not appear to be selling us anything or trying to get us to register to a service of some kind;
        • If they are a customer, update the contact as follows:
          • Add the tag "customer"
          • Add the tag "needs-enriched"
          • Add the tag "needs-nmi-sync"
        • Check obsidian-ldr to see if there is a file about the customer.
          • If there isn't, create one. Place it under people/DRAFT/ to indicate a human should confirm it seems to be accurate.
          • The file should cross-reference/link to the Redmine contact, including the following information:
            • First and last name, name of company they work for, email address, phone number, and contact_id
        • They are not a customer, but are clearly something else:
          • Automated Mailer: The contact is most likely an automated mailer.
            • This is determined by checking for the following criteria. If most of these are true, they're probably an automated mailer:
              • There are five or more tickets from them, and those tickets all seem to be about the same general topic;
              • There are no replies to their tickets from anyone on our team;
              • The tickets are not assigned to named person or group;
              • The email body says something like "this inbox is not monitored";
              • Their email address includes "no-reply@" or "noreply@"
            • Apply the tag "Automailer" to the contact
  • This system should have a "dry run" feature for testing purposes, and should work so it can be limited to updating a specific number of contacts each time it is run.

The main goal to start with is tuning up and improving the redmine_contacts.py CLI script. Feel free to note and sideline goals which seem too ambicious to handle at the moment.