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

241 lines
7.7 KiB
Python
Raw 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.
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,
})