from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator from django.db import transaction from django.http import HttpRequest, HttpResponse from django.utils import timezone from django.contrib import messages from django.views.decorators.http import require_http_methods from django.core.exceptions import PermissionDenied from typing import Dict, Any from .models import VersionBranch, ChangeSet, VersionTag, CommentThread from .managers import ChangeTracker from .comparison import ComparisonEngine from .state_machine import ApprovalStateMachine ITEMS_PER_PAGE = 20 @login_required def version_comparison(request: HttpRequest) -> HttpResponse: """View for comparing different versions""" versions = VersionTag.objects.all().order_by('-created_at') version1_id = request.GET.get('version1') version2_id = request.GET.get('version2') page_number = request.GET.get('page', 1) diff_result = None if version1_id and version2_id: try: version1 = get_object_or_404(VersionTag, id=version1_id) version2 = get_object_or_404(VersionTag, id=version2_id) # Get comparison results engine = ComparisonEngine() diff_result = engine.compute_enhanced_diff(version1, version2) # Paginate changes paginator = Paginator(diff_result['changes'], ITEMS_PER_PAGE) diff_result['changes'] = paginator.get_page(page_number) # Add comments to changes for change in diff_result['changes']: anchor_id = change['metadata']['comment_anchor_id'] change['comments'] = CommentThread.objects.filter( anchor__contains={'id': anchor_id} ).prefetch_related('comments') except Exception as e: messages.error(request, f"Error comparing versions: {str(e)}") context = { 'versions': versions, 'selected_version1': version1_id, 'selected_version2': version2_id, 'diff_result': diff_result } return render(request, 'history_tracking/version_comparison.html', context) @login_required @require_http_methods(["POST"]) @transaction.atomic def submit_for_approval(request: HttpRequest, changeset_id: int) -> HttpResponse: """Submit a changeset for approval""" changeset = get_object_or_404(ChangeSet, pk=changeset_id) if not request.user.has_perm('history_tracking.submit_for_approval'): raise PermissionDenied("You don't have permission to submit changes for approval") try: # Initialize approval workflow state_machine = ApprovalStateMachine(changeset) stages_config = [ { 'name': 'Technical Review', 'required_roles': ['tech_reviewer'] }, { 'name': 'Final Approval', 'required_roles': ['approver'] } ] state_machine.initialize_workflow(stages_config) changeset.status = 'pending_approval' changeset.save() messages.success(request, "Changes submitted for approval successfully") except Exception as e: messages.error(request, f"Error submitting for approval: {str(e)}") return render(request, 'history_tracking/approval_status.html', { 'changeset': changeset }) @login_required def approval_status(request: HttpRequest, changeset_id: int) -> HttpResponse: """View approval status of a changeset""" changeset = get_object_or_404(ChangeSet, pk=changeset_id) state_machine = ApprovalStateMachine(changeset) current_stage = state_machine.get_current_stage() context = { 'changeset': changeset, 'current_stage': current_stage, 'can_approve': state_machine.can_user_approve(request.user), 'pending_approvers': state_machine.get_pending_approvers() } return render(request, 'history_tracking/approval_status.html', context) @login_required @require_http_methods(["POST"]) @transaction.atomic def approve_changes(request: HttpRequest, changeset_id: int) -> HttpResponse: """Submit an approval decision""" changeset = get_object_or_404(ChangeSet, pk=changeset_id) state_machine = ApprovalStateMachine(changeset) try: decision = request.POST.get('decision', 'approve') comment = request.POST.get('comment', '') stage_id = request.POST.get('stage_id') success = state_machine.submit_approval( user=request.user, decision=decision, comment=comment, stage_id=stage_id ) if success: messages.success(request, f"Successfully {decision}d changes") else: messages.error(request, "Failed to submit approval") except Exception as e: messages.error(request, f"Error processing approval: {str(e)}") return render(request, 'history_tracking/approval_status.html', { 'changeset': changeset })