Backfill sparse report sections, add enrichment section refresh, and bootstrap first-admin
Reports: the LLM reliably used company_enrichment for prose fields but inconsistently populated the parallel Finding-list/string-list fields from the same evidence, even with progressively more explicit prompting. Add a code-level backfill (products, recent developments, financial signals, strategic initiatives, regulatory signals, risks/opportunities mirrored from SWOT, unknowns, monitoring recommendations) that only ever fills in what the model left empty, never overwrites what it produced. Enrichment tab: reorder sections (Products/Recent updates before Customers/Competitors) and add a per-section "Refresh" button that re-fetches just one of NinjaPear's six independent per-company endpoints when it came back empty - confirmed live that a data-coverage gap (e.g. Amazon returning no products) is real provider behavior, not a bug. Auth: the first account registered on a deployment with zero existing admins is now auto-promoted to admin, closing the chicken-and-egg gap where the only path to admin access was direct DB access. Self-heals if the last admin ever deletes their account. Also bumps nginx's proxy_read_timeout for api.ciagent.org to cover the enrichment refresh's synchronous funding-endpoint call (up to 5 minutes per NinjaPear's docs). Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def _register_and_login(client) -> dict[str, str]:
|
||||
@@ -252,3 +255,73 @@ def test_create_company_does_not_enqueue_enrichment_without_a_key(client):
|
||||
assert resp.status_code == 201
|
||||
mock_delay.assert_not_called()
|
||||
assert resp.json()["enrichment"] is None
|
||||
|
||||
|
||||
def test_refresh_enrichment_section_requires_authentication(client):
|
||||
resp = client.post(f"/api/v1/companies/{uuid.uuid4()}/enrichment/sections/products/refresh")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
def test_refresh_enrichment_section_rejects_an_unknown_section(client):
|
||||
headers = _register_and_login(client)
|
||||
company = _create_company(client, headers).json()
|
||||
|
||||
resp = client.post(
|
||||
f"/api/v1/companies/{company['id']}/enrichment/sections/not_a_section/refresh",
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
def test_refresh_enrichment_section_404s_without_an_enrichment_record(client):
|
||||
headers = _register_and_login(client)
|
||||
company = _create_company(client, headers).json() # no NINJAPEAR_API_KEY -> no record
|
||||
|
||||
resp = client.post(
|
||||
f"/api/v1/companies/{company['id']}/enrichment/sections/products/refresh",
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
def test_refresh_enrichment_section_is_scoped_to_owner(client):
|
||||
owner_headers = _register_and_login(client)
|
||||
other_headers = _register_and_login(client)
|
||||
company = _create_company(client, owner_headers).json()
|
||||
|
||||
resp = client.post(
|
||||
f"/api/v1/companies/{company['id']}/enrichment/sections/products/refresh",
|
||||
headers=other_headers,
|
||||
)
|
||||
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_refresh_enrichment_section_updates_just_that_section(client, db_session, settings):
|
||||
from app.enrichment.base import Product
|
||||
from app.enrichment.mock import MockEnrichmentProvider
|
||||
from app.repositories.company_repository import CompanyRepository
|
||||
from app.services.enrichment_service import enrich_company
|
||||
|
||||
headers = _register_and_login(client)
|
||||
company_id = _create_company(client, headers).json()["id"]
|
||||
|
||||
company = await CompanyRepository(db_session).get_by_id(uuid.UUID(company_id))
|
||||
await enrich_company(db_session, settings, MockEnrichmentProvider(), company)
|
||||
|
||||
class _ProductsProvider(MockEnrichmentProvider):
|
||||
async def get_products(self, name, website):
|
||||
return [Product(name="Acme Pay", description="Payments product")]
|
||||
|
||||
with patch("app.api.v1.companies.get_enrichment_provider", return_value=_ProductsProvider()):
|
||||
resp = client.post(
|
||||
f"/api/v1/companies/{company_id}/enrichment/sections/products/refresh",
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
products = resp.json()["enrichment"]["data"]["products"]
|
||||
assert products == [{"name": "Acme Pay", "description": "Payments product", "category": None}]
|
||||
|
||||
Reference in New Issue
Block a user