180 lines
6.1 KiB
Python
180 lines
6.1 KiB
Python
import random
|
||
from django.conf import settings
|
||
from django.db import models
|
||
from django.utils import timezone
|
||
from .username_utils import build_unique_username
|
||
|
||
|
||
class NameVocab(models.Model):
|
||
"""Eğitim ve üretim için isim/soyisim sözlüğü. Türkçe kökenli isimler önceliklidir."""
|
||
|
||
ORIGIN_CHOICES = [
|
||
('turkce', 'Türkçe'), # Birinci öncelik
|
||
('bati', 'Batı'),
|
||
('diger', 'Diğer'),
|
||
('arapca', 'Arapça'), # Son öncelik
|
||
]
|
||
|
||
NAME_TYPE_CHOICES = [
|
||
('first', 'İsim'),
|
||
('last', 'Soyisim'),
|
||
]
|
||
|
||
GENDER_CHOICES = [
|
||
('E', 'Erkek'),
|
||
('K', 'Kadın'),
|
||
('U', 'Unisex'),
|
||
]
|
||
|
||
name = models.CharField(max_length=100, help_text='İsim veya soyisim')
|
||
name_type = models.CharField(max_length=5, choices=NAME_TYPE_CHOICES)
|
||
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='U')
|
||
origin = models.CharField(
|
||
max_length=10,
|
||
choices=ORIGIN_CHOICES,
|
||
default='turkce',
|
||
help_text='Türkçe kökenli isimler varsayılan ve önceliklidir',
|
||
)
|
||
frequency = models.PositiveIntegerField(
|
||
default=1,
|
||
help_text='Veri setindeki görülme sıklığı — ağırlıklı seçimde kullanılır'
|
||
)
|
||
|
||
class Meta:
|
||
ordering = ['origin', '-frequency', 'name']
|
||
verbose_name = 'İsim Sözlüğü'
|
||
verbose_name_plural = 'İsim Sözlüğü'
|
||
unique_together = [('name', 'name_type', 'gender')]
|
||
|
||
def __str__(self):
|
||
return f"{self.name} ({self.get_name_type_display()}, {self.get_gender_display()}, {self.get_origin_display()})"
|
||
|
||
|
||
class GeneratedPerson(models.Model):
|
||
"""Modelin ürettiği kişi kaydı."""
|
||
|
||
GENDER_CHOICES = [
|
||
('E', 'Erkek'),
|
||
('K', 'Kadın'),
|
||
]
|
||
|
||
training_job = models.ForeignKey(
|
||
'TrainingJob',
|
||
on_delete=models.SET_NULL,
|
||
null=True,
|
||
blank=True,
|
||
related_name='generated_persons',
|
||
help_text='Bu kişiyi üreten model versiyonu',
|
||
)
|
||
first_name = models.CharField(max_length=100)
|
||
last_name = models.CharField(max_length=100)
|
||
username = models.CharField(max_length=150, blank=True, db_index=True)
|
||
username_locked = models.BooleanField(
|
||
default=False,
|
||
help_text='Aciksa username yeniden uretilmez (force ile degistirilebilir).'
|
||
)
|
||
birth_date = models.DateField()
|
||
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
|
||
confidence = models.FloatField(
|
||
null=True, blank=True,
|
||
help_text='Modelin seçim güven skoru (0-1)'
|
||
)
|
||
generated_at = models.DateTimeField(auto_now_add=True)
|
||
|
||
class Meta:
|
||
ordering = ['-generated_at']
|
||
verbose_name = 'Üretilen Kişi'
|
||
verbose_name_plural = 'Üretilen Kişiler'
|
||
|
||
def __str__(self):
|
||
return f"{self.first_name} {self.last_name} ({self.birth_date})"
|
||
|
||
def regenerate_username(self, used_usernames=None, force=False, save=True):
|
||
"""Kayit icin yeni bir username uretir. Mevcut username her zaman degisir."""
|
||
if self.username_locked and not force:
|
||
return self.username
|
||
|
||
if used_usernames is None:
|
||
used_usernames = set(
|
||
GeneratedPerson.objects.exclude(pk=self.pk).exclude(username='').values_list('username', flat=True)
|
||
)
|
||
|
||
# Mevcut username'i yasak listesine ekle ve rastgele sonek ile basla
|
||
# Boylece ayni base'e hic donulmez (ping-pong olmaz)
|
||
if self.username:
|
||
used_usernames.add(self.username)
|
||
|
||
suffix_min = getattr(settings, 'USERNAME_SUFFIX_MIN', 2)
|
||
suffix_max = getattr(settings, 'USERNAME_SUFFIX_MAX', 999)
|
||
self.username = build_unique_username(
|
||
self.first_name,
|
||
self.last_name,
|
||
self.birth_date,
|
||
used_usernames,
|
||
_force_suffix=random.randint(suffix_min, suffix_max),
|
||
)
|
||
if save:
|
||
self.save(update_fields=['username'])
|
||
return self.username
|
||
|
||
|
||
class TrainingJob(models.Model):
|
||
"""Makine öğrenme model eğitim görevinin kaydı."""
|
||
|
||
STATUS_CHOICES = [
|
||
('pending', 'Beklemede'),
|
||
('running', 'Eğitiliyor'),
|
||
('completed', 'Tamamlandı'),
|
||
('failed', 'Başarısız'),
|
||
]
|
||
|
||
# Temel bilgiler
|
||
task_id = models.CharField(max_length=255, unique=True, help_text='Celery task ID')
|
||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
|
||
created_at = models.DateTimeField(auto_now_add=True)
|
||
started_at = models.DateTimeField(null=True, blank=True)
|
||
completed_at = models.DateTimeField(null=True, blank=True)
|
||
|
||
# Model bilgileri
|
||
model_type = models.CharField(max_length=100, default='RandomForest')
|
||
model_version = models.DateTimeField(default=timezone.now, help_text='Model versiyonu (timestamp)')
|
||
model_path = models.CharField(max_length=500, null=True, blank=True)
|
||
|
||
# Eğitim verisi
|
||
features = models.JSONField(
|
||
null=True, blank=True,
|
||
help_text='2D liste: her satır bir örnek, her sütun bir özellik'
|
||
)
|
||
labels = models.JSONField(
|
||
null=True, blank=True,
|
||
help_text='1D liste: her örneğin sınıf etiketi'
|
||
)
|
||
feature_count = models.PositiveIntegerField(
|
||
null=True, blank=True,
|
||
help_text='Özellik (sütun) sayısı — ONNX tipi için kullanılır'
|
||
)
|
||
sample_count = models.PositiveIntegerField(
|
||
null=True, blank=True,
|
||
help_text='Eğitim verisi satır sayısı'
|
||
)
|
||
|
||
# Metrikler
|
||
accuracy = models.FloatField(null=True, blank=True)
|
||
precision = models.FloatField(null=True, blank=True)
|
||
recall = models.FloatField(null=True, blank=True)
|
||
f1_score = models.FloatField(null=True, blank=True)
|
||
|
||
# Hata handling
|
||
error_message = models.TextField(null=True, blank=True)
|
||
|
||
# Go servisine sinyal
|
||
go_service_notified = models.BooleanField(default=False)
|
||
|
||
class Meta:
|
||
ordering = ['-created_at']
|
||
verbose_name = 'Eğitim Görevi'
|
||
verbose_name_plural = 'Eğitim Görevleri'
|
||
|
||
def __str__(self):
|
||
return f"{self.model_type} - {self.status} - {self.created_at}"
|