Refonte design : 4 humeurs, onboarding, sections avec boite a outils
- Systeme de themes adaptatifs : Peps (light chaud), Zen (light calme), Chagrine (dark violet), Grave (dark ambre) avec CSS custom properties - Dashboard d'accueil orienté onboarding avec cartes-portes et teaser boite a outils - SectionLayout reutilisable : liste + sidebar toolbox + status pills cliquables (En prepa / En vote / En vigueur / Clos) - ToolboxVignette : vignettes Contexte / Tutos / Choisir / Demarrer - Seed : Acte engagement certification + forgeron, Runtime Upgrade (decision on-chain), 3 modalites de vote (majoritaire, quadratique, permanent) - Backend adapte SQLite (Uuid portable, 204 fix, pool conditionnel) - Correction noms composants (pathPrefix: false), pinia/nuxt ^0.11 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,8 @@ class Settings(BaseSettings):
|
||||
ENVIRONMENT: str = "development" # development, staging, production
|
||||
LOG_LEVEL: str = "INFO"
|
||||
|
||||
# Database
|
||||
DATABASE_URL: str = "postgresql+asyncpg://glibredecision:change-me-in-production@localhost:5432/glibredecision"
|
||||
# Database — SQLite by default for local dev, PostgreSQL for Docker/prod
|
||||
DATABASE_URL: str = "sqlite+aiosqlite:///./glibredecision.db"
|
||||
DATABASE_POOL_SIZE: int = 20
|
||||
DATABASE_MAX_OVERFLOW: int = 10
|
||||
|
||||
|
||||
@@ -3,14 +3,25 @@ from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from app.config import settings
|
||||
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
echo=settings.ENVIRONMENT == "development",
|
||||
pool_size=settings.DATABASE_POOL_SIZE,
|
||||
max_overflow=settings.DATABASE_MAX_OVERFLOW,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=3600,
|
||||
)
|
||||
_is_sqlite = settings.DATABASE_URL.startswith("sqlite")
|
||||
|
||||
# SQLite doesn't support pool_size/max_overflow/pool_pre_ping/pool_recycle
|
||||
if _is_sqlite:
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
echo=settings.ENVIRONMENT == "development",
|
||||
connect_args={"check_same_thread": False},
|
||||
)
|
||||
else:
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
echo=settings.ENVIRONMENT == "development",
|
||||
pool_size=settings.DATABASE_POOL_SIZE,
|
||||
max_overflow=settings.DATABASE_MAX_OVERFLOW,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=3600,
|
||||
)
|
||||
|
||||
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime, func
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy import String, DateTime, JSON, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,8 +10,8 @@ from app.database import Base
|
||||
class BlockchainCache(Base):
|
||||
__tablename__ = "blockchain_cache"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
cache_key: Mapped[str] = mapped_column(String(256), unique=True, nullable=False, index=True)
|
||||
cache_value: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
||||
cache_value: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||||
fetched_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Integer, Text, DateTime, ForeignKey, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Integer, Text, DateTime, ForeignKey, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,7 +10,7 @@ from app.database import Base
|
||||
class Decision(Base):
|
||||
__tablename__ = "decisions"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
title: Mapped[str] = mapped_column(String(256), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
context: Mapped[str | None] = mapped_column(Text)
|
||||
@@ -28,7 +27,7 @@ class Decision(Base):
|
||||
class DecisionStep(Base):
|
||||
__tablename__ = "decision_steps"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
decision_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("decisions.id"), nullable=False)
|
||||
step_order: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
step_type: Mapped[str] = mapped_column(String(32), nullable=False) # qualification, review, vote, execution, reporting
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Integer, Text, DateTime, ForeignKey, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Integer, Text, DateTime, ForeignKey, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,7 +10,7 @@ from app.database import Base
|
||||
class Document(Base):
|
||||
__tablename__ = "documents"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
slug: Mapped[str] = mapped_column(String(128), unique=True, nullable=False, index=True)
|
||||
title: Mapped[str] = mapped_column(String(256), nullable=False)
|
||||
doc_type: Mapped[str] = mapped_column(String(64), nullable=False) # licence, engagement, reglement, constitution
|
||||
@@ -29,7 +28,7 @@ class Document(Base):
|
||||
class DocumentItem(Base):
|
||||
__tablename__ = "document_items"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
document_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("documents.id"), nullable=False)
|
||||
position: Mapped[str] = mapped_column(String(16), nullable=False) # "1", "1.1", "3.2"
|
||||
item_type: Mapped[str] = mapped_column(String(32), default="clause") # clause, rule, verification, preamble, section
|
||||
@@ -47,7 +46,7 @@ class DocumentItem(Base):
|
||||
class ItemVersion(Base):
|
||||
__tablename__ = "item_versions"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
item_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("document_items.id"), nullable=False)
|
||||
proposed_text: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
diff_text: Mapped[str | None] = mapped_column(Text)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Integer, Text, DateTime, ForeignKey, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Integer, Text, DateTime, ForeignKey, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,7 +10,7 @@ from app.database import Base
|
||||
class Mandate(Base):
|
||||
__tablename__ = "mandates"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
title: Mapped[str] = mapped_column(String(256), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
mandate_type: Mapped[str] = mapped_column(String(64), nullable=False) # techcomm, smith, custom
|
||||
@@ -29,7 +28,7 @@ class Mandate(Base):
|
||||
class MandateStep(Base):
|
||||
__tablename__ = "mandate_steps"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
mandate_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("mandates.id"), nullable=False)
|
||||
step_order: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
step_type: Mapped[str] = mapped_column(String(32), nullable=False) # formulation, candidacy, vote, assignment, reporting, completion, revocation
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Integer, Float, Boolean, DateTime, ForeignKey, Text, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Integer, Float, Boolean, DateTime, ForeignKey, Text, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,7 +10,7 @@ from app.database import Base
|
||||
class FormulaConfig(Base):
|
||||
__tablename__ = "formula_configs"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
|
||||
@@ -40,7 +39,7 @@ class FormulaConfig(Base):
|
||||
class VotingProtocol(Base):
|
||||
__tablename__ = "voting_protocols"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
vote_type: Mapped[str] = mapped_column(String(32), nullable=False) # binary, nuanced
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Integer, Text, DateTime, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Integer, Text, DateTime, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,9 +10,9 @@ from app.database import Base
|
||||
class SanctuaryEntry(Base):
|
||||
__tablename__ = "sanctuary_entries"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
entry_type: Mapped[str] = mapped_column(String(64), nullable=False) # document, decision, vote_result
|
||||
reference_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
reference_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False)
|
||||
title: Mapped[str | None] = mapped_column(String(256))
|
||||
content_hash: Mapped[str] = mapped_column(String(128), nullable=False) # SHA-256
|
||||
ipfs_cid: Mapped[str | None] = mapped_column(String(128))
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Boolean, DateTime, ForeignKey, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,7 +10,7 @@ from app.database import Base
|
||||
class DuniterIdentity(Base):
|
||||
__tablename__ = "duniter_identities"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
address: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True)
|
||||
display_name: Mapped[str | None] = mapped_column(String(128))
|
||||
wot_status: Mapped[str] = mapped_column(String(32), default="unknown") # member, pending, revoked, unknown
|
||||
@@ -26,7 +25,7 @@ class DuniterIdentity(Base):
|
||||
class Session(Base):
|
||||
__tablename__ = "sessions"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False, index=True)
|
||||
identity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("duniter_identities.id"), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, Integer, Float, Boolean, Text, DateTime, ForeignKey, func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy import String, Integer, Float, Boolean, Text, DateTime, ForeignKey, Uuid, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
@@ -11,7 +10,7 @@ from app.database import Base
|
||||
class VoteSession(Base):
|
||||
__tablename__ = "vote_sessions"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
decision_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("decisions.id"))
|
||||
item_version_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("item_versions.id"))
|
||||
voting_protocol_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("voting_protocols.id"), nullable=False)
|
||||
@@ -49,7 +48,7 @@ class VoteSession(Base):
|
||||
class Vote(Base):
|
||||
__tablename__ = "votes"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
|
||||
session_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("vote_sessions.id"), nullable=False)
|
||||
voter_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("duniter_identities.id"), nullable=False)
|
||||
vote_value: Mapped[str] = mapped_column(String(32), nullable=False) # for, against, or nuanced levels
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import secrets
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import settings
|
||||
@@ -133,11 +133,11 @@ async def get_me(
|
||||
return IdentityOut.model_validate(identity)
|
||||
|
||||
|
||||
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT, response_class=Response, response_model=None)
|
||||
async def logout(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
identity: DuniterIdentity = Depends(get_current_identity),
|
||||
) -> None:
|
||||
):
|
||||
"""Invalidate the current session token.
|
||||
|
||||
Note: get_current_identity already validated the token, so we know it exists.
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -188,7 +188,7 @@ async def create_vote_session_for_step_endpoint(
|
||||
return VoteSessionOut.model_validate(session)
|
||||
|
||||
|
||||
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response, response_model=None)
|
||||
async def delete_decision(
|
||||
id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
|
||||
@@ -6,7 +6,7 @@ import difflib
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -316,7 +316,7 @@ async def update_item(
|
||||
return DocumentItemOut.model_validate(item)
|
||||
|
||||
|
||||
@router.delete("/{slug}/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete("/{slug}/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response, response_model=None)
|
||||
async def delete_item(
|
||||
slug: str,
|
||||
item_id: uuid.UUID,
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -124,7 +124,7 @@ async def update_mandate(
|
||||
return MandateOut.model_validate(mandate)
|
||||
|
||||
|
||||
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT, response_class=Response, response_model=None)
|
||||
async def delete_mandate(
|
||||
id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
|
||||
@@ -3,6 +3,7 @@ uvicorn[standard]==0.34.0
|
||||
sqlalchemy==2.0.36
|
||||
alembic==1.14.0
|
||||
asyncpg==0.30.0
|
||||
aiosqlite==0.22.1
|
||||
pydantic==2.10.3
|
||||
pydantic-settings==2.7.0
|
||||
python-multipart==0.0.18
|
||||
|
||||
186
backend/seed.py
186
backend/seed.py
@@ -101,39 +101,45 @@ async def seed_formula_configs(session: AsyncSession) -> dict[str, FormulaConfig
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed: VotingProtocols
|
||||
# Seed: VotingProtocols (premier pack de modalites)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def seed_voting_protocols(
|
||||
session: AsyncSession,
|
||||
formulas: dict[str, FormulaConfig],
|
||||
) -> dict[str, VotingProtocol]:
|
||||
"""Create the 4 base voting protocols."""
|
||||
"""Create the first pack of voting modalities (3 protocols)."""
|
||||
protocols: dict[str, dict] = {
|
||||
"Standard G1": {
|
||||
"description": "Protocole binaire standard pour la Licence G1.",
|
||||
"Vote majoritaire": {
|
||||
"description": (
|
||||
"Vote binaire a majorite simple. Le seuil d'adoption "
|
||||
"s'adapte dynamiquement au taux de participation via "
|
||||
"la formule d'inertie WoT."
|
||||
),
|
||||
"vote_type": "binary",
|
||||
"formula_config_id": formulas["Standard Licence G1"].id,
|
||||
"mode_params": "D30M50B.1G.2",
|
||||
},
|
||||
"Forgeron Smith": {
|
||||
"description": "Protocole binaire avec critere Smith pour les forgerons.",
|
||||
"Vote quadratique": {
|
||||
"description": (
|
||||
"Vote pondere par la racine carree des certifications. "
|
||||
"Reduit l'influence des gros certificateurs et favorise "
|
||||
"une participation large et diversifiee."
|
||||
),
|
||||
"vote_type": "binary",
|
||||
"formula_config_id": formulas["Forgeron avec Smith"].id,
|
||||
"mode_params": "D30M50B.1G.2S.1",
|
||||
},
|
||||
"Comite Tech": {
|
||||
"description": "Protocole binaire avec critere Comite Technique.",
|
||||
"Vote permanent": {
|
||||
"description": (
|
||||
"Vote continu sans date de fin. Le resultat evolue en "
|
||||
"temps reel avec chaque nouveau vote. Adapte aux documents "
|
||||
"de reference sous revision permanente."
|
||||
),
|
||||
"vote_type": "binary",
|
||||
"formula_config_id": formulas["Comite Tech"].id,
|
||||
"mode_params": "D30M50B.1G.2T.1",
|
||||
},
|
||||
"Vote Nuance 6 niveaux": {
|
||||
"description": "Protocole de vote nuance a 6 niveaux.",
|
||||
"vote_type": "nuanced",
|
||||
"formula_config_id": formulas["Vote Nuance"].id,
|
||||
"mode_params": None,
|
||||
},
|
||||
}
|
||||
|
||||
result: dict[str, VotingProtocol] = {}
|
||||
@@ -149,143 +155,134 @@ async def seed_voting_protocols(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed: Document - Licence G1
|
||||
# Seed: Document - Acte d'engagement certification
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
LICENCE_G1_ITEMS: list[dict] = [
|
||||
ENGAGEMENT_CERTIFICATION_ITEMS: list[dict] = [
|
||||
{
|
||||
"position": "1",
|
||||
"item_type": "preamble",
|
||||
"title": "Preambule",
|
||||
"title": "Objet",
|
||||
"sort_order": 1,
|
||||
"current_text": (
|
||||
"Licence de la monnaie libre et engagement de responsabilite. "
|
||||
"La monnaie libre G1 (June) est co-produite par ses membres."
|
||||
"Le present acte definit les engagements de tout membre de la "
|
||||
"toile de confiance qui certifie l'identite d'une autre personne "
|
||||
"dans le reseau Duniter."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "2",
|
||||
"item_type": "section",
|
||||
"title": "Avertissement TdC",
|
||||
"item_type": "clause",
|
||||
"title": "Connaissance personnelle",
|
||||
"sort_order": 2,
|
||||
"current_text": (
|
||||
"Certifier n'est pas uniquement s'assurer de l'identite unique "
|
||||
"de la personne (son unicite). C'est aussi affirmer que vous la "
|
||||
"connaissez bien et que vous saurez la joindre facilement."
|
||||
"Je certifie connaitre personnellement la personne que je "
|
||||
"certifie, l'avoir rencontree physiquement a plusieurs reprises, "
|
||||
"et pouvoir la contacter par au moins deux moyens de communication "
|
||||
"differents."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "3",
|
||||
"item_type": "clause",
|
||||
"title": "Conseils",
|
||||
"title": "Verification d'identite",
|
||||
"sort_order": 3,
|
||||
"current_text": (
|
||||
"Connaitre la personne par plusieurs moyens de communication differents "
|
||||
"(physique, electronique, etc.). Connaitre son lieu de vie principal. "
|
||||
"Avoir echange avec elle en utilisant des moyens de communication "
|
||||
"susceptibles d'identifier un humain vivant."
|
||||
"Je certifie avoir verifie que la personne n'a qu'un seul compte "
|
||||
"membre dans la toile de confiance, et que l'identite declaree "
|
||||
"correspond a une personne humaine vivante."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "4",
|
||||
"item_type": "verification",
|
||||
"title": "Verifications",
|
||||
"item_type": "clause",
|
||||
"title": "Engagement de suivi",
|
||||
"sort_order": 4,
|
||||
"current_text": (
|
||||
"De suffisamment bien connaitre la personne pour pouvoir la contacter, "
|
||||
"echanger avec elle. De s'assurer que la personne a bien le controle "
|
||||
"de son compte Duniter."
|
||||
"Je m'engage a surveiller l'activite de mes certifies et a "
|
||||
"signaler tout comportement suspect (comptes multiples, "
|
||||
"usurpation d'identite, comptes abandonnes)."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "5",
|
||||
"item_type": "rule",
|
||||
"title": "Regles TdC",
|
||||
"item_type": "verification",
|
||||
"title": "Delai entre certifications",
|
||||
"sort_order": 5,
|
||||
"current_text": (
|
||||
"Chaque membre dispose de 100 certifications possibles. "
|
||||
"Il est possible de certifier 1 nouveau membre tous les 5 jours. "
|
||||
"Un membre doit avoir au moins 5 certifications pour devenir membre. "
|
||||
"Un membre doit renouveler son adhesion tous les 2 ans."
|
||||
"Je respecte un delai minimum de reflexion de 5 jours entre "
|
||||
"chaque nouvelle certification emise."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "6",
|
||||
"item_type": "rule",
|
||||
"title": "Production DU",
|
||||
"title": "Renouvellement",
|
||||
"sort_order": 6,
|
||||
"current_text": (
|
||||
"1 DU (Dividende Universel) est produit par personne et par jour. "
|
||||
"Le DU est la monnaie de base co-produite par chaque membre."
|
||||
"Je renouvelle mes certifications avant leur expiration pour "
|
||||
"maintenir la cohesion de la toile de confiance."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "7",
|
||||
"item_type": "rule",
|
||||
"title": "Code monetaire",
|
||||
"item_type": "clause",
|
||||
"title": "Responsabilite",
|
||||
"sort_order": 7,
|
||||
"current_text": (
|
||||
"DU formule : DU(t+1) = DU(t) + c^2 * M/N. "
|
||||
"c = 4.88% / an. Le DU est re-evalue chaque equinoxe."
|
||||
"Je suis conscient que la certification engage ma responsabilite "
|
||||
"vis-a-vis de la communaute. Une certification abusive peut "
|
||||
"entrainer la perte de confiance des autres membres."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "8",
|
||||
"item_type": "clause",
|
||||
"title": "Logiciels",
|
||||
"item_type": "rule",
|
||||
"title": "Revocation",
|
||||
"sort_order": 8,
|
||||
"current_text": (
|
||||
"Les logiciels G1 doivent transmettre cette licence integralement "
|
||||
"aux utilisateurs et developper un acces libre au code source."
|
||||
),
|
||||
},
|
||||
{
|
||||
"position": "9",
|
||||
"item_type": "clause",
|
||||
"title": "Modification",
|
||||
"sort_order": 9,
|
||||
"current_text": (
|
||||
"Proposants, soutiens et votants doivent etre membres de la TdC. "
|
||||
"Toute modification de cette licence doit etre soumise au vote "
|
||||
"des membres selon le protocole en vigueur."
|
||||
"Une certification peut etre revoquee si les conditions de "
|
||||
"l'engagement ne sont plus remplies. La revocation est soumise "
|
||||
"au protocole de vote en vigueur."
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def seed_document_licence_g1(session: AsyncSession) -> Document:
|
||||
"""Create the Licence G1 document with its items."""
|
||||
async def seed_document_engagement_certification(session: AsyncSession) -> Document:
|
||||
"""Create the Acte d'engagement certification document with its items."""
|
||||
doc, created = await get_or_create(
|
||||
session,
|
||||
Document,
|
||||
"slug",
|
||||
"licence-g1",
|
||||
title="Licence G1",
|
||||
doc_type="licence",
|
||||
version="0.3.0",
|
||||
"engagement-certification",
|
||||
title="Acte d'engagement certification",
|
||||
doc_type="engagement",
|
||||
version="1.0.0",
|
||||
status="active",
|
||||
description=(
|
||||
"Licence de la monnaie libre G1 (June). "
|
||||
"Definit les regles de la toile de confiance et du Dividende Universel."
|
||||
"Acte d'engagement pour les certificateurs de la toile de confiance "
|
||||
"Duniter. Definit les obligations et responsabilites liees a la "
|
||||
"certification de nouveaux membres."
|
||||
),
|
||||
)
|
||||
print(f" Document 'Licence G1': {'created' if created else 'exists'}")
|
||||
print(f" Document 'Acte d'engagement certification': {'created' if created else 'exists'}")
|
||||
|
||||
if created:
|
||||
for item_data in LICENCE_G1_ITEMS:
|
||||
for item_data in ENGAGEMENT_CERTIFICATION_ITEMS:
|
||||
item = DocumentItem(document_id=doc.id, **item_data)
|
||||
session.add(item)
|
||||
await session.flush()
|
||||
print(f" -> {len(LICENCE_G1_ITEMS)} items created")
|
||||
print(f" -> {len(ENGAGEMENT_CERTIFICATION_ITEMS)} items created")
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed: Document - Engagement Forgeron v2.0.0
|
||||
# Seed: Document - Acte d'engagement forgeron v2.0.0
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
FORGERON_ITEMS: list[dict] = [
|
||||
ENGAGEMENT_FORGERON_ITEMS: list[dict] = [
|
||||
{
|
||||
"position": "1",
|
||||
"item_type": "preamble",
|
||||
@@ -387,36 +384,36 @@ FORGERON_ITEMS: list[dict] = [
|
||||
]
|
||||
|
||||
|
||||
async def seed_document_forgeron(session: AsyncSession) -> Document:
|
||||
"""Create the Engagement Forgeron v2.0.0 document with its items."""
|
||||
async def seed_document_engagement_forgeron(session: AsyncSession) -> Document:
|
||||
"""Create the Acte d'engagement forgeron v2.0.0 document with its items."""
|
||||
doc, created = await get_or_create(
|
||||
session,
|
||||
Document,
|
||||
"slug",
|
||||
"engagement-forgeron",
|
||||
title="Engagement Forgeron v2.0.0",
|
||||
title="Acte d'engagement forgeron",
|
||||
doc_type="engagement",
|
||||
version="2.0.0",
|
||||
status="active",
|
||||
description=(
|
||||
"Engagement des forgerons (validateurs) pour Duniter V2. "
|
||||
"Adopte en fevrier 2026 (97 pour / 23 contre)."
|
||||
"Acte d'engagement des forgerons (validateurs de blocs) pour "
|
||||
"Duniter V2. Adopte en fevrier 2026 (97 pour / 23 contre)."
|
||||
),
|
||||
)
|
||||
print(f" Document 'Engagement Forgeron v2.0.0': {'created' if created else 'exists'}")
|
||||
print(f" Document 'Acte d'engagement forgeron': {'created' if created else 'exists'}")
|
||||
|
||||
if created:
|
||||
for item_data in FORGERON_ITEMS:
|
||||
for item_data in ENGAGEMENT_FORGERON_ITEMS:
|
||||
item = DocumentItem(document_id=doc.id, **item_data)
|
||||
session.add(item)
|
||||
await session.flush()
|
||||
print(f" -> {len(FORGERON_ITEMS)} items created")
|
||||
print(f" -> {len(ENGAGEMENT_FORGERON_ITEMS)} items created")
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Seed: Decision template - Processus Runtime Upgrade
|
||||
# Seed: Decision template - Runtime Upgrade
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
RUNTIME_UPGRADE_STEPS: list[dict] = [
|
||||
@@ -474,15 +471,16 @@ async def seed_decision_runtime_upgrade(session: AsyncSession) -> Decision:
|
||||
session,
|
||||
Decision,
|
||||
"title",
|
||||
"Processus Runtime Upgrade",
|
||||
"Runtime Upgrade",
|
||||
description=(
|
||||
"Template de decision pour les mises a jour du runtime Duniter V2. "
|
||||
"5 etapes : qualification, revue, vote, execution, suivi."
|
||||
"Processus de mise a jour du runtime Duniter V2 on-chain. "
|
||||
"Chaque upgrade suit un protocole strict en 5 etapes. "
|
||||
"(Decision on-chain)"
|
||||
),
|
||||
decision_type="runtime_upgrade",
|
||||
status="draft",
|
||||
)
|
||||
print(f" Decision 'Processus Runtime Upgrade': {'created' if created else 'exists'}")
|
||||
print(f" Decision 'Runtime Upgrade': {'created' if created else 'exists'}")
|
||||
|
||||
if created:
|
||||
for step_data in RUNTIME_UPGRADE_STEPS:
|
||||
@@ -509,16 +507,16 @@ async def run_seed():
|
||||
print("\n[1/5] Formula Configs...")
|
||||
formulas = await seed_formula_configs(session)
|
||||
|
||||
print("\n[2/5] Voting Protocols...")
|
||||
print("\n[2/5] Voting Protocols (premier pack de modalites)...")
|
||||
await seed_voting_protocols(session, formulas)
|
||||
|
||||
print("\n[3/5] Document: Licence G1...")
|
||||
await seed_document_licence_g1(session)
|
||||
print("\n[3/5] Document: Acte d'engagement certification...")
|
||||
await seed_document_engagement_certification(session)
|
||||
|
||||
print("\n[4/5] Document: Engagement Forgeron v2.0.0...")
|
||||
await seed_document_forgeron(session)
|
||||
print("\n[4/5] Document: Acte d'engagement forgeron...")
|
||||
await seed_document_engagement_forgeron(session)
|
||||
|
||||
print("\n[5/5] Decision: Processus Runtime Upgrade...")
|
||||
print("\n[5/5] Decision: Runtime Upgrade...")
|
||||
await seed_decision_runtime_upgrade(session)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
|
||||
Reference in New Issue
Block a user