v3 : socle Rust (moteur pur, accès trustinWallet, serveur) ; maquette Nuxt rangée dans maquette/
- crates/ld-engine : formule inertielle (parité avec la maquette, garde C), coquille / changement de fond, assistant d'édition à règles, composition d'un document sous vote permanent (adoptions, aperçus, rivales). - crates/trustin-access : qui voit, qui ouvre, qui gère les accès. - crates/ld-server : API axum + SQLite, connexion Ğ1 (client OAuth2 de connect.monnaie-libre.fr), invitations, entrée en vigueur automatique, signalements de contradiction, traductions éditoriales, publication git, taille de la toile lue sur la chaîne. - seeds/acte-engagement-certification.json : 33 articles, fr + en + es ; en vigueur : préambule, E1, E2 (seed a minima). - maquette/ : frontend, backend et docker v2 déplacés tels quels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Request schemas ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class ChallengeRequest(BaseModel):
|
||||
"""Request a challenge nonce for Ed25519 authentication."""
|
||||
|
||||
address: str = Field(..., min_length=1, max_length=64, description="Duniter V2 SS58 address")
|
||||
|
||||
|
||||
class VerifyRequest(BaseModel):
|
||||
"""Submit the signed challenge to obtain a session token."""
|
||||
|
||||
address: str = Field(..., min_length=1, max_length=64)
|
||||
signature: str = Field(..., description="Ed25519 signature of the challenge (hex)")
|
||||
challenge: str = Field(..., description="The challenge string that was signed")
|
||||
|
||||
|
||||
# ── Response schemas ─────────────────────────────────────────────
|
||||
|
||||
|
||||
class ChallengeResponse(BaseModel):
|
||||
"""Returned after requesting a challenge."""
|
||||
|
||||
challenge: str
|
||||
expires_at: datetime
|
||||
|
||||
|
||||
class IdentityOut(BaseModel):
|
||||
"""Public representation of a Duniter identity."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
address: str
|
||||
display_name: str | None = None
|
||||
wot_status: str
|
||||
is_smith: bool
|
||||
is_techcomm: bool
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
"""Returned after successful challenge verification."""
|
||||
|
||||
token: str
|
||||
identity: IdentityOut
|
||||
@@ -0,0 +1,91 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Decision ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class DecisionStepCreate(BaseModel):
|
||||
"""Payload for creating a step within a decision process."""
|
||||
|
||||
step_order: int = Field(..., ge=0)
|
||||
step_type: str = Field(..., max_length=32, description="qualification, review, vote, execution, reporting")
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class DecisionStepOut(BaseModel):
|
||||
"""Full decision step representation."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
decision_id: UUID
|
||||
step_order: int
|
||||
step_type: str
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
status: str
|
||||
vote_session_id: UUID | None = None
|
||||
outcome: str | None = None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class DecisionCreate(BaseModel):
|
||||
"""Payload for creating a new decision."""
|
||||
|
||||
title: str = Field(..., min_length=1, max_length=256)
|
||||
description: str | None = None
|
||||
context: str | None = None
|
||||
decision_type: str = Field(..., max_length=64, description="runtime_upgrade, document_change, mandate_vote, custom")
|
||||
voting_protocol_id: UUID | None = None
|
||||
|
||||
|
||||
class DecisionUpdate(BaseModel):
|
||||
"""Partial update for a decision."""
|
||||
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
description: str | None = None
|
||||
status: str | None = Field(default=None, max_length=32)
|
||||
voting_protocol_id: UUID | None = None
|
||||
|
||||
|
||||
class DecisionOut(BaseModel):
|
||||
"""Full decision representation returned by the API."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
title: str
|
||||
description: str | None = None
|
||||
context: str | None = None
|
||||
decision_type: str
|
||||
status: str
|
||||
voting_protocol_id: UUID | None = None
|
||||
created_by_id: UUID | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
steps: list[DecisionStepOut] = Field(default_factory=list)
|
||||
|
||||
|
||||
class DecisionAdvanceOut(BaseModel):
|
||||
"""Output after advancing a decision through its workflow."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
title: str
|
||||
description: str | None = None
|
||||
context: str | None = None
|
||||
decision_type: str
|
||||
status: str
|
||||
voting_protocol_id: UUID | None = None
|
||||
created_by_id: UUID | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
steps: list[DecisionStepOut] = Field(default_factory=list)
|
||||
message: str = Field(..., description="Message decrivant l'avancement effectue")
|
||||
@@ -0,0 +1,184 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Document ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class DocumentCreate(BaseModel):
|
||||
"""Payload for creating a new reference document."""
|
||||
|
||||
slug: str = Field(..., min_length=1, max_length=128)
|
||||
title: str = Field(..., min_length=1, max_length=256)
|
||||
doc_type: str = Field(..., max_length=64, description="licence, engagement, reglement, constitution")
|
||||
description: str | None = None
|
||||
version: str | None = Field(default="0.1.0", max_length=32)
|
||||
|
||||
|
||||
class DocumentUpdate(BaseModel):
|
||||
"""Partial update for a document."""
|
||||
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
status: str | None = Field(default=None, max_length=32)
|
||||
description: str | None = None
|
||||
version: str | None = Field(default=None, max_length=32)
|
||||
|
||||
|
||||
class DocumentOut(BaseModel):
|
||||
"""Full document representation returned by the API."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
slug: str
|
||||
title: str
|
||||
doc_type: str
|
||||
version: str
|
||||
status: str
|
||||
description: str | None = None
|
||||
ipfs_cid: str | None = None
|
||||
chain_anchor: str | None = None
|
||||
genesis_json: str | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
items_count: int = Field(default=0, description="Number of items in this document")
|
||||
|
||||
|
||||
# ── Document Item ────────────────────────────────────────────────
|
||||
|
||||
|
||||
class DocumentItemCreate(BaseModel):
|
||||
"""Payload for creating a document item (clause, rule, etc.)."""
|
||||
|
||||
position: str = Field(..., max_length=16, description='Hierarchical position e.g. "1", "1.1", "3.2"')
|
||||
item_type: str = Field(default="clause", max_length=32, description="clause, rule, verification, preamble, section, genesis")
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
current_text: str = Field(..., min_length=1)
|
||||
voting_protocol_id: UUID | None = None
|
||||
section_tag: str | None = Field(default=None, max_length=64)
|
||||
inertia_preset: str = Field(default="standard", max_length=16)
|
||||
is_permanent_vote: bool = True
|
||||
|
||||
|
||||
class DocumentItemUpdate(BaseModel):
|
||||
"""Partial update for a document item."""
|
||||
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
current_text: str | None = Field(default=None, min_length=1)
|
||||
position: str | None = Field(default=None, max_length=16)
|
||||
item_type: str | None = Field(default=None, max_length=32)
|
||||
|
||||
|
||||
class DocumentItemOut(BaseModel):
|
||||
"""Full document item representation."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
document_id: UUID
|
||||
position: str
|
||||
item_type: str
|
||||
title: str | None = None
|
||||
current_text: str
|
||||
voting_protocol_id: UUID | None = None
|
||||
sort_order: int
|
||||
section_tag: str | None = None
|
||||
inertia_preset: str = "standard"
|
||||
is_permanent_vote: bool = True
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class DocumentItemFullOut(BaseModel):
|
||||
"""Document item with its full version history."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
document_id: UUID
|
||||
position: str
|
||||
item_type: str
|
||||
title: str | None = None
|
||||
current_text: str
|
||||
voting_protocol_id: UUID | None = None
|
||||
sort_order: int
|
||||
section_tag: str | None = None
|
||||
inertia_preset: str = "standard"
|
||||
is_permanent_vote: bool = True
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
versions: list[ItemVersionOut] = Field(default_factory=list)
|
||||
|
||||
|
||||
class DocumentFullOut(BaseModel):
|
||||
"""Document with full items list (not just count)."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
slug: str
|
||||
title: str
|
||||
doc_type: str
|
||||
version: str
|
||||
status: str
|
||||
description: str | None = None
|
||||
ipfs_cid: str | None = None
|
||||
chain_anchor: str | None = None
|
||||
genesis_json: str | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
items: list[DocumentItemOut] = Field(default_factory=list)
|
||||
|
||||
|
||||
# ── Item Reorder ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
class ItemReorderEntry(BaseModel):
|
||||
"""A single item reorder entry."""
|
||||
|
||||
item_id: UUID
|
||||
sort_order: int = Field(..., ge=0)
|
||||
|
||||
|
||||
class ItemReorderRequest(BaseModel):
|
||||
"""Payload for reordering items in a document."""
|
||||
|
||||
items: list[ItemReorderEntry]
|
||||
|
||||
|
||||
# ── Item Version ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
class ItemVersionCreate(BaseModel):
|
||||
"""Payload for proposing a new version of a document item."""
|
||||
|
||||
proposed_text: str = Field(..., min_length=1)
|
||||
rationale: str | None = None
|
||||
|
||||
|
||||
class ItemVersionOut(BaseModel):
|
||||
"""Full item version representation."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
item_id: UUID
|
||||
proposed_text: str
|
||||
diff_text: str | None = None
|
||||
rationale: str | None = None
|
||||
status: str
|
||||
decision_id: UUID | None = None
|
||||
proposed_by_id: UUID | None = None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
# ── Forward reference resolution ─────────────────────────────────
|
||||
# DocumentItemFullOut references ItemVersionOut which is defined after it.
|
||||
# With `from __future__ import annotations`, Pydantic needs explicit rebuild.
|
||||
|
||||
DocumentItemFullOut.model_rebuild()
|
||||
DocumentFullOut.model_rebuild()
|
||||
@@ -0,0 +1,46 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class GroupMemberCreate(BaseModel):
|
||||
display_name: str
|
||||
identity_id: uuid.UUID | None = None
|
||||
|
||||
|
||||
class GroupMemberOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
display_name: str
|
||||
identity_id: uuid.UUID | None
|
||||
added_at: datetime
|
||||
|
||||
|
||||
class GroupCreate(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class GroupOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
description: str | None
|
||||
organization_id: uuid.UUID | None
|
||||
created_at: datetime
|
||||
members: list[GroupMemberOut] = []
|
||||
|
||||
|
||||
class GroupSummary(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
description: str | None
|
||||
organization_id: uuid.UUID | None
|
||||
member_count: int = 0
|
||||
@@ -0,0 +1,86 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Mandate Step ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
class MandateStepCreate(BaseModel):
|
||||
step_order: int = Field(..., ge=0)
|
||||
step_type: str = Field(..., max_length=32)
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class MandateStepOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
mandate_id: UUID
|
||||
step_order: int
|
||||
step_type: str
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
status: str
|
||||
vote_session_id: UUID | None = None
|
||||
outcome: str | None = None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
# ── Mandate ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class MandateCreate(BaseModel):
|
||||
title: str = Field(..., min_length=1, max_length=256)
|
||||
origin_id: UUID | None = None
|
||||
description: str | None = None
|
||||
mandate_type: str = Field(..., max_length=64)
|
||||
nomination_mode: str = Field(
|
||||
default="postpone",
|
||||
description="auto (auto-assign author), collective, postpone",
|
||||
)
|
||||
decision_id: UUID | None = None
|
||||
starts_at: datetime | None = None
|
||||
ends_at: datetime | None = None
|
||||
|
||||
|
||||
class MandateUpdate(BaseModel):
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
origin_id: UUID | None = None
|
||||
description: str | None = None
|
||||
mandate_type: str | None = Field(default=None, max_length=64)
|
||||
decision_id: UUID | None = None
|
||||
starts_at: datetime | None = None
|
||||
ends_at: datetime | None = None
|
||||
|
||||
|
||||
class MandateAssignRequest(BaseModel):
|
||||
mandatee_id: UUID = Field(..., description="UUID de l'identite Duniter du mandataire")
|
||||
|
||||
|
||||
class MandateOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
title: str
|
||||
origin_id: UUID | None = None
|
||||
origin_display_name: str | None = None
|
||||
description: str | None = None
|
||||
mandate_type: str
|
||||
status: str
|
||||
mandatee_id: UUID | None = None
|
||||
mandatee_display_name: str | None = None
|
||||
decision_id: UUID | None = None
|
||||
starts_at: datetime | None = None
|
||||
ends_at: datetime | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
steps: list[MandateStepOut] = Field(default_factory=list)
|
||||
|
||||
|
||||
class MandateAdvanceOut(MandateOut):
|
||||
message: str = Field(..., description="Message decrivant l'avancement effectue")
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Pydantic v2 schemas for organizations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class OrganizationOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
slug: str
|
||||
org_type: str
|
||||
is_transparent: bool
|
||||
color: str | None
|
||||
icon: str | None
|
||||
description: str | None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class OrganizationCreate(BaseModel):
|
||||
name: str
|
||||
slug: str
|
||||
org_type: str = "community"
|
||||
is_transparent: bool = False
|
||||
color: str | None = None
|
||||
icon: str | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class OrgMemberOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: uuid.UUID
|
||||
org_id: uuid.UUID
|
||||
identity_id: uuid.UUID
|
||||
role: str
|
||||
created_at: datetime
|
||||
@@ -0,0 +1,141 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Formula Config ───────────────────────────────────────────────
|
||||
|
||||
|
||||
class FormulaConfigCreate(BaseModel):
|
||||
"""Payload for creating a WoT threshold formula configuration."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=128)
|
||||
description: str | None = None
|
||||
|
||||
# WoT threshold params
|
||||
duration_days: int = Field(default=30, ge=1, description="Duration of the vote in days")
|
||||
majority_pct: int = Field(default=50, ge=1, le=100, description="Majority percentage required")
|
||||
base_exponent: float = Field(default=0.1, ge=0.0, le=1.0, description="Base exponent B in the formula")
|
||||
gradient_exponent: float = Field(default=0.2, ge=0.0, le=2.0, description="Gradient exponent G in the formula")
|
||||
constant_base: float = Field(default=0.0, ge=0.0, le=1.0, description="Constant base C in the formula")
|
||||
|
||||
# Smith criterion
|
||||
smith_exponent: float | None = Field(default=None, ge=0.0, le=1.0, description="Smith criterion exponent S")
|
||||
|
||||
# TechComm criterion
|
||||
techcomm_exponent: float | None = Field(default=None, ge=0.0, le=1.0, description="TechComm criterion exponent T")
|
||||
|
||||
# Nuanced vote
|
||||
nuanced_min_participants: int | None = Field(default=None, ge=0, description="Minimum participants for nuanced vote")
|
||||
nuanced_threshold_pct: int | None = Field(default=None, ge=0, le=100, description="Threshold percentage for nuanced vote")
|
||||
|
||||
|
||||
class FormulaConfigUpdate(BaseModel):
|
||||
"""Partial update payload for a formula configuration (all fields optional)."""
|
||||
|
||||
name: str | None = Field(default=None, min_length=1, max_length=128)
|
||||
description: str | None = None
|
||||
|
||||
duration_days: int | None = Field(default=None, ge=1)
|
||||
majority_pct: int | None = Field(default=None, ge=1, le=100)
|
||||
base_exponent: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
gradient_exponent: float | None = Field(default=None, ge=0.0, le=2.0)
|
||||
constant_base: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
|
||||
smith_exponent: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
techcomm_exponent: float | None = Field(default=None, ge=0.0, le=1.0)
|
||||
|
||||
nuanced_min_participants: int | None = Field(default=None, ge=0)
|
||||
nuanced_threshold_pct: int | None = Field(default=None, ge=0, le=100)
|
||||
|
||||
|
||||
class FormulaConfigOut(BaseModel):
|
||||
"""Full formula configuration representation."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
name: str
|
||||
description: str | None = None
|
||||
duration_days: int
|
||||
majority_pct: int
|
||||
base_exponent: float
|
||||
gradient_exponent: float
|
||||
constant_base: float
|
||||
smith_exponent: float | None = None
|
||||
techcomm_exponent: float | None = None
|
||||
nuanced_min_participants: int | None = None
|
||||
nuanced_threshold_pct: int | None = None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
# ── Voting Protocol ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class VotingProtocolCreate(BaseModel):
|
||||
"""Payload for creating a voting protocol."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=128)
|
||||
description: str | None = None
|
||||
vote_type: str = Field(..., max_length=32, description="binary, nuanced")
|
||||
formula_config_id: UUID = Field(..., description="Reference to the formula configuration")
|
||||
mode_params: str | None = Field(default=None, max_length=64, description='e.g. "D30M50B.1G.2T.1"')
|
||||
is_meta_governed: bool = Field(default=False, description="Whether this protocol is itself governed by meta-vote")
|
||||
|
||||
|
||||
class VotingProtocolUpdate(BaseModel):
|
||||
"""Partial update payload for a voting protocol (meta-governance)."""
|
||||
|
||||
name: str | None = Field(default=None, min_length=1, max_length=128)
|
||||
description: str | None = None
|
||||
vote_type: str | None = Field(default=None, max_length=32)
|
||||
mode_params: str | None = Field(default=None, max_length=64)
|
||||
is_meta_governed: bool | None = None
|
||||
|
||||
|
||||
class VotingProtocolOut(BaseModel):
|
||||
"""Full voting protocol representation including formula config."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
name: str
|
||||
description: str | None = None
|
||||
vote_type: str
|
||||
formula_config_id: UUID
|
||||
mode_params: str | None = None
|
||||
is_meta_governed: bool
|
||||
created_at: datetime
|
||||
formula_config: FormulaConfigOut
|
||||
|
||||
|
||||
# ── Formula Simulation ───────────────────────────────────────────
|
||||
|
||||
|
||||
class FormulaSimulationRequest(BaseModel):
|
||||
"""Request payload for simulating a WoT threshold formula computation."""
|
||||
|
||||
wot_size: int = Field(..., ge=1, description="Size of the WoT (eligible voter corpus)")
|
||||
total_votes: int = Field(..., ge=0, description="Number of votes cast (for + against)")
|
||||
majority_pct: int = Field(default=50, ge=1, le=100, description="Majority percentage M")
|
||||
base_exponent: float = Field(default=0.1, ge=0.0, le=1.0, description="Base exponent B")
|
||||
gradient_exponent: float = Field(default=0.2, ge=0.0, le=2.0, description="Gradient exponent G")
|
||||
constant_base: float = Field(default=0.0, ge=0.0, le=1.0, description="Constant base C")
|
||||
smith_wot_size: int | None = Field(default=None, ge=1, description="Smith sub-WoT size")
|
||||
smith_exponent: float | None = Field(default=None, ge=0.0, le=1.0, description="Smith exponent S")
|
||||
techcomm_size: int | None = Field(default=None, ge=1, description="TechComm size")
|
||||
techcomm_exponent: float | None = Field(default=None, ge=0.0, le=1.0, description="TechComm exponent T")
|
||||
|
||||
|
||||
class FormulaSimulationResult(BaseModel):
|
||||
"""Result of a formula simulation."""
|
||||
|
||||
wot_threshold: int
|
||||
smith_threshold: int | None = None
|
||||
techcomm_threshold: int | None = None
|
||||
participation_rate: float
|
||||
required_ratio: float
|
||||
inertia_factor: float
|
||||
@@ -0,0 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class QualifyRequest(BaseModel):
|
||||
within_mandate: bool = False
|
||||
affected_count: int | None = Field(default=None, ge=2, description="Nombre de personnes concernées (minimum 2)")
|
||||
is_structural: bool = False
|
||||
context_description: str | None = Field(default=None, max_length=2000)
|
||||
|
||||
|
||||
class QualifyResponse(BaseModel):
|
||||
decision_type: str
|
||||
process: str
|
||||
recommended_modalities: list[str]
|
||||
recommend_onchain: bool
|
||||
onchain_reason: str | None
|
||||
confidence: str
|
||||
collective_available: bool
|
||||
record_in_observatory: bool
|
||||
reasons: list[str]
|
||||
|
||||
|
||||
class QualificationProtocolOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
name: str
|
||||
description: str | None = None
|
||||
small_group_max: int
|
||||
collective_wot_min: int
|
||||
default_modalities: list[str]
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class QualificationProtocolCreate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=128)
|
||||
description: str | None = None
|
||||
small_group_max: int = Field(default=5, ge=1)
|
||||
collective_wot_min: int = Field(default=50, ge=1)
|
||||
default_modalities: list[str] = Field(
|
||||
default=["vote_wot", "vote_smith", "consultation_avis", "election"]
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Sanctuary Entry ──────────────────────────────────────────────
|
||||
|
||||
|
||||
class SanctuaryEntryCreate(BaseModel):
|
||||
"""Payload for creating a new sanctuary entry (IPFS + chain anchor)."""
|
||||
|
||||
entry_type: str = Field(..., max_length=64, description="document, decision, vote_result")
|
||||
reference_id: UUID = Field(..., description="ID of the referenced entity")
|
||||
title: str | None = Field(default=None, max_length=256)
|
||||
content_hash: str = Field(..., max_length=128, description="SHA-256 hash of the content")
|
||||
|
||||
|
||||
class SanctuaryEntryOut(BaseModel):
|
||||
"""Full sanctuary entry representation."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
entry_type: str
|
||||
reference_id: UUID
|
||||
title: str | None = None
|
||||
content_hash: str
|
||||
ipfs_cid: str | None = None
|
||||
chain_tx_hash: str | None = None
|
||||
chain_block: int | None = None
|
||||
metadata_json: str | None = None
|
||||
created_at: datetime
|
||||
@@ -0,0 +1,157 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
# ── Vote Session ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
class VoteSessionCreate(BaseModel):
|
||||
"""Payload for opening a new vote session."""
|
||||
|
||||
decision_id: UUID | None = None
|
||||
item_version_id: UUID | None = None
|
||||
voting_protocol_id: UUID = Field(..., description="ID of the voting protocol to apply")
|
||||
|
||||
|
||||
class VoteSessionOut(BaseModel):
|
||||
"""Full vote session representation including tallies."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
decision_id: UUID | None = None
|
||||
item_version_id: UUID | None = None
|
||||
voting_protocol_id: UUID
|
||||
|
||||
# Snapshot at session start
|
||||
wot_size: int
|
||||
smith_size: int
|
||||
techcomm_size: int
|
||||
|
||||
# Dates
|
||||
starts_at: datetime
|
||||
ends_at: datetime
|
||||
|
||||
# Status
|
||||
status: str
|
||||
|
||||
# Tallies
|
||||
votes_for: int
|
||||
votes_against: int
|
||||
votes_total: int
|
||||
smith_votes_for: int
|
||||
techcomm_votes_for: int
|
||||
threshold_required: float
|
||||
result: str | None = None
|
||||
|
||||
# Chain recording
|
||||
chain_recorded: bool
|
||||
chain_tx_hash: str | None = None
|
||||
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class VoteSessionListOut(BaseModel):
|
||||
"""Lighter vote session representation for list endpoints (no nested votes)."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
decision_id: UUID | None = None
|
||||
item_version_id: UUID | None = None
|
||||
voting_protocol_id: UUID
|
||||
|
||||
# Snapshot at session start
|
||||
wot_size: int
|
||||
smith_size: int
|
||||
techcomm_size: int
|
||||
|
||||
# Dates
|
||||
starts_at: datetime
|
||||
ends_at: datetime
|
||||
|
||||
# Status
|
||||
status: str
|
||||
|
||||
# Tallies
|
||||
votes_for: int
|
||||
votes_against: int
|
||||
votes_total: int
|
||||
threshold_required: float
|
||||
result: str | None = None
|
||||
|
||||
created_at: datetime
|
||||
|
||||
|
||||
# ── Threshold Details ────────────────────────────────────────────
|
||||
|
||||
|
||||
class ThresholdDetailOut(BaseModel):
|
||||
"""Detailed threshold computation result for a vote session."""
|
||||
|
||||
wot_threshold: int
|
||||
smith_threshold: int | None = None
|
||||
techcomm_threshold: int | None = None
|
||||
votes_for: int
|
||||
votes_against: int
|
||||
votes_total: int
|
||||
wot_passed: bool
|
||||
smith_passed: bool | None = None
|
||||
techcomm_passed: bool | None = None
|
||||
overall_passed: bool
|
||||
participation_rate: float
|
||||
formula_params: dict
|
||||
|
||||
|
||||
# ── Vote Result ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
class VoteResultOut(BaseModel):
|
||||
"""Structured vote result response."""
|
||||
|
||||
session_id: UUID
|
||||
result: str | None = None # adopted, rejected
|
||||
threshold_required: float
|
||||
votes_for: int
|
||||
votes_against: int
|
||||
votes_total: int
|
||||
adopted: bool
|
||||
nuanced_breakdown: dict | None = None # for nuanced votes
|
||||
|
||||
|
||||
# ── Vote ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class VoteCreate(BaseModel):
|
||||
"""Payload for casting a vote (with cryptographic proof)."""
|
||||
|
||||
session_id: UUID
|
||||
vote_value: str = Field(..., max_length=32, description="for, against, or nuanced level")
|
||||
nuanced_level: int | None = Field(default=None, ge=0, le=5, description="0-5 for nuanced votes")
|
||||
comment: str | None = None
|
||||
signature: str = Field(..., description="Ed25519 signature of signed_payload")
|
||||
signed_payload: str = Field(..., description="The exact payload that was signed")
|
||||
|
||||
|
||||
class VoteOut(BaseModel):
|
||||
"""Full vote representation."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
session_id: UUID
|
||||
voter_id: UUID
|
||||
vote_value: str
|
||||
nuanced_level: int | None = None
|
||||
comment: str | None = None
|
||||
signature: str
|
||||
signed_payload: str
|
||||
voter_wot_status: str
|
||||
voter_is_smith: bool
|
||||
voter_is_techcomm: bool
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
Reference in New Issue
Block a user