new site
This commit is contained in:
@@ -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",
|
||||
]
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user