fix path in generate_commits.py
[mygpo.git] / mygpo / admin / views.py
blob78a720aa85cbaf49d143bcde26d22a7528c2088d
1 import re
2 from itertools import count
4 from django.shortcuts import render
5 from django.contrib import messages
6 from django.core.urlresolvers import reverse
7 from django.http import HttpResponseRedirect
8 from django.utils.translation import ugettext as _
9 from django.views.generic import TemplateView
10 from django.utils.decorators import method_decorator
12 from mygpo.admin.auth import require_staff
13 from mygpo.admin.group import PodcastGrouper
14 from mygpo.counter import Counter
15 from mygpo.maintenance.merge import PodcastMerger, IncorrectMergeException
16 from mygpo.users.models import User
17 from mygpo.admin.clients import UserAgentStats, ClientStats
18 from mygpo.admin.tasks import merge_podcasts
19 from mygpo.api.httpresponse import JsonResponse
20 from mygpo.db.couchdb.episode import episode_count
21 from mygpo.db.couchdb.podcast import podcast_count, podcast_for_url
24 class InvalidPodcast(Exception):
25 """ raised when we try to merge a podcast that doesn't exist """
27 class AdminView(TemplateView):
29 @method_decorator(require_staff)
30 def dispatch(self, *args, **kwargs):
31 return super(AdminView, self).dispatch(*args, **kwargs)
34 class Overview(AdminView):
35 template_name = 'admin/overview.html'
38 class MergeSelect(AdminView):
39 template_name = 'admin/merge-select.html'
41 def get(self, request):
42 num = int(request.GET.get('podcasts', 2))
43 urls = [''] * num
45 return self.render_to_response({
46 'urls': urls,
50 class MergeBase(AdminView):
52 def _get_podcasts(self, request):
53 podcasts = []
54 for n in count():
55 podcast_url = request.POST.get('feed%d' % n, None)
56 if podcast_url is None:
57 break
59 if not podcast_url:
60 continue
62 podcast = podcast_for_url(podcast_url)
64 if not podcast:
65 raise InvalidPodcast(podcast_url)
67 podcasts.append(podcast_for_url(podcast_url))
69 return podcasts
72 class MergeVerify(MergeBase):
74 template_name = 'admin/merge-grouping.html'
76 def post(self, request):
78 try:
79 podcasts = self._get_podcasts(request)
81 except InvalidPodcast as ip:
82 messages.error(request,
83 _('No podcast with URL {url}').format(url=str(ip)))
85 grouper = PodcastGrouper(podcasts)
87 get_features = lambda (e_id, e): ((e.url, e.title), e_id)
89 num_groups = grouper.group(get_features)
91 return self.render_to_response({
92 'podcasts': podcasts,
93 'groups': num_groups,
97 class MergeProcess(MergeBase):
99 RE_EPISODE = re.compile(r'episode_([0-9a-fA-F]{32})')
101 def post(self, request):
103 try:
104 podcasts = self._get_podcasts(request)
106 except InvalidPodcast as ip:
107 messages.error(request,
108 _('No podcast with URL {url}').format(url=str(ip)))
110 grouper = PodcastGrouper(podcasts)
112 features = {}
113 for key, feature in request.POST.items():
114 m = self.RE_EPISODE.match(key)
115 if m:
116 episode_id = m.group(1)
117 features[episode_id] = feature
119 get_features = lambda (e_id, e): (features[e_id], e_id)
121 num_groups = grouper.group(get_features)
123 if 'renew' in request.POST:
124 return render(request, 'admin/merge-grouping.html', {
125 'podcasts': podcasts,
126 'groups': num_groups,
130 elif 'merge' in request.POST:
132 podcast_ids = [p.get_id() for p in podcasts]
133 num_groups = list(num_groups)
135 res = merge_podcasts.delay(podcast_ids, num_groups)
137 return HttpResponseRedirect(reverse('admin-merge-status',
138 args=[res.task_id]))
141 class MergeStatus(AdminView):
142 """ Displays the status of the merge operation """
144 template_name = 'admin/merge-status.html'
146 def get(self, request, task_id):
147 result = merge_podcasts.AsyncResult(task_id)
149 if not result.ready():
150 return self.render_to_response({
151 'ready': False,
155 try:
156 actions, podcast = result.get()
158 except IncorrectMergeException as ime:
159 messages.error(request, str(ime))
160 return HttpResponseRedirect(reverse('admin-merge'))
162 return render(request, 'admin/merge-status.html', {
163 'ready': True,
164 'actions': actions.items(),
165 'podcast': podcast,
170 class UserAgentStatsView(AdminView):
171 template_name = 'admin/useragents.html'
173 def get(self, request):
175 uas = UserAgentStats()
176 useragents = uas.get_entries()
178 return self.render_to_response({
179 'useragents': useragents.most_common(),
180 'max_users': uas.max_users,
181 'total': uas.total_users,
185 class ClientStatsView(AdminView):
186 template_name = 'admin/clients.html'
188 def get(self, request):
190 cs = ClientStats()
191 clients = cs.get_entries()
193 return self.render_to_response({
194 'clients': clients.most_common(),
195 'max_users': cs.max_users,
196 'total': cs.total_users,
200 class ClientStatsJsonView(AdminView):
201 def get(self, request):
203 cs = ClientStats()
204 clients = cs.get_entries()
206 return JsonResponse(map(self.to_dict, clients.most_common()))
208 def to_dict(self, res):
209 obj, count = res
211 if not isinstance(obj, tuple):
212 return obj, count
214 return obj._asdict(), count
217 class StatsView(AdminView):
218 """ shows general stats as HTML page """
220 template_name = 'admin/stats.html'
222 def _get_stats(self):
223 return {
224 'podcasts': podcast_count(),
225 'episodes': episode_count(),
226 'users': User.count(),
229 def get(self, request):
230 stats = self._get_stats()
231 return self.render_to_response({
232 'stats': stats,
236 class StatsJsonView(StatsView):
237 """ provides general stats as JSON """
239 def get(self, request):
240 stats = self._get_stats()
241 return JsonResponse(stats)