CARONTE v1.0 - Plataforma de Gestão Social
This commit is contained in:
0
backend/app/api/__init__.py
Normal file
0
backend/app/api/__init__.py
Normal file
0
backend/app/api/v1/__init__.py
Normal file
0
backend/app/api/v1/__init__.py
Normal file
38
backend/app/api/v1/auth.py
Normal file
38
backend/app/api/v1/auth.py
Normal 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
|
||||
32
backend/app/api/v1/beneficios.py
Normal file
32
backend/app/api/v1/beneficios.py
Normal 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
|
||||
41
backend/app/api/v1/checklist.py
Normal file
41
backend/app/api/v1/checklist.py
Normal 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
|
||||
54
backend/app/api/v1/dashboard.py
Normal file
54
backend/app/api/v1/dashboard.py
Normal 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,
|
||||
)
|
||||
62
backend/app/api/v1/documentos.py
Normal file
62
backend/app/api/v1/documentos.py
Normal 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")
|
||||
61
backend/app/api/v1/familias.py
Normal file
61
backend/app/api/v1/familias.py
Normal 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()
|
||||
Reference in New Issue
Block a user