from django.views.generic import TemplateView, View from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404 from django.template.loader import render_to_string from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import ValidationError from django.db import transaction from .models import VersionBranch, VersionTag, ChangeSet from .managers import BranchManager, ChangeTracker, MergeStrategy class VersionControlPanel(LoginRequiredMixin, TemplateView): """Main version control interface""" template_name = 'history_tracking/version_control_panel.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) branch_manager = BranchManager() context.update({ 'branches': branch_manager.list_branches(), 'current_branch': self.request.GET.get('branch'), }) return context class BranchListView(LoginRequiredMixin, View): """HTMX view for branch list""" def get(self, request): branch_manager = BranchManager() branches = branch_manager.list_branches() content = render_to_string( 'history_tracking/components/branch_list.html', {'branches': branches}, request=request ) return HttpResponse(content) class HistoryView(LoginRequiredMixin, View): """HTMX view for change history""" def get(self, request): branch_name = request.GET.get('branch') if not branch_name: return HttpResponse("No branch selected") branch = get_object_or_404(VersionBranch, name=branch_name) tracker = ChangeTracker() changes = tracker.get_changes(branch) content = render_to_string( 'history_tracking/components/history_view.html', {'changes': changes}, request=request ) return HttpResponse(content) class MergeView(LoginRequiredMixin, View): """HTMX view for merge operations""" def get(self, request): source = request.GET.get('source') target = request.GET.get('target') if not (source and target): return HttpResponse("Source and target branches required") content = render_to_string( 'history_tracking/components/merge_panel.html', { 'source': source, 'target': target }, request=request ) return HttpResponse(content) @transaction.atomic def post(self, request): source_name = request.POST.get('source') target_name = request.POST.get('target') if not (source_name and target_name): return JsonResponse({ 'error': 'Source and target branches required' }, status=400) try: source = get_object_or_404(VersionBranch, name=source_name) target = get_object_or_404(VersionBranch, name=target_name) branch_manager = BranchManager() success, conflicts = branch_manager.merge_branches( source=source, target=target, user=request.user ) if success: content = render_to_string( 'history_tracking/components/merge_success.html', {'source': source, 'target': target}, request=request ) return HttpResponse(content) else: content = render_to_string( 'history_tracking/components/merge_conflicts.html', { 'source': source, 'target': target, 'conflicts': conflicts }, request=request ) return HttpResponse(content) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) except Exception as e: return JsonResponse( {'error': 'Merge failed. Please try again.'}, status=500 ) class BranchCreateView(LoginRequiredMixin, View): """HTMX view for branch creation""" def get(self, request): content = render_to_string( 'history_tracking/components/branch_create.html', request=request ) return HttpResponse(content) @transaction.atomic def post(self, request): name = request.POST.get('name') parent_name = request.POST.get('parent') if not name: return JsonResponse({'error': 'Branch name required'}, status=400) try: branch_manager = BranchManager() parent = None if parent_name: parent = get_object_or_404(VersionBranch, name=parent_name) branch = branch_manager.create_branch( name=name, parent=parent, user=request.user ) content = render_to_string( 'history_tracking/components/branch_item.html', {'branch': branch}, request=request ) return HttpResponse(content) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) except Exception as e: return JsonResponse( {'error': 'Branch creation failed. Please try again.'}, status=500 ) class TagCreateView(LoginRequiredMixin, View): """HTMX view for version tagging""" def get(self, request): branch_name = request.GET.get('branch') if not branch_name: return HttpResponse("Branch required") content = render_to_string( 'history_tracking/components/tag_create.html', {'branch_name': branch_name}, request=request ) return HttpResponse(content) @transaction.atomic def post(self, request): name = request.POST.get('name') branch_name = request.POST.get('branch') if not (name and branch_name): return JsonResponse( {'error': 'Tag name and branch required'}, status=400 ) try: branch = get_object_or_404(VersionBranch, name=branch_name) # Get latest historical record for the branch latest_change = ChangeSet.objects.filter( branch=branch, status='applied' ).latest('created_at') if not latest_change: return JsonResponse( {'error': 'No changes to tag'}, status=400 ) tag = VersionTag.objects.create( name=name, branch=branch, historical_record=latest_change.historical_records.latest('history_date'), created_by=request.user, metadata={ 'tagged_at': timezone.now().isoformat(), 'changeset': latest_change.pk } ) content = render_to_string( 'history_tracking/components/tag_item.html', {'tag': tag}, request=request ) return HttpResponse(content) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) except Exception as e: return JsonResponse( {'error': 'Tag creation failed. Please try again.'}, status=500 )