131 lines
4.5 KiB
Python
131 lines
4.5 KiB
Python
"""Client delivery pipeline: Google Drive upload + Gmail draft creation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
|
|
from .contacts import CompanyContact, lookup_contact, parse_company_directory
|
|
from .email_templates import load_template, render_template
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class DeliveryResult:
|
|
"""Result of a client delivery attempt."""
|
|
|
|
doc_links: list[str] = field(default_factory=list)
|
|
draft_id: str = ""
|
|
errors: list[str] = field(default_factory=list)
|
|
|
|
@property
|
|
def success(self) -> bool:
|
|
return bool(self.draft_id) and not self.errors
|
|
|
|
|
|
def deliver_to_client(
|
|
files: list[Path],
|
|
company_name: str,
|
|
task_id: str,
|
|
task_type: str,
|
|
ctx: dict | None = None,
|
|
) -> DeliveryResult:
|
|
"""Upload files to Google Drive and create a Gmail draft for the client.
|
|
|
|
Steps:
|
|
1. Look up client contact (email, opening, cc) from companies.md
|
|
2. Upload each .docx to Google Drive as a Google Doc
|
|
3. Load and render the email template for this task type
|
|
4. Create a Gmail draft with the rendered content
|
|
|
|
This function is designed to be non-fatal -- callers should wrap in try/except
|
|
so delivery failures don't break the main pipeline.
|
|
"""
|
|
result = DeliveryResult()
|
|
config = ctx.get("config") if ctx else None
|
|
|
|
# 1. Look up client contact
|
|
contacts = parse_company_directory()
|
|
contact = lookup_contact(company_name, contacts)
|
|
if not contact:
|
|
result.errors.append("No contact found for company '%s'" % company_name)
|
|
log.warning("Delivery aborted: %s", result.errors[-1])
|
|
return result
|
|
if not contact.email:
|
|
result.errors.append(
|
|
"No email address for '%s' -- add Email field to skills/companies.md" % contact.name
|
|
)
|
|
log.warning("Delivery aborted: %s", result.errors[-1])
|
|
return result
|
|
|
|
# 2. Upload to Google Drive
|
|
drive_enabled = config and config.google_drive.enabled if config else False
|
|
if drive_enabled:
|
|
try:
|
|
from .google_drive import DriveClient
|
|
|
|
drive = DriveClient(root_folder_id=config.google_drive.root_folder_id)
|
|
folder_id = drive.ensure_client_folder(contact.name)
|
|
|
|
for file_path in files:
|
|
if not file_path.exists():
|
|
result.errors.append("File not found: %s" % file_path)
|
|
continue
|
|
file_id, web_link = drive.upload_as_google_doc(file_path, folder_id)
|
|
drive.set_link_sharing(file_id, role="commenter")
|
|
result.doc_links.append(web_link)
|
|
except Exception as e:
|
|
result.errors.append("Drive upload failed: %s" % e)
|
|
log.error("Drive upload failed: %s", e)
|
|
else:
|
|
log.info("Google Drive disabled, skipping upload")
|
|
# Fall back to listing local file paths
|
|
for file_path in files:
|
|
result.doc_links.append(str(file_path))
|
|
|
|
# 3. Load and render email template
|
|
template = load_template(task_type)
|
|
if not template:
|
|
result.errors.append("No email template for task type '%s'" % task_type)
|
|
log.warning("Delivery aborted: %s", result.errors[-1])
|
|
return result
|
|
|
|
# Build Google Doc links as a bullet list
|
|
links_text = "\n".join("- %s" % link for link in result.doc_links)
|
|
|
|
# Use the contact's custom opening, or fall back to "Hi {first_name},"
|
|
opening = contact.opening or ("Hi %s," % contact.executive_first_name)
|
|
|
|
context = {
|
|
"company_name": contact.name,
|
|
"opening": opening,
|
|
"executive_first_name": contact.executive_first_name,
|
|
"google_doc_links": links_text,
|
|
"task_id": task_id,
|
|
}
|
|
subject, body = render_template(template, context)
|
|
|
|
# 4. Create Gmail draft
|
|
gmail_enabled = config and config.gmail_api.enabled if config else False
|
|
if gmail_enabled:
|
|
try:
|
|
from .gmail_draft import GmailDraftClient
|
|
|
|
gmail = GmailDraftClient()
|
|
result.draft_id = gmail.create_draft(
|
|
to=contact.email,
|
|
subject=subject,
|
|
body=body,
|
|
cc=contact.cc_list or None,
|
|
)
|
|
except Exception as e:
|
|
result.errors.append("Gmail draft creation failed: %s" % e)
|
|
log.error("Gmail draft creation failed: %s", e)
|
|
else:
|
|
log.info("Gmail API disabled, skipping draft creation")
|
|
result.errors.append("Gmail API disabled -- draft not created")
|
|
|
|
return result
|