from django.db import models class Organization(models.Model): type = models.CharField(max_length=50, default='Ingeniería') career = models.CharField(max_length=200) gradient_index = models.IntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) class Group(models.Model): name = models.CharField(max_length=100) organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='groups') class Applicant(models.Model): STATUS_CHOICES = ( ('registered', 'Registrado'), ('approved', 'Aprobado'), ('rejected', 'Reprobado'), ('error', 'Error en Evaluación'), ) first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) email = models.EmailField(unique=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='registered') modality = models.CharField(max_length=50, choices=(('Virtual', 'Virtual'), ('Semi presencial', 'Semi presencial')), default='Virtual') organization = models.ForeignKey(Organization, on_delete=models.SET_NULL, null=True, blank=True) group = models.ForeignKey(Group, on_delete=models.SET_NULL, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.first_name} {self.last_name} ({self.get_status_display()})" # ponytail: Modelo ultra-minimalista para entrevistas. Relación N:M implícita con Applicant. class Interview(models.Model): title = models.CharField(max_length=200, default='Entrevista Técnica') date = models.DateField() time_index = models.IntegerField(help_text="0=7AM, 1=8AM, 2=9AM, etc.") duration_minutes = models.IntegerField(default=60, help_text="Duración de la entrevista en minutos") active = models.BooleanField(default=False) applicants = models.ManyToManyField(Applicant, blank=True) class Meta: ordering = ['date', 'time_index'] def __str__(self): return f"{self.title} on {self.date} (slot {self.time_index})" class EvaluationCategory(models.Model): name = models.CharField(max_length=100) description = models.TextField(blank=True) weight = models.FloatField(default=1.0) order = models.IntegerField(default=0) active = models.BooleanField(default=True) color = models.CharField(max_length=7, default='#0F766E') class InterviewSession(models.Model): STATUS_CHOICES = ( ('in_progress', 'En Progreso'), ('completed', 'Completada'), ('evaluated', 'Evaluada'), ('error', 'Error'), ) applicant = models.ForeignKey(Applicant, on_delete=models.CASCADE) interview = models.ForeignKey(Interview, on_delete=models.CASCADE) started_at = models.DateTimeField(auto_now_add=True) ended_at = models.DateTimeField(null=True, blank=True) recording_url = models.CharField(max_length=500, blank=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='in_progress') overall_score = models.FloatField(null=True, blank=True) dynamic_traits = models.JSONField(null=True, blank=True) interviewer_notes = models.TextField(blank=True) hr_recommendation = models.TextField(blank=True) class QuestionResponse(models.Model): session = models.ForeignKey(InterviewSession, on_delete=models.CASCADE) question_index = models.IntegerField() question_text = models.TextField() audio_url = models.CharField(max_length=500, blank=True) transcription = models.TextField(blank=True) duration_seconds = models.IntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) class EvaluationResult(models.Model): session = models.ForeignKey(InterviewSession, on_delete=models.CASCADE) category = models.ForeignKey(EvaluationCategory, on_delete=models.CASCADE) score = models.FloatField() feedback = models.TextField(blank=True) class LLMProviderConfig(models.Model): name = models.CharField(max_length=50) provider_type = models.CharField(max_length=20) api_key = models.CharField(max_length=200, blank=True) endpoint = models.CharField(max_length=200, blank=True) model = models.CharField(max_length=100) is_active = models.BooleanField(default=False) is_embedding = models.BooleanField(default=False) class TranscriptionProviderConfig(models.Model): name = models.CharField(max_length=50) provider_type = models.CharField(max_length=20) endpoint = models.CharField(max_length=200, blank=True) api_key = models.CharField(max_length=200, blank=True) is_active = models.BooleanField(default=False) model = models.CharField(max_length=150, blank=True, null=True) role = models.CharField(max_length=50, blank=True, null=True) class SystemPromptConfig(models.Model): prompt_text = models.TextField(default="Actúa como un reclutador experto ITCA. Evalúa al candidato en base a sus respuestas en la entrevista.") updated_at = models.DateTimeField(auto_now=True)