177 lines
6.5 KiB
Python
177 lines
6.5 KiB
Python
from pydantic import BaseModel, EmailStr, Field, ConfigDict, field_validator, model_validator
|
|
from datetime import datetime
|
|
from typing import Optional, Any, List, Dict
|
|
|
|
class AlumniBase(BaseModel):
|
|
nim: str = Field(..., description="Nomor Induk Mahasiswa (NIM)")
|
|
nama: str = Field(..., min_length=2, description="Nama lengkap alumni")
|
|
tahun_lulus: int = Field(..., ge=1900, le=2100, description="Tahun lulus alumni")
|
|
status_kerja: Optional[str] = Field(None, description="Status pekerjaan saat ini")
|
|
pekerjaan: Optional[str] = Field(None, description="Nama pekerjaan/posisi jika bekerja")
|
|
instansi: Optional[str] = Field(None, description="Nama instansi/perusahaan tempat bekerja")
|
|
no_hp: Optional[str] = Field(None, description="Nomor handphone/WhatsApp aktif")
|
|
email: Optional[str] = Field(None, description="Email aktif alumni")
|
|
kode_prodi: Optional[str] = Field(None, description="Kode program studi")
|
|
|
|
class AlumniCreate(AlumniBase):
|
|
pass
|
|
|
|
class AlumniUpdate(BaseModel):
|
|
nim: Optional[str] = None
|
|
nama: Optional[str] = None
|
|
tahun_lulus: Optional[int] = None
|
|
status_kerja: Optional[str] = None
|
|
pekerjaan: Optional[str] = None
|
|
instansi: Optional[str] = None
|
|
no_hp: Optional[str] = None
|
|
email: Optional[str] = None
|
|
kode_prodi: Optional[str] = None
|
|
|
|
class AlumniResponse(AlumniBase):
|
|
id: int
|
|
created_at: Optional[datetime] = None
|
|
updated_at: Optional[datetime] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
@model_validator(mode='before')
|
|
@classmethod
|
|
def exclude_relationships(cls, data: Any) -> Any:
|
|
"""Convert SQLAlchemy objects to dict, excluding relationship attributes"""
|
|
if hasattr(data, '__dict__'):
|
|
result = {}
|
|
for key, value in data.__dict__.items():
|
|
if key.startswith('_') or key in ['pekerjaan_detail', 'kompetensi', 'tracer_response', 'clustering_results']:
|
|
continue
|
|
if hasattr(value, '__tablename__'):
|
|
result[key] = None
|
|
else:
|
|
result[key] = value
|
|
return result
|
|
return data
|
|
|
|
|
|
class PekerjaanDetail(BaseModel):
|
|
id: Optional[int] = None
|
|
status_pekerjaan: Optional[str] = None
|
|
jenis_perusahaan: Optional[str] = None
|
|
nama_perusahaan: Optional[str] = None
|
|
jabatan: Optional[str] = None
|
|
tingkatan_tempat_kerja: Optional[str] = None
|
|
provinsi: Optional[str] = None
|
|
kabupaten_kota: Optional[str] = None
|
|
teks_pekerjaan: Optional[str] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class KompetensiDetail(BaseModel):
|
|
id: Optional[int] = None
|
|
etika_lulus: Optional[str] = None
|
|
keahlian_lulus: Optional[str] = None
|
|
bahasa_inggris_lulus: Optional[str] = None
|
|
teknologi_informasi_lulus: Optional[str] = None
|
|
komunikasi_lulus: Optional[str] = None
|
|
kerja_sama_lulus: Optional[str] = None
|
|
pengembangan_diri_lulus: Optional[str] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class ClusteringResultDetail(BaseModel):
|
|
id: Optional[int] = None
|
|
model_version: Optional[str] = None
|
|
k: Optional[int] = None
|
|
cluster: Optional[int] = None
|
|
kategori: Optional[str] = None
|
|
profile_match: Optional[bool] = None
|
|
matched_keyword: Optional[str] = None
|
|
created_at: Optional[datetime] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class AlumniDetailResponse(AlumniBase):
|
|
id: int
|
|
created_at: Optional[datetime] = None
|
|
updated_at: Optional[datetime] = None
|
|
pekerjaan_detail: Optional[PekerjaanDetail] = None
|
|
kompetensi: Optional[KompetensiDetail] = None
|
|
tracer_response: Optional[Dict[str, Any]] = None
|
|
clustering_results: Optional[List[ClusteringResultDetail]] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
@model_validator(mode='before')
|
|
@classmethod
|
|
def process_relations(cls, data: Any) -> Any:
|
|
if hasattr(data, '__dict__'):
|
|
result = {}
|
|
for key, value in data.__dict__.items():
|
|
if key.startswith('_'):
|
|
continue
|
|
if key == 'tracer_response' and value is not None and hasattr(value, '__dict__'):
|
|
# Convert tracer_response sqlalchemy object to dict
|
|
tr_dict = {k: v for k, v in value.__dict__.items() if not k.startswith('_') and not hasattr(v, '__tablename__')}
|
|
result[key] = tr_dict
|
|
else:
|
|
result[key] = value
|
|
return result
|
|
return data
|
|
|
|
|
|
class AlumniPaginationResponse(BaseModel):
|
|
total: int
|
|
page: int
|
|
limit: int
|
|
total_pages: int
|
|
data: List[AlumniResponse]
|
|
|
|
|
|
class PublicTracerLookupRequest(BaseModel):
|
|
nim: str = Field(..., description="Nomor Induk Mahasiswa (NIM)")
|
|
email: Optional[str] = Field(None, description="Email / Gmail untuk verifikasi kepemilikan")
|
|
|
|
|
|
class PublicTracerSubmitRequest(BaseModel):
|
|
nim: str = Field(..., description="Nomor Induk Mahasiswa (NIM)")
|
|
nama: str = Field(..., min_length=2, description="Nama lengkap alumni")
|
|
kode_prodi: str = Field(..., description="Kode prodi (55301, 57401, 56401, dll)")
|
|
tahun_lulus: int = Field(..., ge=1990, le=2100, description="Tahun kelulusan")
|
|
email: str = Field(..., description="Email / Gmail aktif alumni")
|
|
no_hp: Optional[str] = Field(None, description="Nomor WhatsApp / HP aktif")
|
|
status_kerja: str = Field(..., description="Status kerja: bekerja, belum_bekerja, wiraswasta, melanjutkan, mencari_kerja")
|
|
|
|
# Detail Pekerjaan (jika bekerja / wiraswasta)
|
|
nama_perusahaan: Optional[str] = None
|
|
jabatan: Optional[str] = None
|
|
jenis_perusahaan: Optional[str] = None
|
|
tingkatan_tempat_kerja: Optional[str] = None
|
|
hubungan_studi: Optional[str] = None
|
|
|
|
# Identitas Extended (Pertanyaan-dan-jawaban.md)
|
|
kode_pt: Optional[str] = "005019"
|
|
nik: Optional[str] = None
|
|
npwp: Optional[str] = None
|
|
|
|
# Karir & Gaji Extended
|
|
bulan_dapat_kerja: Optional[int] = None
|
|
rata_pendapatan: Optional[str] = None
|
|
provinsi_kerja: Optional[str] = None
|
|
kab_kota_kerja: Optional[str] = None
|
|
posisi_wiraswasta: Optional[str] = None
|
|
|
|
# Studi Lanjut
|
|
sumber_biaya_studi: Optional[str] = None
|
|
pt_studi_lanjut: Optional[str] = None
|
|
prodi_studi_lanjut: Optional[str] = None
|
|
tanggal_masuk_studi: Optional[str] = None
|
|
|
|
# Pembiayaan & Kesesuaian
|
|
sumber_biaya_kuliah: Optional[str] = None
|
|
tingkat_pendidikan_tepat: Optional[str] = None
|
|
|
|
# Data Ekstra / Detail Kuesioner (Kompetensi, Metode Pembelajaran, Cara Cari Kerja, dll)
|
|
data_extra: Optional[dict] = None
|
|
|