first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:26:46 +03:00
commit 2be3a313ad
55 changed files with 3609 additions and 0 deletions

241
namecreate/views.py Normal file
View File

@@ -0,0 +1,241 @@
import csv
import io
import json
import os
import uuid
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.authentication import SessionAuthentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.permissions import IsAdminUser
from rest_framework_simplejwt.authentication import JWTAuthentication
from namecreate.models import TrainingJob
from namecreate.tasks import train_model_task
def _parse_json_data(request):
"""
JSON body'den veriyi çeker.
Beklenen format:
{
"features": [[1.2, 3.4, 5.6, 7.8], [2.1, 4.3, 6.5, 8.7], ...],
"labels": [0, 1, 2, ...]
}
"""
body = json.loads(request.body)
features = body.get('features')
labels = body.get('labels')
if not features or not labels:
raise ValueError("'features' ve 'labels' alanları zorunludur.")
if len(features) != len(labels):
raise ValueError("'features' ve 'labels' eleman sayısı eşit olmalıdır.")
if len(features) < 10:
raise ValueError("En az 10 eğitim örneği gönderiniz.")
# Tip güvenliği: tüm değerler sayısal olmalı
for i, row in enumerate(features):
if not all(isinstance(v, (int, float)) for v in row):
raise ValueError(f"features[{i}] içinde sayısal olmayan değer var.")
if not all(isinstance(v, (int, float)) for v in labels):
raise ValueError("labels listesi sadece sayısal değer içermelidir.")
return features, [int(v) for v in labels]
def _parse_csv_data(file):
"""
CSV dosyasından veriyi çeker.
Beklenen format — son sütun label, geri kalanlar feature:
1.2,3.4,5.6,7.8,0
2.1,4.3,6.5,8.7,1
...
Header satırı varsa otomatik atlanır.
"""
content = file.read().decode('utf-8')
reader = csv.reader(io.StringIO(content))
features, labels = [], []
for lineno, row in enumerate(reader, start=1):
if not row:
continue
# Header satırını atla
try:
values = [float(v) for v in row]
except ValueError:
if lineno == 1:
continue # Başlık satırı
raise ValueError(f"CSV satır {lineno}: sayısal olmayan değer.")
if len(values) < 2:
raise ValueError(f"CSV satır {lineno}: en az 2 sütun (özellik + etiket) gerekli.")
features.append(values[:-1])
labels.append(int(values[-1]))
if len(features) < 10:
raise ValueError("En az 10 eğitim örneği gönderiniz.")
return features, labels
@csrf_exempt
def train_and_export_model(request):
"""
Modeli arka planda eğitmek için Celery task'ı başlatır.
Veri gönderme yöntemleri:
1) JSON body:
POST /api/v1/ml/train-model/
Content-Type: application/json
{
"features": [[1.2, 3.4, 5.6, 7.8], ...],
"labels": [0, 1, 2, ...]
}
2) CSV dosyası:
POST /api/v1/ml/train-model/
Content-Type: multipart/form-data
Form alanı: data=<csv_dosyası>
(Son sütun label, geri kalanlar feature)
3) Veri göndermezseniz yerleşik Iris demo verisi kullanılır.
"""
if request.method != 'POST':
return JsonResponse({'error': 'Sadece POST destekleniyor.'}, status=405)
try:
features, labels = None, None
source = 'demo'
# --- Yöntem 1: JSON body ---
ct = request.content_type or ''
if 'application/json' in ct and request.body:
features, labels = _parse_json_data(request)
source = 'json'
# --- Yöntem 2: CSV dosyası ---
elif 'data' in request.FILES:
features, labels = _parse_csv_data(request.FILES['data'])
source = 'csv'
# --- Yöntem 3: Demo (Iris) ---
# features ve labels None kalır, task default veriyi kullanır
feature_count = len(features[0]) if features else None
sample_count = len(features) if features else None
task_id = str(uuid.uuid4())
TrainingJob.objects.create(
task_id=task_id,
status='pending',
features=features,
labels=labels,
feature_count=feature_count,
sample_count=sample_count,
)
celery_task = train_model_task.delay(task_id)
return JsonResponse({
'status': 'queued',
'message': 'Model eğitim görevi başlatıldı.',
'task_id': task_id,
'celery_task_id': celery_task.id,
'data_source': source,
'sample_count': sample_count,
'feature_count': feature_count,
})
except (ValueError, KeyError) as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=400)
except Exception as e:
return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
def get_training_status(request):
"""Task'ın durumunu sorgular."""
task_id = request.GET.get('task_id')
if not task_id:
return JsonResponse({
"error": "task_id gerekli"
}, status=400)
try:
job = TrainingJob.objects.get(task_id=task_id)
response = {
"task_id": job.task_id,
"status": job.status,
"created_at": job.created_at.isoformat() if job.created_at else None,
"started_at": job.started_at.isoformat() if job.started_at else None,
"completed_at": job.completed_at.isoformat() if job.completed_at else None,
}
# Task tamamlandığında metrikleri ekle
if job.status == 'completed':
response.update({
"model_version": job.model_version.isoformat(),
"metrics": {
"accuracy": job.accuracy,
"precision": job.precision,
"recall": job.recall,
"f1_score": job.f1_score,
}
})
# Task başarısız olmuşsa hata mesajı ekle
if job.status == 'failed':
response["error_message"] = job.error_message
return JsonResponse(response)
except TrainingJob.DoesNotExist:
return JsonResponse({
"error": "Task bulunamadı"
}, status=404)
@api_view(['GET'])
@authentication_classes([SessionAuthentication, JWTAuthentication])
@permission_classes([IsAdminUser])
def list_training_jobs(request):
"""TrainingJob kayıtlarını listeler."""
status_filter = request.GET.get('status')
limit = request.GET.get('limit', '50')
try:
limit = max(1, min(int(limit), 200))
except ValueError:
return JsonResponse({"error": "limit sayısal olmalıdır"}, status=400)
jobs = TrainingJob.objects.all().order_by('-created_at')
if status_filter:
jobs = jobs.filter(status=status_filter)
items = []
for job in jobs[:limit]:
model_exists = bool(job.model_path and os.path.exists(job.model_path))
items.append({
'id': job.pk,
'task_id': job.task_id,
'status': job.status,
'model_type': job.model_type,
'created_at': job.created_at.isoformat() if job.created_at else None,
'started_at': job.started_at.isoformat() if job.started_at else None,
'completed_at': job.completed_at.isoformat() if job.completed_at else None,
'sample_count': job.sample_count,
'feature_count': job.feature_count,
'model_path': job.model_path,
'model_exists': model_exists,
'error_message': job.error_message,
})
return JsonResponse({
'count': len(items),
'results': items,
})