mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 08:11:08 -05:00
Add comment and reply functionality with preview and notification templates
This commit is contained in:
@@ -1,238 +1,144 @@
|
||||
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.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, VersionTag, ChangeSet
|
||||
from .managers import BranchManager, ChangeTracker, MergeStrategy
|
||||
from .models import VersionBranch, ChangeSet, VersionTag, CommentThread
|
||||
from .managers import ChangeTracker
|
||||
from .comparison import ComparisonEngine
|
||||
from .state_machine import ApprovalStateMachine
|
||||
|
||||
class VersionControlPanel(LoginRequiredMixin, TemplateView):
|
||||
"""Main version control interface"""
|
||||
template_name = 'history_tracking/version_control_panel.html'
|
||||
ITEMS_PER_PAGE = 20
|
||||
|
||||
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"""
|
||||
@login_required
|
||||
def version_comparison(request: HttpRequest) -> HttpResponse:
|
||||
"""View for comparing different versions"""
|
||||
versions = VersionTag.objects.all().order_by('-created_at')
|
||||
|
||||
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"""
|
||||
version1_id = request.GET.get('version1')
|
||||
version2_id = request.GET.get('version2')
|
||||
page_number = request.GET.get('page', 1)
|
||||
|
||||
def get(self, request):
|
||||
branch_name = request.GET.get('branch')
|
||||
if not branch_name:
|
||||
return HttpResponse("No branch selected")
|
||||
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)
|
||||
|
||||
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")
|
||||
# Get comparison results
|
||||
engine = ComparisonEngine()
|
||||
diff_result = engine.compute_enhanced_diff(version1, version2)
|
||||
|
||||
content = render_to_string(
|
||||
'history_tracking/components/merge_panel.html',
|
||||
# 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 = [
|
||||
{
|
||||
'source': source,
|
||||
'target': target
|
||||
'name': 'Technical Review',
|
||||
'required_roles': ['tech_reviewer']
|
||||
},
|
||||
request=request
|
||||
)
|
||||
return HttpResponse(content)
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
source_name = request.POST.get('source')
|
||||
target_name = request.POST.get('target')
|
||||
{
|
||||
'name': 'Final Approval',
|
||||
'required_roles': ['approver']
|
||||
}
|
||||
]
|
||||
|
||||
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
|
||||
)
|
||||
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
|
||||
})
|
||||
|
||||
class BranchCreateView(LoginRequiredMixin, View):
|
||||
"""HTMX view for branch creation"""
|
||||
@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()
|
||||
|
||||
def get(self, request):
|
||||
content = render_to_string(
|
||||
'history_tracking/components/branch_create.html',
|
||||
request=request
|
||||
)
|
||||
return HttpResponse(content)
|
||||
context = {
|
||||
'changeset': changeset,
|
||||
'current_stage': current_stage,
|
||||
'can_approve': state_machine.can_user_approve(request.user),
|
||||
'pending_approvers': state_machine.get_pending_approvers()
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
return render(request, 'history_tracking/approval_status.html', context)
|
||||
|
||||
class TagCreateView(LoginRequiredMixin, View):
|
||||
"""HTMX view for version tagging"""
|
||||
@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)
|
||||
|
||||
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')
|
||||
try:
|
||||
decision = request.POST.get('decision', 'approve')
|
||||
comment = request.POST.get('comment', '')
|
||||
stage_id = request.POST.get('stage_id')
|
||||
|
||||
if not (name and branch_name):
|
||||
return JsonResponse(
|
||||
{'error': 'Tag name and branch required'},
|
||||
status=400
|
||||
)
|
||||
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")
|
||||
|
||||
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
|
||||
)
|
||||
except Exception as e:
|
||||
messages.error(request, f"Error processing approval: {str(e)}")
|
||||
|
||||
return render(request, 'history_tracking/approval_status.html', {
|
||||
'changeset': changeset
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user