""" Modelos Pydantic (TypeSafe AI) para o Protocolo de Auditoria Civica TecnoPopular (CTP-1.0). Define a taxonomia de governanca em dois niveis, as 5 dimensoes tecnicas e o guia acionavel. """ from datetime import datetime, timezone from enum import Enum from typing import Dict, List, Optional from pydantic import BaseModel, Field class AuditLevel(str, Enum): """Niveis de governanca e reconhecimento do ecossistema TecnoPopular.""" CATALOGADO = "catalogado" # Nivel 1: Diretorio Aberto (Elegibilidade basica de utilidade publica) AUDITADO = "auditado" # Nivel 2: Certificacao TecnoPopular (Aprovado no Protocolo CTP) EM_REVISAO = "em_revisao" # Pendencia documental ou tecnica identificada REJEITADO = "rejeitado" # Descumprimento grave de privacidade, phishing ou finalidade espuria class DimensionType(str, Enum): """As 5 dimensoes avaliadas na regua de certificacao civica.""" PROVENIENCIA_DADOS = "proveniencia_dados" # Peso 25%: TSE, IBGE, CGU, LAI PRIVACIDADE_SOBERANIA = "privacidade_e_soberania" # Peso 25%: Zero trackers comerciais, zero dados pessoais CODIGO_ABERTO_FLOSS = "codigo_aberto_floss" # Peso 20%: Repositorio publico e licenca livre ENQUADRAMENTO_REPUBLICANO = "enquadramento_republicano" # Peso 15%: George Lakoff, despolarizacao e Marco Civil ACESSIBILIDADE_DESEMPENHO = "acessibilidade_desempenho" # Peso 15%: Baixa largura de banda, WCAG AAA, zero dark patterns class ActionableGuidance(BaseModel): """Instrucao didatica e imediata para o mantenedor/voluntario elevar a nota da aplicacao.""" dimension: DimensionType what_is_evaluated: str = Field(..., description="O que o algoritmo avalia no codigo ou na pagina") why_it_matters: str = Field(..., description="Por que essa exigencia protege o cidadao ou a democracia") how_to_fix: str = Field(..., description="Passo a passo pratico de 5 minutos para resolver no codigo") class DimensionScore(BaseModel): """Pontuacao detalhada de uma dimensao especifica da auditoria.""" dimension: DimensionType weight_percentage: float = Field(..., description="Peso percentual da dimensao no score final (ex: 25.0)") raw_score: float = Field(..., ge=0.0, le=100.0, description="Nota bruta obtida na dimensao (0 a 100)") weighted_score: float = Field(..., description="Nota ponderada calculada: raw_score * (weight / 100)") is_passing: bool = Field(..., description="Indica se atingiu a nota de corte minima na dimensao") findings: List[str] = Field(default_factory=list, description="Evidencias empiricas constatadas na inspecao") recommendations: List[str] = Field(default_factory=list, description="Orientacoes tecnicas para aprimoramento") guidance: Optional[ActionableGuidance] = None class ProvenanceDetail(BaseModel): """Detalhamento tecnico da proveniencia documental de dados (Aos Fatos / LAI).""" declared_sources: List[str] = Field(default_factory=list, description="Fontes declaradas (ex: TSE, IBGE, CGU)") official_links_detected: List[str] = Field(default_factory=list, description="Hiperlinks para dominios *.gov.br / *.jus.br") has_speculative_claims: bool = False reproducibility_documented: bool = True class PrivacyDetail(BaseModel): """Detalhamento tecnico da postura de privacidade e soberania (LGPD).""" detected_commercial_trackers: List[str] = Field(default_factory=list, description="GA4, Meta Pixel, Hotjar, Clarity, etc.") collects_sensitive_pII: bool = False # Ex: formularios com CPF, e-mail obrigatorio, telefone enforces_login: bool = False # Exige conta para consulta de dados publicos has_privacy_policy: bool = False is_local_first_or_ephemeral: bool = True class FlossDetail(BaseModel): """Detalhamento tecnico de codigo aberto e auditabilidade.""" public_repository_url: Optional[str] = None spdx_license_detected: Optional[str] = None # MIT, GPL-3.0, Apache-2.0, etc. is_osi_approved: bool = False has_recent_commits: bool = True class FramingDetail(BaseModel): """Detalhamento semantico do enquadramento e conformidade republicana (Lakoff).""" affirmative_public_value: bool = True # Foco em direitos, servicos publicos e custos materiais polarizing_risk_keywords: List[str] = Field(default_factory=list) respects_civil_framework: bool = True # Conformidade com Art. 19 do Marco Civil (sem calunia/difamacao) mitigates_backfire_effect: bool = True # Foco propositivo vs reativacao reativa de fakes class PerformanceDetail(BaseModel): """Detalhamento de acessibilidade e desempenho em conexoes perifericas.""" response_time_ms: int = 0 html_payload_bytes: int = 0 is_low_bandwidth_friendly: bool = True # < 500KB de carga total inicial has_commercial_paywall: bool = False has_ad_banners: bool = False class CivicAuditReport(BaseModel): """Relatorio final emitido pelo Protocolo de Auditoria Civica TecnoPopular para uma iniciativa civica.""" tool_id: str = Field(..., description="Identificador unico (slug da ferramenta)") tool_name: str target_url: str evaluated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) evaluator_version: str = "Protocolo TecnoPopular CTP-1.0" status: AuditLevel total_score: float = Field(..., ge=0.0, le=100.0, description="Nota consolidada de 0 a 100") # Bloqueadores criticos (ex: presenca de Meta Pixel ou formulario abusivo de CPF anula a certificacao) has_critical_blocker: bool = False critical_blockers: List[str] = Field(default_factory=list) # Detalhamento por dimensao dimension_scores: Dict[DimensionType, DimensionScore] = Field(default_factory=dict) # Sub-analises especializadas provenance: ProvenanceDetail = Field(default_factory=ProvenanceDetail) privacy: PrivacyDetail = Field(default_factory=PrivacyDetail) floss: FlossDetail = Field(default_factory=FlossDetail) framing: FramingDetail = Field(default_factory=FramingDetail) performance: PerformanceDetail = Field(default_factory=PerformanceDetail) badge_label: str = Field(..., description="Rotulo legivel (ex: 'Certificado TecnoPopular' ou 'Catalogado no Diretorio')") summary_markdown: str = Field(..., description="Sintese tecnica auditavel para publicacao publica") class ToolMetadata(BaseModel): """Metadados canonicos de uma ferramenta para o catalogo.""" id: str nome: str categoria: str descricao: str url: str repo_url: Optional[str] = None licenca: Optional[str] = None fontes_primarias: List[str] = Field(default_factory=list) status_auditoria: AuditLevel = AuditLevel.CATALOGADO score_auditoria: Optional[float] = None certificado_tecnopopular: bool = False class AuditRubric(BaseModel): """Configuracao dos pesos e notas de corte da governanca TecnoPopular.""" weights: Dict[DimensionType, float] = { DimensionType.PROVENIENCIA_DADOS: 25.0, DimensionType.PRIVACIDADE_SOBERANIA: 25.0, DimensionType.CODIGO_ABERTO_FLOSS: 20.0, DimensionType.ENQUADRAMENTO_REPUBLICANO: 15.0, DimensionType.ACESSIBILIDADE_DESEMPENHO: 15.0, } # Notas de corte minimas por dimensao min_dimension_score: Dict[DimensionType, float] = { DimensionType.PROVENIENCIA_DADOS: 60.0, DimensionType.PRIVACIDADE_SOBERANIA: 80.0, # Rigor maximo em privacidade DimensionType.CODIGO_ABERTO_FLOSS: 50.0, DimensionType.ENQUADRAMENTO_REPUBLICANO: 60.0, DimensionType.ACESSIBILIDADE_DESEMPENHO: 50.0, } seal_threshold: float = 80.0 # Nota minima para receber a Certificacao TecnoPopular directory_threshold: float = 50.0 # Nota minima para manter-se elegivel no catalogo aberto