94b0db0b31cb7ae57c384269319280ba9e002333
3 from itertools
import count
4 from collections
import Counter
7 from django
.shortcuts
import render
8 from django
.contrib
import messages
9 from django
.core
.urlresolvers
import reverse
10 from django
.core
.cache
import cache
11 from django
.http
import HttpResponseRedirect
12 from django
.utils
.translation
import ugettext
as _
13 from django
.views
.generic
import TemplateView
14 from django
.utils
.decorators
import method_decorator
15 from django
.conf
import settings
17 from mygpo
.admin
.auth
import require_staff
18 from mygpo
.admin
.group
import PodcastGrouper
19 from mygpo
.maintenance
.merge
import PodcastMerger
, IncorrectMergeException
20 from mygpo
.users
.models
import User
21 from mygpo
.admin
.clients
import UserAgentStats
, ClientStats
22 from mygpo
.admin
.tasks
import merge_podcasts
23 from mygpo
.utils
import get_git_head
24 from mygpo
.api
.httpresponse
import JsonResponse
25 from mygpo
.cel
import celery
26 from mygpo
.db
.couchdb
import get_main_database
27 from mygpo
.db
.couchdb
.episode
import episode_count
, filetype_stats
28 from mygpo
.db
.couchdb
.podcast
import podcast_count
, podcast_for_url
31 class InvalidPodcast(Exception):
32 """ raised when we try to merge a podcast that doesn't exist """
34 class AdminView(TemplateView
):
36 @method_decorator(require_staff
)
37 def dispatch(self
, *args
, **kwargs
):
38 return super(AdminView
, self
).dispatch(*args
, **kwargs
)
41 class Overview(AdminView
):
42 template_name
= 'admin/overview.html'
45 class HostInfo(AdminView
):
46 """ shows host information for diagnosis """
48 template_name
= 'admin/hostinfo.html'
50 def get(self
, request
):
51 commit
, msg
= get_git_head()
52 base_dir
= settings
.BASE_DIR
53 hostname
= socket
.gethostname()
54 django_version
= django
.VERSION
56 main_db
= get_main_database()
58 db_tasks
= main_db
.server
.active_tasks()
60 i
= celery
.control
.inspect()
61 num_celery_tasks
= len(i
.scheduled() or [])
63 return self
.render_to_response({
68 'django_version': django_version
,
69 'main_db': main_db
.uri
,
71 'num_celery_tasks': num_celery_tasks
,
76 class MergeSelect(AdminView
):
77 template_name
= 'admin/merge-select.html'
79 def get(self
, request
):
80 num
= int(request
.GET
.get('podcasts', 2))
83 return self
.render_to_response({
88 class MergeBase(AdminView
):
90 def _get_podcasts(self
, request
):
93 podcast_url
= request
.POST
.get('feed%d' % n
, None)
94 if podcast_url
is None:
100 podcast
= podcast_for_url(podcast_url
)
103 raise InvalidPodcast(podcast_url
)
105 podcasts
.append(podcast_for_url(podcast_url
))
110 class MergeVerify(MergeBase
):
112 template_name
= 'admin/merge-grouping.html'
114 def post(self
, request
):
117 podcasts
= self
._get
_podcasts
(request
)
119 except InvalidPodcast
as ip
:
120 messages
.error(request
,
121 _('No podcast with URL {url}').format(url
=str(ip
)))
123 grouper
= PodcastGrouper(podcasts
)
125 get_features
= lambda (e_id
, e
): ((e
.url
, e
.title
), e_id
)
127 num_groups
= grouper
.group(get_features
)
129 return self
.render_to_response({
130 'podcasts': podcasts
,
131 'groups': num_groups
,
135 class MergeProcess(MergeBase
):
137 RE_EPISODE
= re
.compile(r
'episode_([0-9a-fA-F]{32})')
139 def post(self
, request
):
142 podcasts
= self
._get
_podcasts
(request
)
144 except InvalidPodcast
as ip
:
145 messages
.error(request
,
146 _('No podcast with URL {url}').format(url
=str(ip
)))
148 grouper
= PodcastGrouper(podcasts
)
151 for key
, feature
in request
.POST
.items():
152 m
= self
.RE_EPISODE
.match(key
)
154 episode_id
= m
.group(1)
155 features
[episode_id
] = feature
157 get_features
= lambda (e_id
, e
): (features
[e_id
], e_id
)
159 num_groups
= grouper
.group(get_features
)
161 if 'renew' in request
.POST
:
162 return render(request
, 'admin/merge-grouping.html', {
163 'podcasts': podcasts
,
164 'groups': num_groups
,
168 elif 'merge' in request
.POST
:
170 podcast_ids
= [p
.get_id() for p
in podcasts
]
171 num_groups
= list(num_groups
)
173 res
= merge_podcasts
.delay(podcast_ids
, num_groups
)
175 return HttpResponseRedirect(reverse('admin-merge-status',
179 class MergeStatus(AdminView
):
180 """ Displays the status of the merge operation """
182 template_name
= 'admin/merge-status.html'
184 def get(self
, request
, task_id
):
185 result
= merge_podcasts
.AsyncResult(task_id
)
187 if not result
.ready():
188 return self
.render_to_response({
192 # clear cache to make merge result visible
193 # TODO: what to do with multiple frontends?
197 actions
, podcast
= result
.get()
199 except IncorrectMergeException
as ime
:
200 messages
.error(request
, str(ime
))
201 return HttpResponseRedirect(reverse('admin-merge'))
203 return render(request
, 'admin/merge-status.html', {
205 'actions': actions
.items(),
211 class UserAgentStatsView(AdminView
):
212 template_name
= 'admin/useragents.html'
214 def get(self
, request
):
216 uas
= UserAgentStats()
217 useragents
= uas
.get_entries()
219 return self
.render_to_response({
220 'useragents': useragents
.most_common(),
221 'max_users': uas
.max_users
,
222 'total': uas
.total_users
,
226 class ClientStatsView(AdminView
):
227 template_name
= 'admin/clients.html'
229 def get(self
, request
):
232 clients
= cs
.get_entries()
234 return self
.render_to_response({
235 'clients': clients
.most_common(),
236 'max_users': cs
.max_users
,
237 'total': cs
.total_users
,
241 class ClientStatsJsonView(AdminView
):
242 def get(self
, request
):
245 clients
= cs
.get_entries()
247 return JsonResponse(map(self
.to_dict
, clients
.most_common()))
249 def to_dict(self
, res
):
252 if not isinstance(obj
, tuple):
255 return obj
._asdict
(), count
258 class StatsView(AdminView
):
259 """ shows general stats as HTML page """
261 template_name
= 'admin/stats.html'
263 def _get_stats(self
):
265 'podcasts': podcast_count(),
266 'episodes': episode_count(),
267 'users': User
.count(),
270 def get(self
, request
):
271 stats
= self
._get
_stats
()
272 return self
.render_to_response({
277 class StatsJsonView(StatsView
):
278 """ provides general stats as JSON """
280 def get(self
, request
):
281 stats
= self
._get
_stats
()
282 return JsonResponse(stats
)
285 class FiletypeStatsView(AdminView
):
287 template_name
= 'admin/filetypes.html'
289 def get(self
, request
):
290 stats
= filetype_stats()
293 max_num
= stats
.most_common(1)[0][1]
297 return self
.render_to_response({
299 'stats': stats
.most_common(),