# jobs/models.py from django.db import models from django.utils import timezone from django.core.validators import URLValidator from .validators import validate_image_size, validate_hash_tags import uuid import os class JobPosting(models.Model): # Basic Job Information JOB_TYPES = [ ('FULL_TIME', 'Full-time'), ('PART_TIME', 'Part-time'), ('CONTRACT', 'Contract'), ('INTERNSHIP', 'Internship'), ('FACULTY', 'Faculty'), ('TEMPORARY', 'Temporary'), ] WORKPLACE_TYPES = [ ('ON_SITE', 'On-site'), ('REMOTE', 'Remote'), ('HYBRID', 'Hybrid'), ] # Core Fields title = models.CharField(max_length=200) department = models.CharField(max_length=100, blank=True) job_type = models.CharField(max_length=20, choices=JOB_TYPES, default='FULL_TIME') workplace_type = models.CharField(max_length=20, choices=WORKPLACE_TYPES, default='ON_SITE') # Location location_city = models.CharField(max_length=100, blank=True) location_state = models.CharField(max_length=100, blank=True) location_country = models.CharField(max_length=100, default='United States') # Job Details description = models.TextField(help_text="Full job description including responsibilities and requirements") qualifications = models.TextField(blank=True, help_text="Required qualifications and skills") salary_range = models.CharField(max_length=200, blank=True, help_text="e.g., $60,000 - $80,000") benefits = models.TextField(blank=True, help_text="Benefits offered") # Application Information application_url = models.URLField(validators=[URLValidator()], help_text="URL where candidates apply") application_deadline = models.DateField(null=True, blank=True) application_instructions = models.TextField(blank=True, help_text="Special instructions for applicants") # Internal Tracking internal_job_id = models.CharField(max_length=50, primary_key=True, editable=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) created_by = models.CharField(max_length=100, blank=True, help_text="Name of person who created this job") # Status Fields STATUS_CHOICES = [ ('DRAFT', 'Draft'), ('ACTIVE', 'Active'), ('CLOSED', 'Closed'), ('ARCHIVED', 'Archived'), ] status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='DRAFT') #hashtags for social media hash_tags = models.CharField(max_length=200, blank=True, help_text="Comma-separated hashtags for linkedin post like #hiring,#jobopening",validators=[validate_hash_tags]) # LinkedIn Integration Fields linkedin_post_id = models.CharField(max_length=200, blank=True, help_text="LinkedIn post ID after posting") linkedin_post_url = models.URLField(blank=True, help_text="Direct URL to LinkedIn post") posted_to_linkedin = models.BooleanField(default=False) linkedin_post_status = models.CharField(max_length=50, blank=True, help_text="Status of LinkedIn posting") linkedin_posted_at = models.DateTimeField(null=True, blank=True) # University Specific Fields position_number = models.CharField(max_length=50, blank=True, help_text="University position number") reporting_to = models.CharField(max_length=100, blank=True, help_text="Who this position reports to") start_date = models.DateField(null=True, blank=True, help_text="Desired start date") open_positions = models.PositiveIntegerField(default=1, help_text="Number of open positions for this job") class Meta: ordering = ['-created_at'] verbose_name = "Job Posting" verbose_name_plural = "Job Postings" def __str__(self): return f"{self.title} - {self.get_status_display()}" def save(self, *args, **kwargs): # Generate unique internal job ID if not exists if not self.internal_job_id: prefix = "UNIV" year = timezone.now().year # Get next sequential number last_job = JobPosting.objects.filter( internal_job_id__startswith=f"{prefix}-{year}-" ).order_by('internal_job_id').last() if last_job: last_num = int(last_job.internal_job_id.split('-')[-1]) next_num = last_num + 1 else: next_num = 1 self.internal_job_id = f"{prefix}-{year}-{next_num:04d}" super().save(*args, **kwargs) def get_location_display(self): """Return formatted location string""" parts = [] if self.location_city: parts.append(self.location_city) if self.location_state: parts.append(self.location_state) if self.location_country and self.location_country != 'United States': parts.append(self.location_country) return ', '.join(parts) if parts else 'Not specified' def is_expired(self): """Check if application deadline has passed""" if self.application_deadline: return self.application_deadline < timezone.now().date() return False def image_file_name(instance, filename): ext = os.path.splitext(filename)[-1] new_filename = f'{instance.id}{ext}' return f'uploads/linkedinpost/images/{new_filename}' class PostImageUpload(models.Model): id = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False, unique=True ) job_posting = models.ForeignKey(JobPosting, on_delete=models.CASCADE, related_name='files') linkedinpost_image=models.ImageField(upload_to=image_file_name, blank=True, null=True,validators=[validate_image_size], help_text="Image file (Max size: 2MB). Accepted formats: .png, .jpg, .jpeg") uploaded_at = models.DateTimeField(auto_now_add=True) class Meta: verbose_name = "Job File" verbose_name_plural = "Job Files" def __str__(self): return f"File for {self.job_posting.title}-{self.job_posting.internal_job_id} - {self.file.name}" # class JobApplication(models.Model): # # Job reference # job_posting = models.ForeignKey(JobPosting, on_delete=models.CASCADE,related_name='applications') # # Personal Information # first_name = models.CharField(max_length=100) # last_name = models.CharField(max_length=100) # email = models.EmailField() # phone = models.CharField(max_length=20, blank=True) # # Resume and Documents # resume = models.FileField(upload_to='resumes/', help_text="PDF or Word document") # cover_letter = models.FileField(upload_to='cover_letters/', blank=True) # # Additional Information # linkedin_profile = models.URLField(blank=True, help_text="Your LinkedIn profile URL") # portfolio_url = models.URLField(blank=True, help_text="Portfolio or personal website") # salary_expectations = models.CharField(max_length=100, blank=True) # availability = models.CharField(max_length=100, blank=True) # # Application Details # applied_at = models.DateTimeField(auto_now_add=True) # status = models.CharField(max_length=20, default='RECEIVED', choices=[ # ('RECEIVED', 'Received'), # ('UNDER_REVIEW', 'Under Review'), # ('INTERVIEW', 'Interview Scheduled'), # ('OFFER', 'Offer Extended'), # ('REJECTED', 'Rejected'), # ('HIRED', 'Hired'), # ]) # class Meta: # ordering = ['-applied_at'] # verbose_name = "Job Application" # verbose_name_plural = "Job Applications" # def __str__(self): # return f"{self.first_name} {self.last_name} - {self.job_posting.title}"