remove gevent monkey-patch from feed-downloader
[mygpo.git] / mygpo / admin / views.py
bloba586c73420e4057e35d419fcf827def11d1e9d27
1 import re
2 import socket
3 from itertools import count
4 from collections import Counter
6 import django
7 from django.shortcuts import render
8 from django.contrib import messages
9 from django.core.urlresolvers import reverse
10 from django.http import HttpResponseRedirect
11 from django.utils.translation import ugettext as _
12 from django.views.generic import TemplateView
13 from django.utils.decorators import method_decorator
14 from django.conf import settings
16 from mygpo.admin.auth import require_staff
17 from mygpo.admin.group import PodcastGrouper
18 from mygpo.maintenance.merge import PodcastMerger, IncorrectMergeException
19 from mygpo.users.models import User
20 from mygpo.admin.clients import UserAgentStats, ClientStats
21 from mygpo.admin.tasks import merge_podcasts
22 from mygpo.utils import get_git_head
23 from mygpo.api.httpresponse import JsonResponse
24 from mygpo.cel import celery
25 from mygpo.db.couchdb import get_main_database
26 from mygpo.db.couchdb.episode import episode_count, filetype_stats
27 from mygpo.db.couchdb.podcast import podcast_count, podcast_for_url
30 class InvalidPodcast(Exception):
31 """ raised when we try to merge a podcast that doesn't exist """
33 class AdminView(TemplateView):
35 @method_decorator(require_staff)
36 def dispatch(self, *args, **kwargs):
37 return super(AdminView, self).dispatch(*args, **kwargs)
40 class Overview(AdminView):
41 template_name = 'admin/overview.html'
44 class HostInfo(AdminView):
45 """ shows host information for diagnosis """
47 template_name = 'admin/hostinfo.html'
49 def get(self, request):
50 commit, msg = get_git_head()
51 base_dir = settings.BASE_DIR
52 hostname = socket.gethostname()
53 django_version = django.VERSION
55 main_db = get_main_database()
57 db_tasks = main_db.server.active_tasks()
59 i = celery.control.inspect()
60 num_celery_tasks = len(i.scheduled() or [])
62 return self.render_to_response({
63 'git_commit': commit,
64 'git_msg': msg,
65 'base_dir': base_dir,
66 'hostname': hostname,
67 'django_version': django_version,
68 'main_db': main_db.uri,
69 'db_tasks': db_tasks,
70 'num_celery_tasks': num_celery_tasks,
75 class MergeSelect(AdminView):
76 template_name = 'admin/merge-select.html'
78 def get(self, request):
79 num = int(request.GET.get('podcasts', 2))
80 urls = [''] * num
82 return self.render_to_response({
83 'urls': urls,
87 class MergeBase(AdminView):
89 def _get_podcasts(self, request):
90 podcasts = []
91 for n in count():
92 podcast_url = request.POST.get('feed%d' % n, None)
93 if podcast_url is None:
94 break
96 if not podcast_url:
97 continue
99 podcast = podcast_for_url(podcast_url)
101 if not podcast:
102 raise InvalidPodcast(podcast_url)
104 podcasts.append(podcast_for_url(podcast_url))
106 return podcasts
109 class MergeVerify(MergeBase):
111 template_name = 'admin/merge-grouping.html'
113 def post(self, request):
115 try:
116 podcasts = self._get_podcasts(request)
118 except InvalidPodcast as ip:
119 messages.error(request,
120 _('No podcast with URL {url}').format(url=str(ip)))
122 grouper = PodcastGrouper(podcasts)
124 get_features = lambda (e_id, e): ((e.url, e.title), e_id)
126 num_groups = grouper.group(get_features)
128 return self.render_to_response({
129 'podcasts': podcasts,
130 'groups': num_groups,
134 class MergeProcess(MergeBase):
136 RE_EPISODE = re.compile(r'episode_([0-9a-fA-F]{32})')
138 def post(self, request):
140 try:
141 podcasts = self._get_podcasts(request)
143 except InvalidPodcast as ip:
144 messages.error(request,
145 _('No podcast with URL {url}').format(url=str(ip)))
147 grouper = PodcastGrouper(podcasts)
149 features = {}
150 for key, feature in request.POST.items():
151 m = self.RE_EPISODE.match(key)
152 if m:
153 episode_id = m.group(1)
154 features[episode_id] = feature
156 get_features = lambda (e_id, e): (features[e_id], e_id)
158 num_groups = grouper.group(get_features)
160 if 'renew' in request.POST:
161 return render(request, 'admin/merge-grouping.html', {
162 'podcasts': podcasts,
163 'groups': num_groups,
167 elif 'merge' in request.POST:
169 podcast_ids = [p.get_id() for p in podcasts]
170 num_groups = list(num_groups)
172 res = merge_podcasts.delay(podcast_ids, num_groups)
174 return HttpResponseRedirect(reverse('admin-merge-status',
175 args=[res.task_id]))
178 class MergeStatus(AdminView):
179 """ Displays the status of the merge operation """
181 template_name = 'admin/merge-status.html'
183 def get(self, request, task_id):
184 result = merge_podcasts.AsyncResult(task_id)
186 if not result.ready():
187 return self.render_to_response({
188 'ready': False,
192 try:
193 actions, podcast = result.get()
195 except IncorrectMergeException as ime:
196 messages.error(request, str(ime))
197 return HttpResponseRedirect(reverse('admin-merge'))
199 return render(request, 'admin/merge-status.html', {
200 'ready': True,
201 'actions': actions.items(),
202 'podcast': podcast,
207 class UserAgentStatsView(AdminView):
208 template_name = 'admin/useragents.html'
210 def get(self, request):
212 uas = UserAgentStats()
213 useragents = uas.get_entries()
215 return self.render_to_response({
216 'useragents': useragents.most_common(),
217 'max_users': uas.max_users,
218 'total': uas.total_users,
222 class ClientStatsView(AdminView):
223 template_name = 'admin/clients.html'
225 def get(self, request):
227 cs = ClientStats()
228 clients = cs.get_entries()
230 return self.render_to_response({
231 'clients': clients.most_common(),
232 'max_users': cs.max_users,
233 'total': cs.total_users,
237 class ClientStatsJsonView(AdminView):
238 def get(self, request):
240 cs = ClientStats()
241 clients = cs.get_entries()
243 return JsonResponse(map(self.to_dict, clients.most_common()))
245 def to_dict(self, res):
246 obj, count = res
248 if not isinstance(obj, tuple):
249 return obj, count
251 return obj._asdict(), count
254 class StatsView(AdminView):
255 """ shows general stats as HTML page """
257 template_name = 'admin/stats.html'
259 def _get_stats(self):
260 return {
261 'podcasts': podcast_count(),
262 'episodes': episode_count(),
263 'users': User.count(),
266 def get(self, request):
267 stats = self._get_stats()
268 return self.render_to_response({
269 'stats': stats,
273 class StatsJsonView(StatsView):
274 """ provides general stats as JSON """
276 def get(self, request):
277 stats = self._get_stats()
278 return JsonResponse(stats)
281 class FiletypeStatsView(AdminView):
283 template_name = 'admin/filetypes.html'
285 def get(self, request):
286 stats = filetype_stats()
288 if len(stats):
289 max_num = stats.most_common(1)[0][1]
290 else:
291 max_num = 0
293 return self.render_to_response({
294 'max_num': max_num,
295 'stats': stats.most_common(),