from django import forms from . import models from django.core.validators import URLValidator from django.core.exceptions import ValidationError # Correct import for the exception from .validators import validate_hash_tags class JobPostingForm(forms.ModelForm): """Form for creating and editing job postings""" class Meta: model = models.JobPosting fields = [ 'title', 'department', 'job_type', 'workplace_type', 'location_city', 'location_state', 'location_country', 'description', 'qualifications', 'salary_range', 'benefits', 'application_url', 'application_deadline', 'application_instructions', 'position_number', 'reporting_to', 'start_date', 'status', 'created_by','open_positions','hash_tags' ] widgets = { # Basic Information 'title': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Assistant Professor of Computer Science', 'required': True }), 'department': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Computer Science, Human Resources, etc.' }), 'job_type': forms.Select(attrs={ 'class': 'form-select', 'required': True }), 'workplace_type': forms.Select(attrs={ 'class': 'form-select', 'required': True }), # Location 'location_city': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Boston' }), 'location_state': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'MA' }), 'location_country': forms.TextInput(attrs={ 'class': 'form-control', 'value': 'United States' }), # Job Details 'description': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 6, 'placeholder': 'Provide a comprehensive description of the role, responsibilities, and expectations...', 'required': True }), 'qualifications': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 4, 'placeholder': 'List required qualifications, skills, education, and experience...' }), 'salary_range': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': '$60,000 - $80,000' }), 'benefits': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 2, 'placeholder': 'Health insurance, retirement plans, tuition reimbursement, etc.' }), # Application Information 'application_url': forms.URLInput(attrs={ 'class': 'form-control', 'placeholder': 'https://university.edu/careers/job123', 'required': True }), 'application_deadline': forms.DateInput(attrs={ 'class': 'form-control', 'type': 'date' }), 'application_instructions': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 3, 'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)' }), 'open_positions': forms.NumberInput(attrs={ 'class': 'form-control', 'min': 1, 'placeholder': 'Number of open positions' }), 'hash_tags': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': '#hiring,#jobopening', 'validators':validate_hash_tags, }), # Internal Information 'position_number': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'UNIV-2025-001' }), 'reporting_to': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Department Chair, Director, etc.' }), 'start_date': forms.DateInput(attrs={ 'class': 'form-control', 'type': 'date' }), 'created_by': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'University Administrator' }), } def __init__(self,*args,**kwargs): # Extract your custom argument BEFORE calling super() self.is_anonymous_user = kwargs.pop('is_anonymous_user', False) # Now call the parent __init__ with remaining args super().__init__(*args, **kwargs) if not self.instance.pk:# Creating new job posting if not self.is_anonymous_user: self.fields['created_by'].initial = 'University Administrator' self.fields['status'].initial = 'Draft' self.fields['location_city'].initial='Riyadh' self.fields['location_state'].initial='Riyadh Province' self.fields['location_country'].initial='Saudi Arabia' def clean_hash_tags(self): hash_tags=self.cleaned_data.get('hash_tags') if hash_tags: tags=[tag.strip() for tag in hash_tags.split(',') if tag.strip()] for tag in tags: if not tag.startswith('#'): raise forms.ValidationError("Each hashtag must start with '#' symbol and must be comma(,) sepearted.") return ','.join(tags) return hash_tags # Allow blank def clean_title(self): title=self.cleaned_data.get('title') if not title or len(title.strip())<3: raise forms.ValidationError("Job title must be at least 3 characters long.") if len(title)>200: raise forms.ValidationError("Job title cannot exceed 200 characters.") return title.strip() def clean_description(self): description=self.cleaned_data.get('description') if not description or len(description.strip())<20: raise forms.ValidationError("Job description must be at least 20 characters long.") return description.strip() # to remove leading/trailing whitespace def clean_application_url(self): url=self.cleaned_data.get('application_url') if url: validator=URLValidator() try: validator(url) except forms.ValidationError: raise forms.ValidationError('Please enter a valid URL (e.g., https://example.com)') return url def clean(self): """Cross-field validation""" cleaned_data = super().clean() # Validate dates start_date = cleaned_data.get('start_date') application_deadline = cleaned_data.get('application_deadline') # Perform cross-field validation only if both fields have values if start_date and application_deadline: if application_deadline > start_date: self.add_error('application_deadline', 'The application deadline must be set BEFORE the job start date.') # # Validate that if status is ACTIVE, we have required fields # status = cleaned_data.get('status') # if status == 'ACTIVE': # if not cleaned_data.get('application_url'): # self.add_error('application_url', # 'Application URL is required for active jobs.') # if not cleaned_data.get('description'): # self.add_error('description', # 'Job description is required for active jobs.') return cleaned_data class PostImageUploadForm(forms.ModelForm): class Meta: model=models.PostImageUpload fields=['linkedinpost_image'] widgets={ 'linkedinpost_image':forms.ClearableFileInput(attrs={'class':'form-control','accept':'.png,.jpg,.jpeg'}) } def clean_linkedinpost_image(self): linkedinpost_image=self.cleaned_data.get('image') if linkedinpost_image: if linkedinpost_image.size>2*1024*1024:#2MB limit raise forms.ValidationError("Image size should not exceed 2MB.") return linkedinpost_image