CARONTE v1.0 - Plataforma de Gestão Social

This commit is contained in:
2026-02-08 23:10:32 -03:00
commit c98c806865
60 changed files with 9450 additions and 0 deletions

View File

View File

@@ -0,0 +1,38 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.core.security import hash_password, verify_password, create_access_token, get_current_user_id
from app.models.usuario import Usuario
from app.schemas.schemas import UserCreate, UserOut, Token
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/registro", response_model=UserOut)
async def registro(data: UserCreate, db: AsyncSession = Depends(get_db)):
existing = await db.execute(select(Usuario).where(Usuario.email == data.email))
if existing.scalar_one_or_none():
raise HTTPException(400, "Email já cadastrado")
user = Usuario(nome=data.nome, email=data.email, senha_hash=hash_password(data.senha), telefone=data.telefone)
db.add(user)
await db.commit()
await db.refresh(user)
return user
@router.post("/login", response_model=Token)
async def login(form: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Usuario).where(Usuario.email == form.username))
user = result.scalar_one_or_none()
if not user or not verify_password(form.password, user.senha_hash):
raise HTTPException(401, "Credenciais inválidas")
token = create_access_token({"sub": str(user.id)})
return {"access_token": token}
@router.get("/me", response_model=UserOut)
async def me(user_id: int = Depends(get_current_user_id), db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Usuario).where(Usuario.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(404, "Usuário não encontrado")
return user

View File

@@ -0,0 +1,32 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.core.security import get_current_user_id
from app.models.beneficio import Beneficio
from app.models.falecido import Falecido
from app.schemas.schemas import BeneficioOut
from app.services.beneficio_scanner import escanear_beneficios
router = APIRouter(prefix="/familias/{familia_id}/beneficios", tags=["beneficios"])
@router.get("/", response_model=list[BeneficioOut])
async def listar(familia_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Beneficio).where(Beneficio.familia_id == familia_id))
return result.scalars().all()
@router.post("/scan", response_model=list[BeneficioOut])
async def scan(familia_id: int, db: AsyncSession = Depends(get_db)):
# Delete existing and rescan
result = await db.execute(select(Beneficio).where(Beneficio.familia_id == familia_id))
for b in result.scalars().all():
await db.delete(b)
await db.commit()
result = await db.execute(select(Falecido).where(Falecido.familia_id == familia_id))
falecidos = result.scalars().all()
all_bens = []
for f in falecidos:
bens = await escanear_beneficios(db, familia_id, f)
all_bens.extend(bens)
return all_bens

View File

@@ -0,0 +1,41 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.core.security import get_current_user_id
from app.models.checklist import ChecklistItem
from app.schemas.schemas import ChecklistItemOut, ChecklistUpdateStatus
from app.services.checklist_engine import get_proximo_passo
router = APIRouter(prefix="/familias/{familia_id}/checklist", tags=["checklist"])
@router.get("/", response_model=list[ChecklistItemOut])
async def listar(familia_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(ChecklistItem).where(ChecklistItem.familia_id == familia_id).order_by(ChecklistItem.ordem)
)
return result.scalars().all()
@router.put("/{item_id}", response_model=ChecklistItemOut)
async def atualizar_status(familia_id: int, item_id: int, data: ChecklistUpdateStatus, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(ChecklistItem).where(ChecklistItem.id == item_id, ChecklistItem.familia_id == familia_id))
item = result.scalar_one_or_none()
if not item:
raise HTTPException(404, "Item não encontrado")
item.status = data.status
await db.commit()
await db.refresh(item)
return item
@router.get("/proximo", response_model=ChecklistItemOut | None)
async def proximo_passo(familia_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(ChecklistItem).where(ChecklistItem.familia_id == familia_id).order_by(ChecklistItem.ordem)
)
items = [{"id": i.id, "status": i.status, "ordem": i.ordem, "titulo": i.titulo, "descricao": i.descricao,
"fase": i.fase, "categoria": i.categoria, "prazo_dias": i.prazo_dias, "familia_id": i.familia_id,
"falecido_id": i.falecido_id, "dependencia_id": i.dependencia_id} for i in result.scalars().all()]
p = get_proximo_passo(items)
if not p:
return None
return p

View File

@@ -0,0 +1,54 @@
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from app.core.database import get_db
from app.core.security import get_current_user_id
from app.models.familia import Familia
from app.models.checklist import ChecklistItem
from app.models.beneficio import Beneficio
from app.models.documento import Documento
from app.schemas.schemas import DashboardOut
router = APIRouter(prefix="/dashboard", tags=["dashboard"])
@router.get("/", response_model=DashboardOut)
async def dashboard(user_id: int = Depends(get_current_user_id), db: AsyncSession = Depends(get_db)):
fams = await db.execute(select(Familia).where(Familia.usuario_id == user_id))
familias = fams.scalars().all()
fam_ids = [f.id for f in familias]
pendentes = 0
bens_count = 0
docs_count = 0
fam_list = []
for fam in familias:
ch = await db.execute(select(ChecklistItem).where(ChecklistItem.familia_id == fam.id))
items = ch.scalars().all()
total = len(items)
done = len([i for i in items if i.status == "concluido"])
pendentes += len([i for i in items if i.status == "pendente"])
bn = await db.execute(select(func.count()).select_from(Beneficio).where(Beneficio.familia_id == fam.id))
bc = bn.scalar() or 0
bens_count += bc
dc = await db.execute(select(func.count()).select_from(Documento).where(Documento.familia_id == fam.id))
docs_count += dc.scalar() or 0
fam_list.append({
"id": fam.id,
"nome": fam.nome,
"total_items": total,
"concluidos": done,
"progresso": round(done / total * 100) if total else 0,
"beneficios": bc,
})
return DashboardOut(
familias_ativas=len(familias),
itens_pendentes=pendentes,
beneficios_encontrados=bens_count,
documentos_gerados=docs_count,
familias=fam_list,
)

View File

@@ -0,0 +1,62 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.core.security import get_current_user_id
from app.models.documento import Documento
from app.models.falecido import Falecido
from app.models.familia import Familia, MembroFamilia
from app.schemas.schemas import DocumentoOut, DocumentoGerarRequest
from app.services.document_generator import GENERATORS
import os
router = APIRouter(prefix="/familias/{familia_id}/documentos", tags=["documentos"])
@router.get("/", response_model=list[DocumentoOut])
async def listar(familia_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Documento).where(Documento.familia_id == familia_id))
return result.scalars().all()
@router.post("/gerar", response_model=DocumentoOut)
async def gerar(familia_id: int, data: DocumentoGerarRequest, db: AsyncSession = Depends(get_db)):
gen = GENERATORS.get(data.tipo)
if not gen:
raise HTTPException(400, f"Tipo de documento inválido: {data.tipo}")
fam = await db.execute(select(Familia).where(Familia.id == familia_id))
familia = fam.scalar_one_or_none()
if not familia:
raise HTTPException(404, "Família não encontrada")
fal = await db.execute(select(Falecido).where(Falecido.id == data.falecido_id))
falecido = fal.scalar_one_or_none()
if not falecido:
raise HTTPException(404, "Falecido não encontrado")
membros = await db.execute(select(MembroFamilia).where(MembroFamilia.familia_id == familia_id))
membro = membros.scalars().first()
membro_nome = membro.nome if membro else "Representante da Família"
path = gen(familia.nome, falecido.nome, membro_nome, falecido.cpf or "000.000.000-00")
doc = Documento(
familia_id=familia_id,
falecido_id=data.falecido_id,
tipo=data.tipo,
nome=f"{data.tipo.replace('_',' ').title()} - {falecido.nome}",
arquivo_path=path,
status="gerado",
)
db.add(doc)
await db.commit()
await db.refresh(doc)
return doc
@router.get("/{doc_id}/download")
async def download(familia_id: int, doc_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Documento).where(Documento.id == doc_id, Documento.familia_id == familia_id))
doc = result.scalar_one_or_none()
if not doc or not doc.arquivo_path or not os.path.exists(doc.arquivo_path):
raise HTTPException(404, "Documento não encontrado")
return FileResponse(doc.arquivo_path, filename=os.path.basename(doc.arquivo_path), media_type="application/pdf")

View File

@@ -0,0 +1,61 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.core.security import get_current_user_id
from app.models.familia import Familia, MembroFamilia
from app.models.falecido import Falecido
from app.schemas.schemas import FamiliaCreate, FamiliaOut, MembroCreate, MembroOut, FalecidoCreate, FalecidoOut
from app.services.checklist_engine import gerar_checklist
from app.services.beneficio_scanner import escanear_beneficios
router = APIRouter(prefix="/familias", tags=["familias"])
@router.get("/", response_model=list[FamiliaOut])
async def listar(user_id: int = Depends(get_current_user_id), db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Familia).where(Familia.usuario_id == user_id))
return result.scalars().all()
@router.post("/", response_model=FamiliaOut)
async def criar(data: FamiliaCreate, user_id: int = Depends(get_current_user_id), db: AsyncSession = Depends(get_db)):
f = Familia(nome=data.nome, usuario_id=user_id)
db.add(f)
await db.commit()
await db.refresh(f)
return f
@router.get("/{id}", response_model=FamiliaOut)
async def detalhe(id: int, user_id: int = Depends(get_current_user_id), db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Familia).where(Familia.id == id, Familia.usuario_id == user_id))
f = result.scalar_one_or_none()
if not f:
raise HTTPException(404, "Família não encontrada")
return f
@router.post("/{id}/membros", response_model=MembroOut)
async def add_membro(id: int, data: MembroCreate, db: AsyncSession = Depends(get_db)):
m = MembroFamilia(familia_id=id, **data.model_dump())
db.add(m)
await db.commit()
await db.refresh(m)
return m
@router.get("/{id}/membros", response_model=list[MembroOut])
async def listar_membros(id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(MembroFamilia).where(MembroFamilia.familia_id == id))
return result.scalars().all()
@router.post("/{id}/falecido", response_model=FalecidoOut)
async def registrar_falecido(id: int, data: FalecidoCreate, db: AsyncSession = Depends(get_db)):
f = Falecido(familia_id=id, **data.model_dump())
db.add(f)
await db.commit()
await db.refresh(f)
await gerar_checklist(db, id, f)
await escanear_beneficios(db, id, f)
return f
@router.get("/{id}/falecidos", response_model=list[FalecidoOut])
async def listar_falecidos(id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(Falecido).where(Falecido.familia_id == id))
return result.scalars().all()