dhisana 0.0.1.dev218__tar.gz → 0.0.1.dev219__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/PKG-INFO +1 -1
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/setup.py +1 -1
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/schemas/sales.py +67 -27
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/test_connect.py +109 -1
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana.egg-info/PKG-INFO +1 -1
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_connectivity.py +23 -4
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/README.md +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/pyproject.toml +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/setup.cfg +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/cli/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/cli/cli.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/cli/datasets.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/cli/models.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/cli/predictions.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/schemas/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/schemas/common.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/ui/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/ui/components.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/add_mapping.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/agent_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/apollo_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/assistant_tool_tag.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/built_with_api_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/cache_output_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/cache_output_tools_local.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/check_email_validity_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/check_for_intent_signal.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/clay_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/clean_properties.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/company_utils.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/compose_salesnav_query.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/compose_search_query.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/composite_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/dataframe_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/domain_parser.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/email_parse_helpers.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/email_provider.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/enrich_lead_information.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/fetch_openai_config.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/field_validators.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/g2_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_content.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_email.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_email_response.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_flow.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_leads_salesnav.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_linkedin_response_message.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/google_custom_search.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/google_oauth_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/google_workspace_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/hubspot_clearbit.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/instantly_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/linkedin_crawler.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/lusha_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/mailgun_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/microsoft365_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openai_helpers.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/profile.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/proxy_curl_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/proxycurl_search_leads.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/python_function_to_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/research_lead.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/search_router.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/search_router_jobs.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/sendgrid_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serarch_router_local_business.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serpapi_additional_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serpapi_google_jobs.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serpapi_google_search.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serpapi_local_business_search.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serpapi_search_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serperdev_google_jobs.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serperdev_local_business.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serperdev_search.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/smtp_email_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/trasform_json.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/web_download_parse_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/workflow_code_model.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/zoominfo_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/workflow/__init__.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/workflow/agent.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/workflow/flow.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/workflow/task.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/workflow/test.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana.egg-info/SOURCES.txt +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana.egg-info/dependency_links.txt +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana.egg-info/entry_points.txt +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana.egg-info/requires.txt +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana.egg-info/top_level.txt +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_agent_tools.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_apollo_company_search.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_google_document.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_hubspot_call_logs.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_linkedin_serper.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_mcp_connectivity.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_proxycurl_get_company_search_id.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_proxycurl_job_count.py +0 -0
- {dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/tests/test_structured_output_with_mcp.py +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
1
3
|
from uuid import UUID
|
|
2
4
|
from pydantic import BaseModel, Field, field_validator
|
|
3
5
|
from typing import List, Optional, Dict, Any
|
|
@@ -10,14 +12,20 @@ from typing import Optional, List, Dict, Literal
|
|
|
10
12
|
# -----------------------------
|
|
11
13
|
|
|
12
14
|
class Lead(BaseModel):
|
|
13
|
-
id: Optional[
|
|
15
|
+
id: Optional[UUID] = None
|
|
14
16
|
full_name: Optional[str] = None
|
|
15
17
|
first_name: Optional[str] = None
|
|
16
18
|
last_name: Optional[str] = None
|
|
17
19
|
email: Optional[str] = None
|
|
18
20
|
user_linkedin_url: Optional[str] = None
|
|
19
21
|
user_linkedin_salesnav_url: Optional[str] = None
|
|
22
|
+
organization_linkedin_url: Optional[str] = None
|
|
23
|
+
organization_linkedin_salesnav_url: Optional[str] = None
|
|
24
|
+
linkedin_follower_count: Optional[int] = None
|
|
20
25
|
primary_domain_of_organization: Optional[str] = None
|
|
26
|
+
twitter_handle: Optional[str] = None
|
|
27
|
+
twitch_handle: Optional[str] = None
|
|
28
|
+
github_handle: Optional[str] = None
|
|
21
29
|
job_title: Optional[str] = None
|
|
22
30
|
phone: Optional[str] = None
|
|
23
31
|
headline: Optional[str] = None
|
|
@@ -25,36 +33,68 @@ class Lead(BaseModel):
|
|
|
25
33
|
organization_name: Optional[str] = None
|
|
26
34
|
organization_website: Optional[str] = None
|
|
27
35
|
summary_about_lead: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
qualification_score: Optional[float] = None
|
|
38
|
+
qualification_reason: Optional[str] = None
|
|
39
|
+
revenue: Optional[str] = None
|
|
40
|
+
company_size: Optional[str] = None
|
|
41
|
+
industry: Optional[str] = None
|
|
42
|
+
|
|
43
|
+
keywords: Optional[Any] = None
|
|
44
|
+
tags: List[str] = []
|
|
45
|
+
notes: List[str] = []
|
|
46
|
+
additional_properties: Optional[Dict[str, Any]] = {}
|
|
28
47
|
workflow_stage: Optional[str] = None
|
|
29
|
-
|
|
30
|
-
engaged:
|
|
48
|
+
|
|
49
|
+
engaged: bool = False
|
|
31
50
|
last_contact: Optional[int] = None
|
|
32
|
-
additional_properties: Optional[Dict[str, Any]] = None
|
|
33
51
|
research_summary: Optional[str] = None
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
] = None
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
email_validation_status: Optional[str] = None
|
|
53
|
+
linkedin_validation_status: Optional[str] = None
|
|
54
|
+
research_status: Optional[str] = None
|
|
55
|
+
enchrichment_status: Optional[str] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@field_validator("linkedin_follower_count", mode="before")
|
|
59
|
+
@classmethod
|
|
60
|
+
def parse_linkedin_follower_count(cls, v):
|
|
61
|
+
if v is None or v == "":
|
|
62
|
+
return None
|
|
63
|
+
if isinstance(v, str):
|
|
64
|
+
v = v.strip()
|
|
65
|
+
if v == "":
|
|
66
|
+
return None
|
|
67
|
+
try:
|
|
68
|
+
return int(v)
|
|
69
|
+
except ValueError:
|
|
70
|
+
raise ValueError("linkedin_follower_count must be an integer")
|
|
71
|
+
return v
|
|
72
|
+
|
|
73
|
+
@field_validator("notes", mode="before")
|
|
55
74
|
@classmethod
|
|
56
|
-
def
|
|
57
|
-
|
|
75
|
+
def ensure_notes_list(cls, v):
|
|
76
|
+
"""Coerce notes to a list of strings.
|
|
77
|
+
Handles legacy cases where the DB may contain a scalar or JSON string.
|
|
78
|
+
"""
|
|
79
|
+
if v is None:
|
|
80
|
+
return []
|
|
81
|
+
if isinstance(v, list):
|
|
82
|
+
# Ensure all elements are strings
|
|
83
|
+
return [str(item) if not isinstance(item, str) else item for item in v]
|
|
84
|
+
if isinstance(v, str):
|
|
85
|
+
# Try to parse JSON array; if not, wrap as single-note list
|
|
86
|
+
try:
|
|
87
|
+
parsed = json.loads(v)
|
|
88
|
+
if isinstance(parsed, list):
|
|
89
|
+
return [str(item) if not isinstance(item, str) else item for item in parsed]
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
return [v]
|
|
93
|
+
# Fallback: wrap any other scalar/object as a single string entry
|
|
94
|
+
try:
|
|
95
|
+
return [json.dumps(v)]
|
|
96
|
+
except Exception:
|
|
97
|
+
return [str(v)]
|
|
58
98
|
|
|
59
99
|
|
|
60
100
|
class LeadList(BaseModel):
|
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
import logging
|
|
3
3
|
import asyncio
|
|
4
4
|
import os
|
|
5
|
+
from datetime import datetime, timedelta, timezone
|
|
5
6
|
from typing import Awaitable, Callable, Dict, List, Any, Optional
|
|
6
7
|
import requests
|
|
7
8
|
|
|
@@ -420,6 +421,112 @@ async def test_sendgrid(api_key: str) -> Dict[str, Any]:
|
|
|
420
421
|
return {"success": False, "status_code": 0, "error_message": str(e)}
|
|
421
422
|
|
|
422
423
|
|
|
424
|
+
async def test_samgov(api_key: str) -> Dict[str, Any]:
|
|
425
|
+
"""Test SAM.gov connectivity by fetching a single opportunity."""
|
|
426
|
+
|
|
427
|
+
url = "https://api.sam.gov/opportunities/v2/search"
|
|
428
|
+
now = datetime.now(timezone.utc)
|
|
429
|
+
posted_to = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
430
|
+
posted_from = (now - timedelta(days=7)).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
431
|
+
|
|
432
|
+
params = {
|
|
433
|
+
"limit": 1,
|
|
434
|
+
"offset": 0,
|
|
435
|
+
"keyword": "software",
|
|
436
|
+
"status": "active",
|
|
437
|
+
"includeCount": "true",
|
|
438
|
+
"postedFrom": posted_from,
|
|
439
|
+
"postedTo": posted_to,
|
|
440
|
+
"api_key": api_key,
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
|
|
445
|
+
|
|
446
|
+
async def perform(request_params: Dict[str, Any]):
|
|
447
|
+
async with session.get(url, params=request_params) as response:
|
|
448
|
+
status = response.status
|
|
449
|
+
body_text = await response.text()
|
|
450
|
+
data: Optional[Dict[str, Any]] = None
|
|
451
|
+
|
|
452
|
+
try:
|
|
453
|
+
parsed = json.loads(body_text)
|
|
454
|
+
if isinstance(parsed, dict):
|
|
455
|
+
data = parsed
|
|
456
|
+
except json.JSONDecodeError:
|
|
457
|
+
data = None
|
|
458
|
+
|
|
459
|
+
return status, data, body_text
|
|
460
|
+
|
|
461
|
+
status, data, body_text = await perform(params)
|
|
462
|
+
|
|
463
|
+
def extract_error_message(payload: Optional[Dict[str, Any]], fallback_text: str) -> Optional[str]:
|
|
464
|
+
if not payload:
|
|
465
|
+
return fallback_text[:200] if fallback_text else None
|
|
466
|
+
|
|
467
|
+
errors = payload.get("errors") or payload.get("error")
|
|
468
|
+
if isinstance(errors, list):
|
|
469
|
+
parts = [
|
|
470
|
+
err.get("message") if isinstance(err, dict) else str(err)
|
|
471
|
+
for err in errors
|
|
472
|
+
if err
|
|
473
|
+
]
|
|
474
|
+
return "; ".join(parts) if parts else fallback_text[:200]
|
|
475
|
+
if isinstance(errors, dict):
|
|
476
|
+
return errors.get("message") or str(errors)
|
|
477
|
+
if errors:
|
|
478
|
+
return str(errors)
|
|
479
|
+
|
|
480
|
+
for key in ("message", "errorMessage", "detail", "description"):
|
|
481
|
+
if key in payload and payload[key]:
|
|
482
|
+
return str(payload[key])
|
|
483
|
+
|
|
484
|
+
return fallback_text[:200] if fallback_text else None
|
|
485
|
+
|
|
486
|
+
error_message = extract_error_message(data, body_text)
|
|
487
|
+
|
|
488
|
+
if status == 400 and error_message and "Invalid Date Entered" in error_message:
|
|
489
|
+
fallback_params = dict(params)
|
|
490
|
+
fallback_params["postedFrom"] = (now - timedelta(days=7)).strftime("%m/%d/%Y")
|
|
491
|
+
fallback_params["postedTo"] = now.strftime("%m/%d/%Y")
|
|
492
|
+
status, data, body_text = await perform(fallback_params)
|
|
493
|
+
error_message = extract_error_message(data, body_text)
|
|
494
|
+
|
|
495
|
+
if status != 200:
|
|
496
|
+
return {
|
|
497
|
+
"success": False,
|
|
498
|
+
"status_code": status,
|
|
499
|
+
"error_message": error_message or f"SAM.gov non-200: {status}",
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if not data:
|
|
503
|
+
return {
|
|
504
|
+
"success": False,
|
|
505
|
+
"status_code": status,
|
|
506
|
+
"error_message": "SAM.gov returned invalid JSON response.",
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if data.get("errors"):
|
|
510
|
+
return {
|
|
511
|
+
"success": False,
|
|
512
|
+
"status_code": status,
|
|
513
|
+
"error_message": extract_error_message(data, body_text) or "SAM.gov reported errors.",
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if data.get("opportunitiesData") or data.get("totalRecords") is not None:
|
|
517
|
+
return {"success": True, "status_code": status, "error_message": None}
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
"success": False,
|
|
521
|
+
"status_code": status,
|
|
522
|
+
"error_message": "Unexpected SAM.gov response payload.",
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
except Exception as e:
|
|
526
|
+
logger.error(f"SAM.gov test failed: {e}")
|
|
527
|
+
return {"success": False, "status_code": 0, "error_message": str(e)}
|
|
528
|
+
|
|
529
|
+
|
|
423
530
|
async def test_salesforce(
|
|
424
531
|
username: str,
|
|
425
532
|
password: str,
|
|
@@ -1086,8 +1193,9 @@ async def test_connectivity(tool_config: List[Dict[str, Any]]) -> Dict[str, Dict
|
|
|
1086
1193
|
"clay": test_clay,
|
|
1087
1194
|
"mcpServer": test_mcp_server,
|
|
1088
1195
|
"slack": test_slack,
|
|
1089
|
-
"mailgun":
|
|
1196
|
+
"mailgun": test_mailgun,
|
|
1090
1197
|
"sendgrid": test_sendgrid,
|
|
1198
|
+
"samgov": test_samgov,
|
|
1091
1199
|
}
|
|
1092
1200
|
|
|
1093
1201
|
results: Dict[str, Dict[str, Any]] = {}
|
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
import unittest
|
|
3
3
|
from unittest.mock import AsyncMock, patch
|
|
4
4
|
|
|
5
|
-
from src.dhisana.utils.test_connect import test_connectivity
|
|
5
|
+
from src.dhisana.utils.test_connect import test_connectivity as run_test_connectivity
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class TestConnectivity(unittest.TestCase):
|
|
@@ -17,7 +17,7 @@ class TestConnectivity(unittest.TestCase):
|
|
|
17
17
|
]
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
|
-
result = await
|
|
20
|
+
result = await run_test_connectivity(tool_config)
|
|
21
21
|
self.assertIn('jinaai', result)
|
|
22
22
|
self.assertTrue(result['jinaai']['success'])
|
|
23
23
|
asyncio.run(runner())
|
|
@@ -33,7 +33,7 @@ class TestConnectivity(unittest.TestCase):
|
|
|
33
33
|
]
|
|
34
34
|
}
|
|
35
35
|
]
|
|
36
|
-
result = await
|
|
36
|
+
result = await run_test_connectivity(tool_config)
|
|
37
37
|
self.assertIn('firecrawl', result)
|
|
38
38
|
self.assertTrue(result['firecrawl']['success'])
|
|
39
39
|
asyncio.run(runner())
|
|
@@ -57,11 +57,30 @@ class TestConnectivity(unittest.TestCase):
|
|
|
57
57
|
]
|
|
58
58
|
}
|
|
59
59
|
]
|
|
60
|
-
result = await
|
|
60
|
+
result = await run_test_connectivity(tool_config)
|
|
61
61
|
self.assertIn('salesforce', result)
|
|
62
62
|
self.assertTrue(result['salesforce']['success'])
|
|
63
63
|
asyncio.run(runner())
|
|
64
64
|
|
|
65
|
+
def test_samgov_dispatch(self):
|
|
66
|
+
async def runner():
|
|
67
|
+
with patch(
|
|
68
|
+
'src.dhisana.utils.test_connect.test_samgov',
|
|
69
|
+
new=AsyncMock(return_value={'success': True, 'status_code': 200, 'error_message': None})
|
|
70
|
+
):
|
|
71
|
+
tool_config = [
|
|
72
|
+
{
|
|
73
|
+
'name': 'samgov',
|
|
74
|
+
'configuration': [
|
|
75
|
+
{'name': 'apiKey', 'value': 'dummy'}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
result = await run_test_connectivity(tool_config)
|
|
80
|
+
self.assertIn('samgov', result)
|
|
81
|
+
self.assertTrue(result['samgov']['success'])
|
|
82
|
+
asyncio.run(runner())
|
|
83
|
+
|
|
65
84
|
|
|
66
85
|
if __name__ == '__main__':
|
|
67
86
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/check_email_validity_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/check_linkedin_url_validity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/compose_three_step_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/extract_email_content_for_llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/generate_linkedin_connect_message.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openai_assistant_and_file_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/openapi_tool/openapi_tool.py
RENAMED
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/parse_linkedin_messages_txt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serarch_router_local_business.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev218 → dhisana-0.0.1.dev219}/src/dhisana/utils/serpapi_local_business_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|