Ryanhub - file viewer
filename: scripts/log_event.py
branch: main
back to repo
#!/usr/bin/env python3
"""Append an interaction event and update related entities."""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from common import (  # noqa: E402
    COMPANIES_DIR,
    CONTACTS_DIR,
    INTERACTION_TYPES,
    LEADS_DIR,
    append_interaction,
    load_entities,
    positive_reply,
    run_sync_indexes,
    today_str,
    write_markdown,
    die,
)


CONTACT_EVENT_TYPES = {"message", "email", "followup", "reply"}
LEAD_STATUS_BY_TYPE = {
    "message": "messaged",
    "email": "messaged",
    "application": "applied",
    "followup": "followed_up",
}


def resolve_entity(entity_type: str, entity_id: str):
    directories = {
        "lead": LEADS_DIR,
        "contact": CONTACTS_DIR,
        "company": COMPANIES_DIR,
    }
    directory = directories.get(entity_type)
    if not directory:
        die(f"Invalid entity type: {entity_type}")
    entities = load_entities(directory)
    if entity_id not in entities:
        die(f"Unknown {entity_type} id: {entity_id}")
    return entities[entity_id]


def infer_company_id(
    contact_id: str | None,
    lead_id: str | None,
    company_id: str | None,
) -> str:
    if company_id:
        resolve_entity("company", company_id)
        return company_id

    if lead_id:
        _, lead_meta, _ = resolve_entity("lead", lead_id)
        lead_company = str(lead_meta.get("company_id", "")).strip()
        if lead_company:
            return lead_company

    if contact_id:
        _, contact_meta, _ = resolve_entity("contact", contact_id)
        contact_company = str(contact_meta.get("company_id", "")).strip()
        if contact_company:
            return contact_company

    return ""


def update_contact(
    contact_id: str,
    event_date: str,
    event_type: str,
    next_followup: str,
) -> None:
    if event_type not in CONTACT_EVENT_TYPES:
        return
    path, meta, body = resolve_entity("contact", contact_id)
    meta["last_contacted"] = event_date
    if next_followup:
        meta["next_followup"] = next_followup
    meta["date_updated"] = today_str()
    write_markdown(path, meta, body)


def update_lead(lead_id: str, event_type: str, summary: str) -> None:
    path, meta, body = resolve_entity("lead", lead_id)
    changed = False

    if event_type in LEAD_STATUS_BY_TYPE:
        meta["status"] = LEAD_STATUS_BY_TYPE[event_type]
        changed = True
    elif event_type == "reply" and positive_reply(summary):
        meta["status"] = "interviewing"
        changed = True

    if changed:
        meta["date_updated"] = today_str()
        write_markdown(path, meta, body)


def main() -> None:
    parser = argparse.ArgumentParser(description="Log an interaction event.")
    parser.add_argument(
        "--type",
        required=True,
        choices=sorted(INTERACTION_TYPES),
        help="Interaction type",
    )
    parser.add_argument("--date", default=today_str(), help="Event date (YYYY-MM-DD)")
    parser.add_argument("--entity-type", default="", help="Primary entity type")
    parser.add_argument("--entity-id", default="", help="Primary entity id")
    parser.add_argument("--contact", default="", help="Contact id")
    parser.add_argument("--lead", default="", help="Lead id")
    parser.add_argument("--company", default="", help="Company id")
    parser.add_argument("--summary", required=True, help="Event summary")
    parser.add_argument("--next-followup", default="", help="Next follow-up date")
    args = parser.parse_args()

    contact_id = args.contact.strip()
    lead_id = args.lead.strip()
    company_id = args.company.strip()

    if contact_id:
        resolve_entity("contact", contact_id)
    if lead_id:
        resolve_entity("lead", lead_id)

    company_id = infer_company_id(contact_id or None, lead_id or None, company_id or None)

    entity_type = args.entity_type.strip()
    entity_id = args.entity_id.strip()
    if not entity_type or not entity_id:
        if contact_id:
            entity_type, entity_id = "contact", contact_id
        elif lead_id:
            entity_type, entity_id = "lead", lead_id
        elif company_id:
            entity_type, entity_id = "company", company_id
        else:
            die("Provide --contact, --lead, or --company")

    resolve_entity(entity_type, entity_id)

    append_interaction(
        {
            "date": args.date,
            "type": args.type,
            "entity_type": entity_type,
            "entity_id": entity_id,
            "contact_id": contact_id,
            "lead_id": lead_id,
            "company_id": company_id,
            "summary": args.summary,
            "next_followup": args.next_followup,
        }
    )

    if contact_id:
        update_contact(contact_id, args.date, args.type, args.next_followup)
    if lead_id:
        update_lead(lead_id, args.type, args.summary)

    print("Logged interaction")
    run_sync_indexes()


if __name__ == "__main__":
    main()