kaauh_ats/recruitment/views_source.py
2025-11-02 13:42:23 +03:00

279 lines
9.8 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, CreateView, UpdateView, DetailView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.urls import reverse_lazy
from django.contrib import messages
from django.db import transaction
from django.http import JsonResponse
from django.db import models
import secrets
import string
from .models import Source, IntegrationLog
from .forms import SourceForm, generate_api_key, generate_api_secret
class SourceListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
"""List all sources"""
model = Source
template_name = 'recruitment/source_list.html'
context_object_name = 'sources'
paginate_by = 10
def test_func(self):
return self.request.user.is_staff
def get_queryset(self):
queryset = super().get_queryset().order_by('name')
# Search functionality
search_query = self.request.GET.get('search', '')
if search_query:
queryset = queryset.filter(
models.Q(name__icontains=search_query) |
models.Q(source_type__icontains=search_query) |
models.Q(description__icontains=search_query)
)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['search_query'] = self.request.GET.get('search', '')
return context
class SourceCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
"""Create a new source"""
model = Source
form_class = SourceForm
template_name = 'recruitment/source_form.html'
success_url = reverse_lazy('source_list')
def test_func(self):
return self.request.user.is_staff
def form_valid(self, form):
# Set initial values
form.instance.created_by = self.request.user.get_full_name() or self.request.user.username
# Check if we need to generate API keys
if form.cleaned_data.get('generate_keys') == 'true':
form.instance.api_key = generate_api_key()
form.instance.api_secret = generate_api_secret()
# Log the key generation
IntegrationLog.objects.create(
source=form.instance,
action=IntegrationLog.ActionChoices.CREATE,
endpoint='/api/sources/',
method='POST',
request_data={'name': form.instance.name},
ip_address=self.request.META.get('REMOTE_ADDR'),
user_agent=self.request.META.get('HTTP_USER_AGENT', '')
)
response = super().form_valid(form)
# Add success message
messages.success(self.request, f'Source "{form.instance.name}" created successfully!')
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Create New Source'
context['generate_keys'] = self.request.GET.get('generate_keys', 'false')
return context
class SourceDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
"""View source details"""
model = Source
template_name = 'recruitment/source_detail.html'
context_object_name = 'source'
def test_func(self):
return self.request.user.is_staff
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Mask API keys in display
source = self.object
if source.api_key:
masked_key = source.api_key[:8] + '*' * 24
context['masked_api_key'] = masked_key
else:
context['masked_api_key'] = 'Not generated'
if source.api_secret:
masked_secret = source.api_secret[:12] + '*' * 52
context['masked_api_secret'] = masked_secret
else:
context['masked_api_secret'] = 'Not generated'
# Get recent integration logs
context['recent_logs'] = IntegrationLog.objects.filter(
source=source
).order_by('-created_at')[:10]
return context
class SourceUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
"""Update an existing source"""
model = Source
form_class = SourceForm
template_name = 'recruitment/source_form.html'
success_url = reverse_lazy('source_list')
def test_func(self):
return self.request.user.is_staff
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'Edit Source: {self.object.name}'
context['generate_keys'] = self.request.GET.get('generate_keys', 'false')
return context
def form_valid(self, form):
# Check if we need to generate new API keys
if form.cleaned_data.get('generate_keys') == 'true':
form.instance.api_key = generate_api_key()
form.instance.api_secret = generate_api_secret()
# Log the key regeneration
IntegrationLog.objects.create(
source=self.object,
action=IntegrationLog.ActionChoices.CREATE,
endpoint=f'/api/sources/{self.object.pk}/',
method='PUT',
request_data={'name': form.instance.name, 'regenerated_keys': True},
ip_address=self.request.META.get('REMOTE_ADDR'),
user_agent=self.request.META.get('HTTP_USER_AGENT', '')
)
messages.success(self.request, 'New API keys generated successfully!')
response = super().form_valid(form)
messages.success(self.request, f'Source "{form.instance.name}" updated successfully!')
return response
class SourceDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
"""Delete a source"""
model = Source
template_name = 'recruitment/source_confirm_delete.html'
success_url = reverse_lazy('source_list')
def test_func(self):
return self.request.user.is_staff
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
# Log the deletion
IntegrationLog.objects.create(
source=self.object,
action=IntegrationLog.ActionChoices.SYNC, # Using SYNC for deletion
endpoint=f'/api/sources/{self.object.pk}/',
method='DELETE',
request_data={'name': self.object.name},
ip_address=self.request.META.get('REMOTE_ADDR'),
user_agent=self.request.META.get('HTTP_USER_AGENT', '')
)
messages.success(request, f'Source "{self.object.name}" deleted successfully!')
return super().delete(request, *args, **kwargs)
def generate_api_keys_view(request, pk):
"""Generate new API keys for a specific source"""
if not request.user.is_staff:
return JsonResponse({'error': 'Permission denied'}, status=403)
try:
source = get_object_or_404(Source, pk=pk)
except Source.DoesNotExist:
return JsonResponse({'error': 'Source not found'}, status=404)
if request.method == 'POST':
# Generate new API keys
new_api_key = generate_api_key()
new_api_secret = generate_api_secret()
# Update the source with new keys
old_api_key = source.api_key
source.api_key = new_api_key
source.api_secret = new_api_secret
source.save()
# Log the key regeneration
IntegrationLog.objects.create(
source=source,
action=IntegrationLog.ActionChoices.CREATE,
endpoint=f'/api/sources/{source.pk}/generate-keys/',
method='POST',
request_data={
'name': source.name,
'old_api_key': old_api_key[:8] + '...' if old_api_key else None,
'new_api_key': new_api_key[:8] + '...'
},
ip_address=request.META.get('REMOTE_ADDR'),
user_agent=request.META.get('HTTP_USER_AGENT', '')
)
return JsonResponse({
'success': True,
'api_key': new_api_key,
'api_secret': new_api_secret,
'message': 'API keys regenerated successfully'
})
return JsonResponse({'error': 'Invalid request method'}, status=405)
def toggle_source_status_view(request, pk):
"""Toggle the active status of a source"""
if not request.user.is_staff:
return JsonResponse({'error': 'Permission denied'}, status=403)
try:
source = get_object_or_404(Source, pk=pk)
except Source.DoesNotExist:
return JsonResponse({'error': 'Source not found'}, status=404)
if request.method == 'POST':
# Toggle the status
old_status = source.is_active
source.is_active = not source.is_active
source.save()
# Log the status change
IntegrationLog.objects.create(
source=source,
action=IntegrationLog.ActionChoices.SYNC,
endpoint=f'/api/sources/{source.pk}/toggle-status/',
method='POST',
request_data={
'name': source.name,
'old_status': old_status,
'new_status': source.is_active
},
ip_address=request.META.get('REMOTE_ADDR'),
user_agent=request.META.get('HTTP_USER_AGENT', '')
)
status_text = 'activated' if source.is_active else 'deactivated'
return JsonResponse({
'success': True,
'is_active': source.is_active,
'message': f'Source "{source.name}" {status_text} successfully'
})
def copy_to_clipboard_view(request):
"""HTMX endpoint to copy text to clipboard"""
if request.method == 'POST':
text_to_copy = request.POST.get('text', '')
return render(request, 'includes/copy_to_clipboard.html', {
'text': text_to_copy
})
return JsonResponse({'error': 'Invalid request method'}, status=405)