memleketmeselesi/mm_api/routers/officials.py
Mukan Erkin 2498e75594 init: memleketmeselesi platform — API + migrations
FastAPI + PostgreSQL 16. KYC, issue sistemi, permission/group yönetimi,
session yönetimi, API client auth (kışla kapısı), officials/persons CRUD.
Migration 0001–0013 dahil.
2026-04-27 23:06:59 +03:00

166 lines
5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import Literal
from fastapi import APIRouter, Depends, HTTPException, Query
from psycopg import AsyncConnection
from pydantic import BaseModel, Field
from mm_api.db import get_conn
from mm_api.dependencies import current_user
import mm_api.services.official as svc
import mm_api.services.permission as perm_svc
router = APIRouter(tags=["officials"])
async def _require_profile_perm(action: str, user: dict, conn: AsyncConnection):
if not await perm_svc.can(conn, user["id"], action, "profile", is_admin=True):
raise HTTPException(403, "Yetersiz yetki")
class PersonCreate(BaseModel):
first_name: str = Field(..., min_length=1, max_length=100)
last_name: str = Field(..., min_length=1, max_length=100)
birth_year: int | None = Field(None, ge=1900, le=2010)
class PersonUpdate(BaseModel):
first_name: str | None = Field(None, min_length=1, max_length=100)
last_name: str | None = Field(None, min_length=1, max_length=100)
birth_year: int | None = Field(None, ge=1900, le=2010)
class ElectionCreate(BaseModel):
name: str = Field(..., min_length=2, max_length=200)
held_at: str # YYYY-MM-DD
type: Literal["genel", "yerel", "cumhurbaskanligi", "referandum"]
class OfficialCreate(BaseModel):
person_id: int
unit_id: int
title: str = Field(..., min_length=2, max_length=200)
started_at: str # YYYY-MM-DD
election_id: int | None = None
class OfficialClose(BaseModel):
ended_at: str # YYYY-MM-DD
# --- Persons ---
@router.get("/persons")
async def list_persons(
q: str | None = Query(None),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
conn: AsyncConnection = Depends(get_conn),
):
return await svc.list_persons(conn, q, limit, offset)
@router.get("/persons/{person_id}")
async def get_person(
person_id: int,
conn: AsyncConnection = Depends(get_conn),
):
person = await svc.get_person(conn, person_id)
if not person:
raise HTTPException(404, "Kişi bulunamadı")
officials = await svc.get_person_officials(conn, person_id)
return {**person, "officials": officials}
@router.post("/persons", status_code=201)
async def create_person(
data: PersonCreate,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
await _require_profile_perm("create", user, conn)
return await svc.create_person(conn, data.first_name, data.last_name, data.birth_year)
@router.patch("/persons/{person_id}")
async def update_person(
person_id: int,
data: PersonUpdate,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
await _require_profile_perm("update", user, conn)
person = await svc.get_person(conn, person_id)
if not person:
raise HTTPException(404, "Kişi bulunamadı")
try:
return await svc.update_person(conn, person_id, data.first_name, data.last_name, data.birth_year)
except ValueError as e:
raise HTTPException(400, str(e))
# --- Elections ---
@router.get("/elections")
async def list_elections(conn: AsyncConnection = Depends(get_conn)):
return await svc.list_elections(conn)
@router.post("/elections", status_code=201)
async def create_election(
data: ElectionCreate,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
await _require_profile_perm("create", user, conn)
return await svc.create_election(conn, data.name, data.held_at, data.type)
# --- Officials ---
@router.get("/officials")
async def list_officials(
person_id: int | None = Query(None),
unit_id: int | None = Query(None),
active_only: bool = Query(False),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
conn: AsyncConnection = Depends(get_conn),
):
return await svc.list_officials(conn, person_id, unit_id, active_only, limit, offset)
@router.get("/officials/{official_id}")
async def get_official(
official_id: int,
conn: AsyncConnection = Depends(get_conn),
):
official = await svc.get_official(conn, official_id)
if not official:
raise HTTPException(404, "Yetkili kaydı bulunamadı")
stats = await svc.get_official_stats(conn, official_id)
return {**official, "stats": stats}
@router.post("/officials", status_code=201)
async def create_official(
data: OfficialCreate,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
await _require_profile_perm("create", user, conn)
try:
return await svc.create_official(conn, data.person_id, data.unit_id, data.title, data.started_at, data.election_id)
except ValueError as e:
raise HTTPException(400, str(e))
@router.post("/officials/{official_id}/close")
async def close_official(
official_id: int,
data: OfficialClose,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
await _require_profile_perm("update", user, conn)
try:
return await svc.close_official(conn, official_id, data.ended_at)
except ValueError as e:
raise HTTPException(400, str(e))