Files
insta/namecreate/management/commands/generate_persons.py
Beyhan Oğur 2be3a313ad first commit
2026-04-26 22:26:46 +03:00

205 lines
8.2 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.
"""
NameVocab tablosundan ağırlıklı örneklemeyle kişi verisi üretir.
- Doğum tarihi: 20 yaş ve üstü (bugüne göre)
- Türkçe isimler frekans ağırlıklı olarak önce gelir
- Üretilen kayıtlar GeneratedPerson tablosuna kaydedilir
Kullanım:
python manage.py generate_persons # 1000 kişi
python manage.py generate_persons --count 500 # 500 kişi
python manage.py generate_persons --clear # önce tabloyu temizle
"""
import random
import calendar
from datetime import date
from django.core.management.base import BaseCommand, CommandError
from namecreate.models import NameVocab, GeneratedPerson, TrainingJob
from namecreate.username_utils import build_unique_username
from namecreate.llm_generator import generate_people_with_llm
# Bugün: 27 Mart 2026 — 20 yaş ve üstü → en geç 27 Mart 2006 doğum
MAX_BIRTH_YEAR = date.today().year - 20 # 2006
MIN_BIRTH_YEAR = 1940
def _weighted_pick(queryset):
"""Frequency değerlerine göre ağırlıklı rastgele seçim."""
names = list(queryset.values_list('name', 'frequency'))
if not names:
raise CommandError("NameVocab tablosu boş. Önce 'seed_name_vocab' komutunu çalıştırın.")
population = [n for n, _ in names]
weights = [f for _, f in names]
return random.choices(population, weights=weights, k=1)[0]
def _random_birth_date():
"""20 yaş ve üstü rastgele doğum tarihi üretir."""
year = random.randint(MIN_BIRTH_YEAR, MAX_BIRTH_YEAR)
month = random.randint(1, 12)
# Ayın son gününü doğru hesapla (şubat, 30/31 gün farkları)
_, max_day = calendar.monthrange(year, month)
# 2006 doğumsa mart ayı sonrasında doğanlar henüz 20 yaşında değil
if year == MAX_BIRTH_YEAR:
today = date.today()
if month > today.month:
month = random.randint(1, today.month)
if month == today.month:
_, max_day = calendar.monthrange(year, month)
max_day = min(max_day, today.day)
_, max_day = calendar.monthrange(year, month)
day = random.randint(1, max_day)
return date(year, month, day)
class Command(BaseCommand):
help = '1000 kişilik sahte isim/soyisim/doğum tarihi verisi üretir (20 yaş ve üstü)'
def add_arguments(self, parser):
parser.add_argument(
'--count', type=int, default=1000,
help='Üretilecek kişi sayısı (varsayılan: 1000)'
)
parser.add_argument(
'--clear', action='store_true',
help='Üretmeden önce mevcut GeneratedPerson kayıtlarını sil'
)
parser.add_argument(
'--job-id', type=int, default=None,
help='Bağlanacak TrainingJob ID (belirtilmezse en son tamamlanmış iş kullanılır)'
)
parser.add_argument(
'--use-llm', action='store_true',
help='Uretimi LLM ile yapmayi dener; basarisiz olursa istatistiksel fallback yapar.'
)
parser.add_argument(
'--no-llm', action='store_true',
help='LLM denemeyi kapatir, dogrudan istatistiksel uretim yapar.'
)
parser.add_argument(
'--llm-strict', action='store_true',
help='LLM basarisiz olursa fallback yapma, komutu hata ile sonlandir.'
)
def handle(self, *args, **options):
count = options['count']
if options['clear']:
deleted, _ = GeneratedPerson.objects.all().delete()
self.stdout.write(self.style.WARNING(f'{deleted} eski kayıt silindi.'))
# TrainingJob bağlantısını belirle
job_id = options.get('job_id')
if job_id:
try:
training_job = TrainingJob.objects.get(pk=job_id)
except TrainingJob.DoesNotExist:
raise CommandError(f'TrainingJob id={job_id} bulunamadı.')
else:
training_job = TrainingJob.objects.filter(status='completed').exclude(
model_path=None
).order_by('-completed_at').first()
if training_job is None:
self.stdout.write(self.style.WARNING(
'Tamamlanmış (model dosyalı) TrainingJob bulunamadı. training_job=None olarak üretilecek.'
))
else:
self.stdout.write(f'TrainingJob kullanılıyor: id={training_job.pk} ({training_job.model_type})')
# Erkek / kadın isimlerini bir kez çek
male_first = NameVocab.objects.filter(name_type='first', gender='E')
female_first = NameVocab.objects.filter(name_type='first', gender='K')
last_names = NameVocab.objects.filter(name_type='last')
if not male_first.exists() or not female_first.exists() or not last_names.exists():
raise CommandError(
"NameVocab tablosu eksik. Önce 'python manage.py seed_name_vocab' komutunu çalıştırın."
)
# Tüm ağırlıklı listeleri RAM'e al (1000 kayıt için yeterli)
male_pool = list(male_first.values_list('name', 'frequency'))
female_pool = list(female_first.values_list('name', 'frequency'))
last_pool = list(last_names.values_list('name', 'frequency'))
def pick(pool):
names, weights = zip(*pool)
return random.choices(names, weights=weights, k=1)[0]
used_usernames = set(GeneratedPerson.objects.values_list('username', flat=True).exclude(username=''))
persons = []
source = 'statistical'
llm_rows = None
use_llm = options.get('use_llm') or not options.get('no_llm')
if use_llm:
try:
llm_rows = generate_people_with_llm(count=count, min_age=20)
source = 'llm'
except Exception as e:
if options.get('llm_strict'):
raise CommandError(f'LLM uretimi basarisiz: {e}')
self.stdout.write(self.style.WARNING(
f'LLM uretimi basarisiz ({e}). Istatistiksel fallback kullaniliyor.'
))
if llm_rows:
for row in llm_rows[:count]:
first = row['first_name']
last = row['last_name']
gender = row['gender']
birth = row['birth_date']
username = build_unique_username(first, last, birth, used_usernames)
persons.append(GeneratedPerson(
first_name=first,
last_name=last.upper(),
username=username,
birth_date=birth,
gender=gender,
confidence=None,
training_job=training_job,
))
missing = count - len(persons)
for _ in range(missing):
gender = random.choice(['E', 'K'])
first = pick(male_pool if gender == 'E' else female_pool)
last = pick(last_pool)
birth = _random_birth_date()
username = build_unique_username(first, last, birth, used_usernames)
persons.append(GeneratedPerson(
first_name=first,
last_name=last.upper(), # soyisim büyük harf
username=username,
birth_date=birth,
gender=gender,
confidence=None,
training_job=training_job,
))
GeneratedPerson.objects.bulk_create(persons)
self.stdout.write(self.style.SUCCESS(
f'\n{count} kişi başarıyla üretildi ve kaydedildi.'
))
self.stdout.write(f' Kaynak: {source}')
self.stdout.write(
f" Erkek: {sum(1 for p in persons if p.gender == 'E')} | "
f"Kadın: {sum(1 for p in persons if p.gender == 'K')}"
)
self.stdout.write(
f" Doğum aralığı: {MIN_BIRTH_YEAR} {MAX_BIRTH_YEAR} (20 yaş ve üstü)"
)
# Örnek 5 kayıt göster
self.stdout.write('\nİlk 5 örnek:')
for p in persons[:5]:
age = date.today().year - p.birth_date.year - (
(date.today().month, date.today().day) < (p.birth_date.month, p.birth_date.day)
)
self.stdout.write(
f" {p.first_name} {p.last_name} (@{p.username}) | {p.birth_date} | "
f"{p.get_gender_display()} | {age} yaş"
)