Files
nocobase-mcp/nocobase_crm_mcp/crm_metadata.py
T

185 lines
9.2 KiB
Python

from __future__ import annotations
from .crm_mixin import CrmMixin
from .crm_schema_helpers import (
_configured_or_search_fields,
_date_field,
_existing_field_names,
_field_list,
_field_names,
_find_field,
_find_named_field,
_metadata_error,
_relation_field,
_template_sort,
)
from .crm_shared import Any
class CrmMetadataMixin(CrmMixin):
def crm_health(self) -> dict[str, Any]:
health = self.client.health(self.config.customer.resource, self.config.customer.id_field)
configured = {
"env": self.config.env_name,
"data_source": self.config.data_source,
"collections": {
"customers": self.config.customer.resource,
"contacts": self.config.contact.resource,
"followups": self.config.followup.resource,
"teams": self.config.team.resource,
"projects": self.config.project.resource,
"project_updates": self.config.project.update_resource,
},
}
return {"ok": bool(health.get("ok")), "nb": health, "configured": configured}
def crm_schema(self, include_fields: bool = False) -> dict[str, Any]:
schema: dict[str, Any] = {
"env": self.config.env_name,
"data_source": self.config.data_source,
"collections": {
"customers": self.config.customer.__dict__,
"contacts": self.config.contact.__dict__,
"followups": self.config.followup.__dict__,
"users": self.config.user.__dict__,
"teams": self.config.team.__dict__,
"projects": self.config.project.__dict__,
},
}
if include_fields:
schema["metadata"] = self._safe_metadata()
return schema
def crm_config_template(self) -> dict[str, Any]:
metadata = self._safe_metadata()
fields_by_kind = metadata.get("fields", {}) if isinstance(metadata, dict) else {}
customer_fields = _field_list(fields_by_kind.get("customers"))
contact_fields = _field_list(fields_by_kind.get("contacts"))
followup_fields = _field_list(fields_by_kind.get("followups"))
customer_resource = self.config.customer.resource
contact_resource = self.config.contact.resource
return {
"env": self.config.env_name,
"dataSource": self.config.data_source,
"users": {
"resource": self.config.user.resource,
"fields": self.config.user.fields,
"matchFields": self.config.user.match_fields,
"labelFields": self.config.user.label_fields,
},
"collections": {
"customers": {
"resource": customer_resource,
"fields": _field_names(customer_fields),
"searchFields": _configured_or_search_fields(
customer_fields,
["f_q2q60q7bqbg", "f_kh_english_name", "f_territorial_name"],
),
# Ordered: the first non-empty one becomes the customer's
# displayed name everywhere it is referenced.
"nameFields": _existing_field_names(
customer_fields,
["f_q2q60q7bqbg", "f_kh_english_name", "f_territorial_name"],
)
or self.config.customer.name_fields,
"managerField": _find_named_field(customer_fields, ["kehu_manage_person"])
or self.config.customer.manager_field,
"detailFields": {
key: value
for key, value in self.config.customer.detail_fields.items()
if _find_named_field(customer_fields, [value])
},
"statusField": _find_named_field(customer_fields, ["kh_progress"])
or _find_field(customer_fields, ["进度", "状态", "status"]),
"sourceField": _find_named_field(customer_fields, ["f_225h6n6v654"])
or _find_field(customer_fields, ["客户来源", "来源", "source"]),
"ownerField": _find_named_field(customer_fields, ["kehu_followup_person"])
or self.config.customer.owner_field,
"sort": self.config.customer.sort,
},
"contacts": {
"resource": contact_resource,
"fields": _field_names(contact_fields),
"searchFields": _configured_or_search_fields(
contact_fields,
["f_2zpg91gtmwp", "khlxr_phone_number", "khlxr_email"],
),
"nameFields": _existing_field_names(contact_fields, ["f_2zpg91gtmwp"])
or self.config.contact.name_fields,
"titleField": _find_named_field(contact_fields, ["f_tn8zdm2f938"])
or self.config.contact.title_field,
"phoneField": _find_named_field(contact_fields, ["khlxr_phone_number"])
or self.config.contact.phone_field,
"emailField": _find_named_field(contact_fields, ["khlxr_email"]) or self.config.contact.email_field,
"customerField": _find_named_field(contact_fields, ["kehulianxiren_client"])
or _relation_field(contact_fields, ["公司", "客户"])
or self.config.contact.customer_field,
"sort": self.config.contact.sort,
},
"followups": {
"resource": self.config.followup.resource,
"fields": _field_names(followup_fields),
"searchFields": _configured_or_search_fields(followup_fields, ["f_19iplxz0nds"]),
"customerField": _find_named_field(followup_fields, ["gjjl_client"])
or _relation_field(followup_fields, ["客户"])
or self.config.followup.customer_field,
"contactField": _find_named_field(followup_fields, ["gjjl_client_contacts"])
or _relation_field(followup_fields, ["客户联系人", "联系人"])
or self.config.followup.contact_field,
"ownerField": _find_named_field(followup_fields, ["gjjl_followup_person"])
or self.config.followup.owner_field,
"ownerFields": _existing_field_names(
followup_fields,
["gjjl_followup_person", "createdBy"],
)
or self.config.followup.owner_fields,
"dateField": _find_named_field(followup_fields, ["f_srysmjkwfq1"])
or _date_field(followup_fields)
or self.config.followup.date_field,
"contentField": _find_named_field(followup_fields, ["f_19iplxz0nds"])
or self.config.followup.content_field,
"typeField": _find_named_field(followup_fields, ["gjjl_followup_type"])
or self.config.followup.type_field,
"statusField": _find_named_field(followup_fields, ["f_hskl7gjvos9"])
or self.config.followup.status_field,
"projectField": _find_named_field(followup_fields, ["f_gjjl_projects"])
or self.config.followup.project_field,
"locationField": _find_named_field(followup_fields, ["f_b5c4hfi5og6"])
or self.config.followup.location_field,
"sort": _template_sort(
_find_named_field(followup_fields, ["f_srysmjkwfq1"])
or _date_field(followup_fields)
or self.config.followup.date_field,
self.config.followup.sort,
),
},
},
"metadata": {
"field_errors": metadata.get("field_errors", {}) if isinstance(metadata, dict) else {},
"note": "Review inferred owner/status/source/date fields before using this template in production.",
},
}
def _safe_metadata(self) -> dict[str, Any]:
collections = {
"customers": self.config.customer.resource,
"contacts": self.config.contact.resource,
"followups": self.config.followup.resource,
"teams": self.config.team.resource,
"projects": self.config.project.resource,
"project_updates": self.config.project.update_resource,
}
fields: dict[str, list[dict[str, Any]]] = {}
field_errors: dict[str, dict[str, str]] = {}
for label, collection_name in collections.items():
try:
response = self.client.list_collection_fields(
collection_name,
page_size=500,
paginate=False,
)
fields[label] = self._data(response)
except Exception as error:
field_errors[label] = _metadata_error(error)
return {"collections": collections, "fields": fields, "field_errors": field_errors}