This commit is contained in:
oliver
2026-08-28 10:11:11 -03:00
parent f533ff8e36
commit 585951c8ce
1380 changed files with 1540268 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
"""Data models for Odoo Directory."""
from .schemas import (
Region,
Country,
City,
Tag,
Company,
CompanyTag,
CityTag,
CountryTag,
ExecutionPlan,
PlanStep,
StepStatus,
TagCategory,
)
__all__ = [
"Region",
"Country",
"City",
"Tag",
"Company",
"CompanyTag",
"CityTag",
"CountryTag",
"ExecutionPlan",
"PlanStep",
"StepStatus",
"TagCategory",
]
+244
View File
@@ -0,0 +1,244 @@
"""
Pydantic models for Odoo Directory data structures.
"""
from datetime import datetime
from enum import Enum
from typing import Optional, List, Dict, Any
from uuid import UUID
from pydantic import BaseModel, Field, HttpUrl
class TagCategory(str, Enum):
"""Tag category types."""
SERVICE = "service"
INDUSTRY = "industry"
MODULE = "module"
PARTNER_LEVEL = "partner_level"
class StepStatus(str, Enum):
"""Execution step status."""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
class Region(BaseModel):
"""Geographic region model."""
id: Optional[UUID] = None
name: str
slug: str
created_at: Optional[datetime] = None
class Config:
from_attributes = True
class Country(BaseModel):
"""Country model."""
id: Optional[UUID] = None
region_id: Optional[UUID] = None
name: str
name_local: Optional[str] = None
code: str # ISO 3166-1 alpha-2
slug: str
language: str # Primary language code
created_at: Optional[datetime] = None
class Config:
from_attributes = True
class City(BaseModel):
"""City model."""
id: Optional[UUID] = None
country_id: Optional[UUID] = None
name: str
name_local: Optional[str] = None
slug: str
state: Optional[str] = None
population: Optional[int] = None
latitude: Optional[float] = None
longitude: Optional[float] = None
created_at: Optional[datetime] = None
class Config:
from_attributes = True
class Tag(BaseModel):
"""Tag model for services, industries, modules, partner levels."""
id: Optional[UUID] = None
category: TagCategory
name: str
slug: str
description: Optional[str] = None
created_at: Optional[datetime] = None
class Config:
from_attributes = True
class Company(BaseModel):
"""Odoo partner company model."""
id: Optional[UUID] = None
city_id: Optional[UUID] = None
# Basic info (from Google Maps)
name: str
slug: str
google_place_id: Optional[str] = None
address: Optional[str] = None
phone: Optional[str] = None
website: Optional[str] = None
email: Optional[str] = None
# Google Maps data
rating: Optional[float] = None
review_count: Optional[int] = None
latitude: Optional[float] = None
longitude: Optional[float] = None
# Enriched data (from Firecrawl)
website_markdown: Optional[str] = None
logo_url: Optional[str] = None
# Generated content (from Haiku)
description: Optional[str] = None
content_html: Optional[str] = None
meta_title: Optional[str] = None
meta_description: Optional[str] = None
# Status tracking
status: str = "scraped" # scraped, enriched, content_generated, exported
last_scraped_at: Optional[datetime] = None
last_enriched_at: Optional[datetime] = None
last_content_at: Optional[datetime] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class CompanyTag(BaseModel):
"""Junction table for company-tag relationship."""
company_id: UUID
tag_id: UUID
confidence: Optional[float] = None # AI confidence score
created_at: Optional[datetime] = None
class Config:
from_attributes = True
class CityTag(BaseModel):
"""Aggregated tag counts per city."""
city_id: UUID
tag_id: UUID
company_count: int = 0
class Config:
from_attributes = True
class CountryTag(BaseModel):
"""Aggregated tag counts per country."""
country_id: UUID
tag_id: UUID
company_count: int = 0
city_count: int = 0
class Config:
from_attributes = True
class PlanStep(BaseModel):
"""Individual step in an execution plan."""
id: Optional[UUID] = None
plan_id: Optional[UUID] = None
step_name: str # scrape, enrich, content, export
city_id: Optional[UUID] = None
status: StepStatus = StepStatus.PENDING
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
error_message: Optional[str] = None
items_processed: int = 0
items_total: int = 0
class Config:
from_attributes = True
class ExecutionPlan(BaseModel):
"""Execution plan for processing a country/region."""
id: Optional[UUID] = None
name: str
country_code: str
cities: List[str] # List of city slugs
status: StepStatus = StepStatus.PENDING
created_at: Optional[datetime] = None
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
# Related steps
steps: List[PlanStep] = []
class Config:
from_attributes = True
# API Response models
class ApifyGoogleMapsResult(BaseModel):
"""Result from Apify Google Maps scraper."""
title: str
address: Optional[str] = None
phone: Optional[str] = None
website: Optional[str] = None
rating: Optional[float] = None
reviewsCount: Optional[int] = None
placeId: Optional[str] = None
url: Optional[str] = None
location: Optional[Dict[str, float]] = None
class Config:
extra = "ignore"
class FirecrawlResult(BaseModel):
"""Result from Firecrawl website scraper."""
markdown: Optional[str] = None
metadata: Optional[Dict[str, Any]] = None
images: Optional[List[str]] = None
class Config:
extra = "ignore"
class HaikuTagsResponse(BaseModel):
"""Response from Haiku tag extraction."""
services: List[str] = []
industries: List[str] = []
modules: List[str] = []
partner_level: Optional[str] = None
confidence: float = 0.0
class Config:
extra = "ignore"
class HaikuContentResponse(BaseModel):
"""Response from Haiku content generation."""
description: str
content_html: str
meta_title: str
meta_description: str
class Config:
extra = "ignore"