]> git.0d.be Git - panikdb.git/blob - panikdb/views.py
revamp full text search to use postgresql
[panikdb.git] / panikdb / views.py
1 import datetime
2 import logging
3
4 import emissions.views
5 import newsletter.views
6 from combo.data.models import Page
7 from django.conf import settings
8 from django.contrib import messages
9 from django.contrib.auth.decorators import login_required
10 from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
11 from django.core.exceptions import PermissionDenied
12 from django.shortcuts import redirect
13 from django.template import loader
14 from django.urls import reverse
15 from django.views.generic.base import RedirectView, TemplateView
16 from django.views.generic.list import ListView
17 from emissions.models import Diffusion, Emission, Episode, Focus, NewsCategory, NewsItem, SoundFile
18 from emissions.utils import period_program
19
20 from .poll.models import Vote
21 from .service_messages.models import Message
22
23 logger = logging.getLogger('panikdb')
24
25
26 class Home(TemplateView):
27     template_name = 'home.html'
28
29     def get_context_data(self, **kwargs):
30         context = super().get_context_data(**kwargs)
31
32         from .regie.views import RegieHome
33
34         context.update(RegieHome().get_context_data(**kwargs))
35
36         try:
37             vote = Vote.objects.get(user=self.request.user)
38         except Vote.DoesNotExist:
39             context['display_poll_button'] = True
40         context['emissions'] = self.request.user.emissions.all().order_by('title')
41         context['news_categories'] = self.request.user.news_categories.all().order_by('title')
42         if not context['service_message']:
43             context['service_message'] = Message.objects.create(style='info')
44         if not context['service_message'].text and not self.request.user.has_perm(
45             'service_messages.change_message'
46         ):
47             context['service_message'] = None
48         if self.request.user.has_perm('emissions.add_focus'):
49             context['focused_items'] = Focus.objects.select_related().filter(current=True)
50             context['recent_episodes'] = Episode.objects.select_related().order_by('-last_update_timestamp')
51             context['recent_newsitems'] = NewsItem.objects.select_related().order_by('-last_update_timestamp')
52             context['recent_soundfiles'] = SoundFile.objects.select_related().order_by(
53                 '-last_update_timestamp'
54             )
55
56         return context
57
58
59 home = login_required(Home.as_view())
60
61
62 class Search(TemplateView):
63     template_name = 'search/search.html'
64
65     def get_context_data(self, **kwargs):
66         context = super().get_context_data(**kwargs)
67         context['query'] = self.request.GET.get('q', '')
68         vector = (
69             SearchVector('title', config=settings.FTS_DICTIONARY_CONFIG, weight='A')
70             + SearchVector('subtitle', config=settings.FTS_DICTIONARY_CONFIG, weight='A')
71             + SearchVector('text', config=settings.FTS_DICTIONARY_CONFIG, weight='B')
72         )
73         query = SearchQuery(self.request.GET.get('q', ''), config=settings.FTS_DICTIONARY_CONFIG)
74         context['hits'] = {
75             'episodes': Episode.objects.annotate(rank=SearchRank(vector, query))
76             .filter(rank__gte=0.1)
77             .select_related('emission')
78             .order_by('-rank')[:10],
79             'emissions': Emission.objects.annotate(rank=SearchRank(vector, query))
80             .filter(rank__gte=0.1)
81             .order_by('-rank')[:10],
82             'newsitems': NewsItem.objects.annotate(rank=SearchRank(vector, query))
83             .filter(rank__gte=0.1)
84             .order_by('-rank')[:10],
85         }
86         return context
87
88
89 search = Search.as_view()
90
91
92 class EmissionListView(emissions.views.EmissionListView):
93     pass
94
95
96 emissions_list = login_required(EmissionListView.as_view())
97
98
99 class NewsListView(ListView):
100     model = NewsCategory
101
102     def get_queryset(self):
103         return NewsCategory.objects.order_by('title')
104
105     def get_context_data(self, **kwargs):
106         context = super().get_context_data(**kwargs)
107         context['unsorted_newsitems'] = NewsItem.objects.filter(category__isnull=True).order_by('title')
108         return context
109
110
111 news = login_required(NewsListView.as_view())
112
113
114 class NewsletterCreateView(newsletter.views.NewsletterCreateView):
115     def get_initial(self):
116         initial = super().get_initial()
117
118         newsletter_body = loader.get_template('newsletter/body.html')
119         newsletter_subject = loader.get_template('newsletter/subject.txt')
120         context = {}
121
122         date = datetime.datetime.today()
123         if date.weekday() < 3:
124             # before and on Wednesday, use current week
125             date = date - datetime.timedelta(days=date.weekday())
126         elif date.weekday():
127             # after Wednesday, use next week
128             date = date + datetime.timedelta(days=7 - date.weekday())
129
130         date = datetime.datetime(*date.timetuple()[:3])
131         date_end = date + datetime.timedelta(days=7)
132
133         context['date'] = date
134         context['date_end'] = date_end
135
136         context['soundfiles'] = (
137             SoundFile.objects.filter(podcastable=True, fragment=False)
138             .select_related()
139             .filter(creation_timestamp__gte=date - datetime.timedelta(days=7))
140         )
141
142         context['diffusions'] = list(
143             Diffusion.objects.select_related()
144             .filter(datetime__gte=date, datetime__lte=date + datetime.timedelta(days=7))
145             .order_by('datetime')
146         )
147
148         # remove rebroadcasts made during the same week
149         seen_episodes = {}
150         for diff in context['diffusions'][:]:
151             if diff.episode.id in seen_episodes:
152                 context['diffusions'].remove(diff)
153             seen_episodes[diff.episode.id] = True
154
155         initial['subject'] = newsletter_subject.render(context)
156         initial['text'] = newsletter_body.render(context)
157         return initial
158
159
160 newsletter_create = NewsletterCreateView.as_view()
161
162
163 class FocusSetView(RedirectView):
164     permanent = False
165
166     def get_redirect_url(self, object_type, object_id):
167         if not self.request.user.has_perm('emissions.add_focus'):
168             raise PermissionDenied()
169         messages.success(self.request, emissions.views.SUCCESS_MESSAGE)
170         if object_type == 'emission':
171             emission = Emission.objects.get(id=object_id)
172             f, _created = Focus.objects.get_or_create(emission=emission)
173             f.current = True
174             f.save()
175             logger.info('set focus on emission %s', emission)
176             return reverse('emission-view', kwargs={'slug': emission.slug})
177         if object_type == 'episode':
178             episode = Episode.objects.get(id=object_id)
179             f, _created = Focus.objects.get_or_create(episode=episode)
180             f.current = True
181             f.episode = episode
182             f.save()
183             logger.info('set focus on episode %s', episode)
184             return reverse(
185                 'episode-view', kwargs={'emission_slug': episode.emission.slug, 'slug': episode.slug}
186             )
187         if object_type == 'newsitem':
188             newsitem = NewsItem.objects.get(id=object_id)
189             f, _created = Focus.objects.get_or_create(newsitem=newsitem)
190             f.current = True
191             f.newsitem = newsitem
192             f.save()
193             logger.info('set focus on newsitem %s', newsitem)
194             return reverse('newsitem-view', kwargs={'slug': newsitem.slug})
195         if object_type == 'soundfile':
196             soundfile = SoundFile.objects.get(id=object_id)
197             f, _created = Focus.objects.get_or_create(soundfile=soundfile)
198             f.current = True
199             f.soundfile = soundfile
200             f.save()
201             logger.info('set focus on sound %s', soundfile)
202             return reverse(
203                 'episode-view',
204                 kwargs={
205                     'slug': soundfile.episode.slug,
206                     'emission_slug': soundfile.episode.emission.slug,
207                 },
208             )
209         if object_type == 'page':
210             page = Page.objects.get(id=object_id)
211             try:
212                 f = Focus.objects.get(page=page)
213             except Focus.DoesNotExist:
214                 f = Focus()
215             f.current = True
216             f.page = page
217             f.save()
218             logger.info('set focus on page %s', page)
219             return reverse('combo-manager-page-view', kwargs={'pk': object_id})
220
221
222 focus_set = FocusSetView.as_view()
223
224
225 class FocusUnsetView(RedirectView):
226     permanent = False
227
228     def get_redirect_url(self, object_type, object_id):
229         if not self.request.user.has_perm('emissions.delete_focus'):
230             raise PermissionDenied()
231         messages.success(self.request, emissions.views.SUCCESS_MESSAGE)
232         if object_type == 'emission':
233             emission = Emission.objects.get(id=object_id)
234             focus = Focus.objects.get(emission=emission)
235             focus.current = False
236             focus.save()
237             logger.info('unset focus of emission %s', emission)
238             return reverse('emission-view', kwargs={'slug': emission.slug})
239         if object_type == 'episode':
240             episode = Episode.objects.get(id=object_id)
241             focus = Focus.objects.get(episode=episode)
242             focus.current = False
243             focus.save()
244             logger.info('unset focus of episode %s', episode)
245             return reverse(
246                 'episode-view', kwargs={'emission_slug': episode.emission.slug, 'slug': episode.slug}
247             )
248         if object_type == 'newsitem':
249             newsitem = NewsItem.objects.get(id=object_id)
250             focus = Focus.objects.get(newsitem=newsitem)
251             focus.current = False
252             focus.save()
253             logger.info('unset focus of newsitem %s', newsitem)
254             return reverse('newsitem-view', kwargs={'slug': newsitem.slug})
255         if object_type == 'soundfile':
256             soundfile = SoundFile.objects.get(id=object_id)
257             focus = Focus.objects.get(soundfile=soundfile)
258             focus.current = False
259             focus.save()
260             logger.info('unset focus of soundfile %s', soundfile)
261             return reverse(
262                 'episode-view',
263                 kwargs={
264                     'slug': soundfile.episode.slug,
265                     'emission_slug': soundfile.episode.emission.slug,
266                 },
267             )
268         if object_type == 'page':
269             focus = Focus.objects.get(page=object_id)
270             focus.current = False
271             focus.save()
272             logger.info('unset focus of page %s', object_id)
273             return reverse('combo-manager-page-view', kwargs={'pk': object_id})
274
275
276 focus_unset = FocusUnsetView.as_view()