190 lines
7.0 KiB
Python
190 lines
7.0 KiB
Python
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.views import View
|
|
from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.urls import reverse_lazy, reverse
|
|
from django.db.models import Q
|
|
from django.db import transaction
|
|
from django.http import JsonResponse, HttpResponseRedirect
|
|
from .models import Bookmark, Team, Extraction, BookmarkShare, Summary
|
|
from .tasks import process_bookmark
|
|
|
|
class BookmarkListView(LoginRequiredMixin, ListView):
|
|
model = Bookmark
|
|
template_name = 'core/index.html'
|
|
context_object_name = 'bookmarks'
|
|
paginate_by = 20
|
|
|
|
def get_queryset(self):
|
|
queryset = Bookmark.objects.filter(user=self.request.user).order_by('-created_at')
|
|
|
|
# Search filter
|
|
query = self.request.GET.get('q')
|
|
if query:
|
|
queryset = queryset.filter(
|
|
Q(title__icontains=query) |
|
|
Q(url__icontains=query) |
|
|
Q(notes__icontains=query) |
|
|
Q(extraction__content_text__icontains=query)
|
|
).distinct()
|
|
|
|
# Tag filter
|
|
tag = self.request.GET.get('tag')
|
|
if tag:
|
|
queryset = queryset.filter(tags__name__in=[tag])
|
|
|
|
return queryset
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
# Add all tags used by the user for a sidebar or filter list
|
|
from taggit.models import Tag
|
|
context['all_tags'] = Tag.objects.filter(bookmark__user=self.request.user).distinct()
|
|
context['teams'] = self.request.user.teams.all()
|
|
return context
|
|
|
|
class BookmarkCreateView(LoginRequiredMixin, CreateView):
|
|
model = Bookmark
|
|
fields = ['url', 'title', 'notes', 'is_favorite']
|
|
template_name = 'core/bookmark_form.html'
|
|
success_url = reverse_lazy('home')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.user = self.request.user
|
|
# Save first to get the object
|
|
self.object = form.save()
|
|
|
|
# Handle tags if provided in a separate field or as a comma-separated string
|
|
tags = self.request.POST.get('tags_input')
|
|
if tags:
|
|
self.object.tags.add(*[t.strip() for t in tags.split(',')])
|
|
|
|
# Trigger background task
|
|
process_bookmark.delay(self.object.id)
|
|
|
|
if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
|
return JsonResponse({'status': 'success', 'redirect_url': str(self.success_url)})
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
|
|
class BookmarkUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = Bookmark
|
|
fields = ['url', 'title', 'notes', 'is_favorite']
|
|
template_name = 'core/bookmark_form.html'
|
|
|
|
def get_success_url(self):
|
|
return reverse('bookmark-detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
# Handle tags update
|
|
tags_input = self.request.POST.get('tags_input', '')
|
|
|
|
with transaction.atomic():
|
|
self.object = form.save()
|
|
|
|
# Clear existing tags and set new ones
|
|
self.object.tags.clear()
|
|
if tags_input:
|
|
tag_names = [t.strip() for t in tags_input.split(',') if t.strip()]
|
|
if tag_names:
|
|
self.object.tags.add(*tag_names)
|
|
|
|
return HttpResponseRedirect(self.get_success_url())
|
|
|
|
def get_queryset(self):
|
|
return Bookmark.objects.filter(user=self.request.user)
|
|
|
|
class BookmarkDeleteView(LoginRequiredMixin, DeleteView):
|
|
model = Bookmark
|
|
success_url = reverse_lazy('home')
|
|
|
|
def get_queryset(self):
|
|
return Bookmark.objects.filter(user=self.request.user)
|
|
|
|
class BookmarkDetailView(LoginRequiredMixin, DetailView):
|
|
model = Bookmark
|
|
template_name = 'core/bookmark_detail.html'
|
|
context_object_name = 'bookmark'
|
|
|
|
def get_queryset(self):
|
|
# Allow viewing if it's the user's bookmark OR shared with one of their teams
|
|
user_teams = self.request.user.teams.all()
|
|
return Bookmark.objects.filter(
|
|
Q(user=self.request.user) |
|
|
Q(shares__team__in=user_teams)
|
|
).distinct()
|
|
|
|
class BookmarkRegenerateView(LoginRequiredMixin, View):
|
|
def post(self, request, pk):
|
|
bookmark = get_object_or_404(Bookmark, pk=pk, user=request.user)
|
|
# Delete existing summary and extraction to force regeneration and show loading states
|
|
if hasattr(bookmark, 'summary'):
|
|
bookmark.summary.delete()
|
|
if hasattr(bookmark, 'extraction'):
|
|
bookmark.extraction.delete()
|
|
|
|
process_bookmark.delay(bookmark.id)
|
|
return HttpResponseRedirect(reverse('bookmark-detail', args=[pk]))
|
|
|
|
class SummaryUpdateView(LoginRequiredMixin, View):
|
|
def post(self, request, pk):
|
|
bookmark = get_object_or_404(Bookmark, pk=pk, user=request.user)
|
|
content = request.POST.get('content')
|
|
if content:
|
|
Summary.objects.update_or_create(
|
|
bookmark=bookmark,
|
|
defaults={'content': content}
|
|
)
|
|
return HttpResponseRedirect(reverse('bookmark-detail', args=[pk]))
|
|
|
|
class ExtractionUpdateView(LoginRequiredMixin, View):
|
|
def post(self, request, pk):
|
|
bookmark = get_object_or_404(Bookmark, pk=pk, user=request.user)
|
|
content_text = request.POST.get('content_text')
|
|
if content_text:
|
|
Extraction.objects.update_or_create(
|
|
bookmark=bookmark,
|
|
defaults={'content_text': content_text}
|
|
)
|
|
return HttpResponseRedirect(reverse('bookmark-detail', args=[pk]))
|
|
|
|
class TeamListView(LoginRequiredMixin, ListView):
|
|
model = Team
|
|
template_name = 'core/team_list.html'
|
|
context_object_name = 'teams'
|
|
|
|
def get_queryset(self):
|
|
return self.request.user.teams.all()
|
|
|
|
class TeamDetailView(LoginRequiredMixin, DetailView):
|
|
model = Team
|
|
template_name = 'core/team_detail.html'
|
|
context_object_name = 'team'
|
|
|
|
def get_queryset(self):
|
|
return self.request.user.teams.all()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
# Get bookmarks shared with this team
|
|
context['shared_bookmarks'] = Bookmark.objects.filter(shares__team=self.object).order_by('-shares__shared_at')
|
|
return context
|
|
|
|
class BookmarkShareToggleView(LoginRequiredMixin, View):
|
|
def post(self, request, pk, team_id):
|
|
bookmark = get_object_or_404(Bookmark, pk=pk, user=request.user)
|
|
team = get_object_or_404(Team, pk=team_id, members=request.user)
|
|
|
|
share, created = BookmarkShare.objects.get_or_create(
|
|
bookmark=bookmark,
|
|
team=team,
|
|
defaults={'shared_by': request.user}
|
|
)
|
|
|
|
if not created:
|
|
share.delete()
|
|
shared = False
|
|
else:
|
|
shared = True
|
|
|
|
return JsonResponse({'shared': shared}) |