|
1
|
|
|
################################################################# |
|
2
|
|
|
# MET v2 Metadate Explorer Tool |
|
3
|
|
|
# |
|
4
|
|
|
# This Software is Open Source. See License: https://github.com/TERENA/met/blob/master/LICENSE.md |
|
5
|
|
|
# Copyright (c) 2012, TERENA All rights reserved. |
|
6
|
|
|
# |
|
7
|
|
|
# This Software is based on MET v1 developed for TERENA by Yaco Sistemas, http://www.yaco.es/ |
|
8
|
|
|
# MET v2 was developed for TERENA by Tamim Ziai, DAASI International GmbH, http://www.daasi.de |
|
9
|
|
|
# Current version of MET has been revised for performance improvements by Andrea Biancini, |
|
10
|
|
|
# Consortium GARR, http://www.garr.it |
|
11
|
|
|
######################################################################################### |
|
12
|
|
|
|
|
13
|
|
|
import re, time |
|
14
|
|
|
import pytz |
|
15
|
|
|
import operator |
|
16
|
|
|
import simplejson as json |
|
17
|
|
|
from urllib import unquote |
|
18
|
|
|
from datetime import datetime |
|
19
|
|
|
from dateutil import tz |
|
20
|
|
|
|
|
21
|
|
|
from django.conf import settings |
|
22
|
|
|
from django.db.models import Count, Q |
|
23
|
|
|
from django.contrib import messages |
|
24
|
|
|
from django.contrib.auth import logout |
|
25
|
|
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger |
|
26
|
|
|
from django.core.urlresolvers import reverse |
|
27
|
|
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest |
|
28
|
|
|
from django.shortcuts import render_to_response, get_object_or_404 |
|
29
|
|
|
from django.template import RequestContext |
|
30
|
|
|
from django.utils.translation import ugettext_lazy as _ |
|
31
|
|
|
from django.utils import timezone |
|
32
|
|
|
|
|
33
|
|
|
from chartit import DataPool, Chart |
|
34
|
|
|
|
|
35
|
|
|
from met.metadataparser.decorators import user_can_edit |
|
36
|
|
|
from met.metadataparser.models import Federation, Entity, EntityStat, EntityCategory, TOP_LENGTH, FEDERATION_TYPES |
|
37
|
|
|
from met.metadataparser.forms import (FederationForm, EntityForm, EntityCommentForm, |
|
38
|
|
|
EntityProposalForm, ServiceSearchForm, ChartForm, SearchEntitiesForm) |
|
39
|
|
|
|
|
40
|
|
|
from met.metadataparser.summary_export import export_summary |
|
41
|
|
|
from met.metadataparser.query_export import export_query_set |
|
42
|
|
|
from met.metadataparser.entity_export import export_entity |
|
43
|
|
|
from met.metadataparser.xmlparser import DESCRIPTOR_TYPES |
|
44
|
|
|
from met.metadataparser.utils import send_mail |
|
45
|
|
|
|
|
46
|
|
|
if settings.PROFILE: |
|
47
|
|
|
from silk.profiling.profiler import silk_profile as profile |
|
48
|
|
|
else: |
|
49
|
|
|
from met.metadataparser.templatetags.decorators import noop_decorator as profile |
|
50
|
|
|
|
|
51
|
|
|
RESCUE_SLASH = re.compile(r"^(http(?:|s):/)([^/])") |
|
52
|
|
|
|
|
53
|
|
|
def increment_current_toplength(request): |
|
54
|
|
|
current_top_length = request.session.get('currentTopLength', TOP_LENGTH) |
|
55
|
|
|
current_top_length += TOP_LENGTH |
|
56
|
|
|
|
|
57
|
|
|
if current_top_length > Entity.objects.all().count(): |
|
58
|
|
|
current_top_length -= TOP_LENGTH |
|
59
|
|
|
|
|
60
|
|
|
request.session['currentTopLength'] = current_top_length |
|
61
|
|
|
return HttpResponseRedirect(reverse('index')) |
|
62
|
|
|
|
|
63
|
|
|
def decrement_current_toplength(request): |
|
64
|
|
|
current_top_length = request.session.get('currentTopLength', TOP_LENGTH) |
|
65
|
|
|
current_top_length -= TOP_LENGTH |
|
66
|
|
|
|
|
67
|
|
|
if current_top_length <= 0: |
|
68
|
|
|
current_top_length = TOP_LENGTH |
|
69
|
|
|
|
|
70
|
|
|
request.session['currentTopLength'] = current_top_length |
|
71
|
|
|
return HttpResponseRedirect(reverse('index')) |
|
72
|
|
|
|
|
73
|
|
|
def _index_export(export, export_format, objects): |
|
74
|
|
|
counters = ( |
|
75
|
|
|
('all', {}), |
|
76
|
|
|
('IDPSSO', {'types__xmlname': 'IDPSSODescriptor'}), |
|
77
|
|
|
('SPSSO', {'types__xmlname': 'SPSSODescriptor'}), |
|
78
|
|
|
) |
|
79
|
|
|
|
|
80
|
|
|
if not export in ['interfederations', 'federations', 'most_federated_entities']: |
|
81
|
|
|
return HttpResponseBadRequest('Not valid export query') |
|
82
|
|
|
|
|
83
|
|
|
if export == 'most_federated_entities': |
|
84
|
|
|
return export_query_set(export_format, objects['most_federated_entities'], |
|
85
|
|
|
'most_federated_entities', ('entityid', 'types', 'name', 'federations')) |
|
86
|
|
|
else: |
|
87
|
|
|
return export_summary(export_format, objects[export], |
|
88
|
|
|
'entity_set', '%s_summary' % export, |
|
89
|
|
|
counters) |
|
90
|
|
|
|
|
91
|
|
|
@profile(name='Index page') |
|
92
|
|
|
def index(request): |
|
93
|
|
|
ff = Federation.objects.all().order_by('name') |
|
94
|
|
|
federations = [] |
|
95
|
|
|
interfederations = [] |
|
96
|
|
|
for f in ff: |
|
97
|
|
|
if f.is_interfederation: |
|
98
|
|
|
interfederations.append(f) |
|
99
|
|
|
else: |
|
100
|
|
|
federations.append(f) |
|
101
|
|
|
|
|
102
|
|
|
cc = Entity.objects.values('federations__id', 'types__xmlname').annotate(Count('federations__id'), Count('types__xmlname')) |
|
103
|
|
|
counts = {} |
|
104
|
|
|
for curtype in DESCRIPTOR_TYPES: |
|
105
|
|
|
counts[curtype] = [] |
|
106
|
|
|
for c in cc: |
|
107
|
|
|
if c['types__xmlname'] == curtype: |
|
108
|
|
|
counts[curtype].append(c) |
|
109
|
|
|
counts['All'] = Entity.objects.values('federations__id').annotate(Count('federations__id')) |
|
110
|
|
|
|
|
111
|
|
|
totals = Entity.objects.values('types__xmlname').annotate(Count('types__xmlname')) |
|
112
|
|
|
|
|
113
|
|
|
# Entities with count how many federations belongs to, and sorted by most first |
|
114
|
|
|
current_top_length = request.session.get('currentTopLength', TOP_LENGTH) |
|
115
|
|
|
most_federated_entities = Entity.get_most_federated_entities(maxlength=current_top_length, cache_expire=24*60*60) |
|
116
|
|
|
|
|
117
|
|
|
params = { |
|
118
|
|
|
'settings': settings, |
|
119
|
|
|
'interfederations': interfederations, |
|
120
|
|
|
'federations': federations, |
|
121
|
|
|
'entity_types': DESCRIPTOR_TYPES, |
|
122
|
|
|
'federation_path': request.path, |
|
123
|
|
|
'counts': counts, |
|
124
|
|
|
'totals': totals, |
|
125
|
|
|
'most_federated_entities': most_federated_entities, |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
export = request.GET.get('export', None) |
|
129
|
|
|
export_format = request.GET.get('format', None) |
|
130
|
|
|
if export and export_format: |
|
131
|
|
|
return _index_export(export, export_format, params) |
|
132
|
|
|
|
|
133
|
|
|
return render_to_response('metadataparser/index.html', params, context_instance=RequestContext(request)) |
|
134
|
|
|
|
|
135
|
|
|
def _paginate_fed(ob_entities, page): |
|
136
|
|
|
paginator = Paginator(ob_entities, 20) |
|
137
|
|
|
|
|
138
|
|
|
try: |
|
139
|
|
|
ob_entities = paginator.page(page) |
|
140
|
|
|
except PageNotAnInteger: |
|
141
|
|
|
ob_entities = paginator.page(1) |
|
142
|
|
|
except EmptyPage: |
|
143
|
|
|
ob_entities = paginator.page(paginator.num_pages) |
|
144
|
|
|
|
|
145
|
|
|
adjacent_pages = 5 |
|
146
|
|
|
page_range = [n for n in \ |
|
147
|
|
|
range(ob_entities.number - adjacent_pages, ob_entities.number + adjacent_pages + 1) \ |
|
148
|
|
|
if n > 0 and n <= ob_entities.paginator.num_pages] |
|
149
|
|
|
|
|
150
|
|
|
return { |
|
151
|
|
|
'page_range': page_range, |
|
152
|
|
|
'cur_page_number': ob_entities.number, |
|
153
|
|
|
'num_pages': ob_entities.paginator.num_pages, |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
@profile(name='Federation view') |
|
157
|
|
|
def federation_view(request, federation_slug=None): |
|
158
|
|
|
if federation_slug: |
|
159
|
|
|
request.session['%s_process_done' % federation_slug] = False |
|
160
|
|
|
request.session['%s_num_entities' % federation_slug] = 0 |
|
161
|
|
|
request.session['%s_cur_entities' % federation_slug] = 0 |
|
162
|
|
|
request.session.save() |
|
163
|
|
|
|
|
164
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
165
|
|
|
categories = EntityCategory.objects.all().filter( |
|
166
|
|
|
Q(category_id__icontains=federation.registration_authority) | |
|
167
|
|
|
Q(category_id__icontains='http://refeds.org') | |
|
168
|
|
|
Q(category_id__icontains='http://www.geant.net')) |
|
169
|
|
|
|
|
170
|
|
|
ob_entities = Entity.objects.filter(federations__id=federation.id) |
|
171
|
|
|
entity_type = None |
|
172
|
|
|
if request.GET and 'entity_type' in request.GET: |
|
173
|
|
|
entity_type = request.GET['entity_type'] |
|
174
|
|
|
ob_entities = ob_entities.filter(types__xmlname=entity_type) |
|
175
|
|
|
|
|
176
|
|
|
entity_category = None |
|
177
|
|
|
if request.GET and 'entity_category' in request.GET: |
|
178
|
|
|
entity_category = request.GET['entity_category'] |
|
179
|
|
|
ob_entities = ob_entities.filter(entity_categories__category_id=entity_category) |
|
180
|
|
|
|
|
181
|
|
|
ob_entities = ob_entities.prefetch_related('types', 'federations') |
|
182
|
|
|
pagination = _paginate_fed(ob_entities, request.GET.get('page')) |
|
183
|
|
|
|
|
184
|
|
|
entities = [] |
|
185
|
|
|
for entity in ob_entities: |
|
186
|
|
|
entities.append({ |
|
187
|
|
|
'entityid': entity.entityid, |
|
188
|
|
|
'name': entity.name, |
|
189
|
|
|
'absolute_url': entity.get_absolute_url(), |
|
190
|
|
|
'types': [unicode(item) for item in entity.types.all()], |
|
191
|
|
|
'federations': [(unicode(item.name), item.get_absolute_url()) for item in entity.federations.all()], |
|
192
|
|
|
}) |
|
193
|
|
|
|
|
194
|
|
|
if 'format' in request.GET: |
|
195
|
|
|
return export_query_set(request.GET.get('format'), entities, |
|
196
|
|
|
'entities_search_result', ('entityid', 'types', 'federations')) |
|
197
|
|
|
|
|
198
|
|
|
context = RequestContext(request) |
|
199
|
|
|
user = context.get('user', None) |
|
200
|
|
|
add_entity = user and user.has_perm('metadataparser.add_federation') |
|
201
|
|
|
pie_chart = fed_pie_chart(request, federation.id) |
|
202
|
|
|
|
|
203
|
|
|
return render_to_response('metadataparser/federation_view.html', |
|
204
|
|
|
{'settings': settings, |
|
205
|
|
|
'federation': federation, |
|
206
|
|
|
'entity_types': DESCRIPTOR_TYPES, |
|
207
|
|
|
'entity_type': entity_type or 'All', |
|
208
|
|
|
'fed_types': dict(FEDERATION_TYPES), |
|
209
|
|
|
'entities': entities, |
|
210
|
|
|
'categories': categories, |
|
211
|
|
|
'show_filters': True, |
|
212
|
|
|
'add_entity': add_entity, |
|
213
|
|
|
'lang': request.GET.get('lang', 'en'), |
|
214
|
|
|
'update_entities': request.GET.get('update', 'false'), |
|
215
|
|
|
'statcharts': [pie_chart], |
|
216
|
|
|
'pagination': pagination, |
|
217
|
|
|
}, context_instance=context) |
|
218
|
|
|
|
|
219
|
|
|
|
|
220
|
|
|
@user_can_edit(Federation) |
|
221
|
|
|
def federation_edit_post(request, federation, form): |
|
222
|
|
|
modify = True if federation else False |
|
223
|
|
|
form.save() |
|
224
|
|
|
|
|
225
|
|
|
if not modify: |
|
226
|
|
|
form.instance.editor_users.add(request.user) |
|
227
|
|
|
if 'file' in form.changed_data or 'file_url' in form.changed_data: |
|
228
|
|
|
form.instance.process_metadata() |
|
229
|
|
|
|
|
230
|
|
|
messages.success(request, _('Federation %s successfully' % 'modified' if modify else 'created')) |
|
231
|
|
|
return HttpResponseRedirect(form.instance.get_absolute_url() + '?update=true') |
|
232
|
|
|
|
|
233
|
|
|
|
|
234
|
|
|
@user_can_edit(Federation) |
|
235
|
|
|
def federation_edit(request, federation_slug=None): |
|
236
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) if federation_slug else None |
|
237
|
|
|
|
|
238
|
|
|
if request.method == 'POST': |
|
239
|
|
|
form = FederationForm(request.POST, request.FILES, instance=federation) |
|
240
|
|
|
if not form.is_valid(): |
|
241
|
|
|
messages.error(request, _('Please correct the errors indicated below')) |
|
242
|
|
|
else: |
|
243
|
|
|
return federation_edit_post(request, federation, form) |
|
244
|
|
|
else: |
|
245
|
|
|
form = FederationForm(instance=federation) |
|
246
|
|
|
|
|
247
|
|
|
context = RequestContext(request) |
|
248
|
|
|
user = context.get('user', None) |
|
249
|
|
|
delete_federation = user and user.has_perm('metadataparser.delete_federation') |
|
250
|
|
|
return render_to_response('metadataparser/federation_edit.html', |
|
251
|
|
|
{'settings': settings, 'form': form, 'delete_federation': delete_federation}, |
|
252
|
|
|
context_instance=RequestContext(request)) |
|
253
|
|
|
|
|
254
|
|
|
|
|
255
|
|
|
@user_can_edit(Federation) |
|
256
|
|
|
def federation_update_entities(request, federation_slug=None): |
|
257
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
258
|
|
|
federation.process_metadata_entities(request=request, federation_slug=federation_slug) |
|
259
|
|
|
|
|
260
|
|
|
messages.success(request, _('Federation entities updated succesfully')) |
|
261
|
|
|
return HttpResponse("Done. All entities updated.", content_type='text/plain') |
|
262
|
|
|
|
|
263
|
|
|
|
|
264
|
|
|
def entityupdate_progress(request, federation_slug=None): |
|
265
|
|
|
data = { 'done': False } |
|
266
|
|
|
if federation_slug: |
|
267
|
|
|
data = { 'done': request.session.get('%s_process_done' % federation_slug, False), |
|
268
|
|
|
'tot': request.session.get('%s_num_entities' % federation_slug, 0), |
|
269
|
|
|
'num': request.session.get('%s_cur_entities' % federation_slug, 0) } |
|
270
|
|
|
|
|
271
|
|
|
return HttpResponse(json.dumps(data), content_type='application/javascript') |
|
272
|
|
|
|
|
273
|
|
|
|
|
274
|
|
|
@user_can_edit(Federation, True) |
|
275
|
|
|
def federation_delete(request, federation_slug): |
|
276
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
277
|
|
|
|
|
278
|
|
|
for entity in federation.entity_set.all(): |
|
279
|
|
|
if len(entity.federations.all()) == 1: |
|
280
|
|
|
entity.delete() |
|
281
|
|
|
|
|
282
|
|
|
messages.success(request, |
|
283
|
|
|
_(u"%(federation)s federation was deleted successfully" |
|
284
|
|
|
% {'federation': unicode(federation)})) |
|
285
|
|
|
federation.delete() |
|
286
|
|
|
return HttpResponseRedirect(reverse('index')) |
|
287
|
|
|
|
|
288
|
|
|
|
|
289
|
|
|
@profile(name='Index charts') |
|
290
|
|
|
def federation_charts(request, federation_slug=None): |
|
291
|
|
|
if federation_slug is None: |
|
292
|
|
|
federation = None |
|
293
|
|
|
else: |
|
294
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
295
|
|
|
|
|
296
|
|
|
if request.method == 'POST': |
|
297
|
|
|
form = ChartForm(request.POST, request.FILES, instance=federation) |
|
298
|
|
|
|
|
299
|
|
|
if form.is_valid(): |
|
300
|
|
|
stats_config_dict = getattr(settings, "STATS") |
|
301
|
|
|
service_terms = stats_config_dict['statistics']['entity_by_type']['terms'] |
|
302
|
|
|
protocol_terms = stats_config_dict['statistics']['entity_by_protocol']['terms'] |
|
303
|
|
|
|
|
304
|
|
|
protocols = stats_config_dict['protocols'] |
|
305
|
|
|
|
|
306
|
|
|
from_time = datetime.fromordinal(form.cleaned_data['fromDate'].toordinal()) |
|
307
|
|
|
if timezone.is_naive(from_time): |
|
308
|
|
|
from_time = pytz.utc.localize(from_time) |
|
309
|
|
|
to_time = datetime.fromordinal(form.cleaned_data['toDate'].toordinal() + 1) |
|
310
|
|
|
if timezone.is_naive(to_time): |
|
311
|
|
|
to_time = pytz.utc.localize(to_time) |
|
312
|
|
|
|
|
313
|
|
|
service_stats = EntityStat.objects.filter( federation=federation \ |
|
314
|
|
|
, feature__in = service_terms \ |
|
315
|
|
|
, time__gte = from_time \ |
|
316
|
|
|
, time__lte = to_time).order_by("time") |
|
317
|
|
|
|
|
318
|
|
|
protocol_stats = EntityStat.objects.filter( federation=federation \ |
|
319
|
|
|
, feature__in = protocol_terms \ |
|
320
|
|
|
, time__gte = from_time \ |
|
321
|
|
|
, time__lte = to_time).order_by("time") |
|
322
|
|
|
|
|
323
|
|
|
s_chart = stats_chart(stats_config_dict, request, service_stats, 'entity_by_type') |
|
324
|
|
|
|
|
325
|
|
|
p_chart = stats_chart(stats_config_dict, request, protocol_stats, 'entity_by_protocol', protocols) |
|
326
|
|
|
|
|
327
|
|
|
return render_to_response('metadataparser/federation_chart.html', |
|
328
|
|
|
{'form': form, |
|
329
|
|
|
'statcharts': [s_chart, p_chart], |
|
330
|
|
|
}, |
|
331
|
|
|
context_instance=RequestContext(request)) |
|
332
|
|
|
|
|
333
|
|
|
else: |
|
334
|
|
|
messages.error(request, _('Please correct the errors indicated' |
|
335
|
|
|
' below')) |
|
336
|
|
|
else: |
|
337
|
|
|
form = ChartForm(instance=federation) |
|
338
|
|
|
|
|
339
|
|
|
return render_to_response('metadataparser/federation_chart.html', |
|
340
|
|
|
{'settings': settings, 'form': form}, |
|
341
|
|
|
context_instance=RequestContext(request)) |
|
342
|
|
|
|
|
343
|
|
|
|
|
344
|
|
|
def stats_chart(stats_config_dict, request, stats, entity, protocols=None): |
|
345
|
|
|
terms = stats_config_dict['statistics'][entity]['terms'] |
|
346
|
|
|
title = stats_config_dict['statistics'][entity]['title'] |
|
347
|
|
|
x_title = stats_config_dict['statistics'][entity]['x_title'] |
|
348
|
|
|
y_title = stats_config_dict['statistics'][entity]['y_title'] |
|
349
|
|
|
chart_type = 'column' |
|
350
|
|
|
stacking = True |
|
351
|
|
|
term_names = stats_config_dict['feature_names'] |
|
352
|
|
|
time_format = stats_config_dict['time_format'] |
|
353
|
|
|
statdata = _create_statdata('bar', stats, terms, term_names) |
|
354
|
|
|
|
|
355
|
|
|
series_options = [] |
|
356
|
|
|
for stack in range(len(protocols) if protocols else 1): |
|
357
|
|
|
for term in terms: |
|
358
|
|
|
if not protocols or term.endswith(protocols[stack]): |
|
359
|
|
|
series_options += \ |
|
360
|
|
|
[{'options':{ |
|
361
|
|
|
'type': chart_type, |
|
362
|
|
|
'stacking': stacking, |
|
363
|
|
|
'stack': stack, |
|
364
|
|
|
}, |
|
365
|
|
|
'terms':{ |
|
366
|
|
|
'time_%s' %term: [{term_names[term]: {'stack': stack, }}], |
|
367
|
|
|
}}] |
|
368
|
|
|
|
|
369
|
|
|
chart_options = _get_chart_options('bar', title, x_title, y_title) |
|
370
|
|
|
|
|
371
|
|
|
return Chart( |
|
372
|
|
|
datasource = statdata, |
|
373
|
|
|
series_options = series_options, |
|
374
|
|
|
chart_options = chart_options, |
|
375
|
|
|
x_sortf_mapf_mts=(None, lambda i: datetime.fromtimestamp(time.mktime(i.replace(tzinfo=tz.gettz('UTC')).astimezone(tz.tzlocal()).timetuple())).strftime(time_format), False) |
|
376
|
|
|
) |
|
377
|
|
|
|
|
378
|
|
|
|
|
379
|
|
|
|
|
380
|
|
|
def _create_statdata(chart_type, stats, terms=None, term_names=None): |
|
381
|
|
|
if chart_type == 'bar': |
|
382
|
|
|
statdata = DataPool( |
|
383
|
|
|
series=[{'options': { |
|
384
|
|
|
'source': stats.filter(feature=term)}, |
|
385
|
|
|
'legend_by': 'feature', |
|
386
|
|
|
'terms': [{'time_%s' %term :'time'}, |
|
387
|
|
|
{term_names[term] : 'value', 'name': 'feature'}] |
|
388
|
|
|
} for term in terms] |
|
389
|
|
|
) |
|
390
|
|
|
elif chart_type == 'pie': |
|
391
|
|
|
statdata = DataPool( |
|
392
|
|
|
series=[{'options': { 'source': stats }, |
|
393
|
|
|
'legend_by': 'feature', |
|
394
|
|
|
'terms': ['feature', 'value'], |
|
395
|
|
|
}] |
|
396
|
|
|
) |
|
397
|
|
|
else: |
|
398
|
|
|
statdata = None |
|
399
|
|
|
|
|
400
|
|
|
return statdata |
|
401
|
|
|
|
|
402
|
|
|
def _get_chart_options(chart_type, title=None, x_title=None, y_title=None): |
|
403
|
|
|
if chart_type == 'bar': |
|
404
|
|
|
chart_options = {'title': { 'text': title }, |
|
405
|
|
|
'xAxis': { |
|
406
|
|
|
'title': { 'text': x_title }, |
|
407
|
|
|
'labels': { |
|
408
|
|
|
'rotation': -45, |
|
409
|
|
|
'align': 'right' |
|
410
|
|
|
}, |
|
411
|
|
|
'max': 10, |
|
412
|
|
|
}, |
|
413
|
|
|
'yAxis': { |
|
414
|
|
|
'title': { 'text': y_title }, |
|
415
|
|
|
'minorTickInterval': 'auto' |
|
416
|
|
|
}, |
|
417
|
|
|
'credits': { 'enabled': False }, |
|
418
|
|
|
'scrollbar': { 'enabled': True }, |
|
419
|
|
|
'zoomType': 'xy', |
|
420
|
|
|
} |
|
421
|
|
|
elif chart_type == 'pie': |
|
422
|
|
|
chart_options = { |
|
423
|
|
|
'title': { 'text': ' ' }, |
|
424
|
|
|
'credits': { 'enabled': False } |
|
425
|
|
|
} |
|
426
|
|
|
else: |
|
427
|
|
|
chart_options = None |
|
428
|
|
|
|
|
429
|
|
|
return chart_options |
|
430
|
|
|
|
|
431
|
|
|
def fed_pie_chart(request, federation_id): |
|
432
|
|
|
stats_config_dict = getattr(settings, "STATS") |
|
433
|
|
|
terms = stats_config_dict['statistics']['entity_by_type']['terms'] |
|
434
|
|
|
stats = EntityStat.objects.filter(federation=federation_id, \ |
|
435
|
|
|
feature__in=terms).order_by('-time')[0:len(terms)] |
|
436
|
|
|
statdata = _create_statdata('pie', stats) |
|
437
|
|
|
series_options = \ |
|
438
|
|
|
[{'options': { 'type': 'pie', 'stacking': False, 'size': '70%' }, |
|
439
|
|
|
'terms':{ 'feature': [ 'value' ] }}] |
|
440
|
|
|
chart_options = _get_chart_options('pie') |
|
441
|
|
|
|
|
442
|
|
|
return Chart( |
|
443
|
|
|
datasource = statdata, |
|
444
|
|
|
series_options = series_options, |
|
445
|
|
|
chart_options = chart_options, |
|
446
|
|
|
) |
|
447
|
|
|
|
|
448
|
|
|
|
|
449
|
|
|
|
|
450
|
|
|
@profile(name='Entity view') |
|
451
|
|
|
def entity_view(request, entityid): |
|
452
|
|
|
entityid = unquote(entityid) |
|
453
|
|
|
entityid = RESCUE_SLASH.sub("\\1/\\2", entityid) |
|
454
|
|
|
|
|
455
|
|
|
entity = get_object_or_404(Entity, entityid=entityid) |
|
456
|
|
|
|
|
457
|
|
|
if 'federation' in request.GET: |
|
458
|
|
|
federation = get_object_or_404(Federation, slug=request.GET.get('federation')) |
|
459
|
|
|
entity.curfed = federation |
|
460
|
|
|
|
|
461
|
|
|
if 'format' in request.GET: |
|
462
|
|
|
return export_entity(request.GET.get('format'), entity) |
|
463
|
|
|
|
|
464
|
|
|
if 'viewxml' in request.GET: |
|
465
|
|
|
serialized = entity.xml |
|
466
|
|
|
response = HttpResponse(serialized, content_type='application/xml') |
|
467
|
|
|
return response |
|
468
|
|
|
|
|
469
|
|
|
return render_to_response('metadataparser/entity_view.html', |
|
470
|
|
|
{'settings': settings, |
|
471
|
|
|
'entity': entity, |
|
472
|
|
|
'lang': request.GET.get('lang', 'en') |
|
473
|
|
|
}, context_instance=RequestContext(request)) |
|
474
|
|
|
|
|
475
|
|
|
|
|
476
|
|
|
@user_can_edit(Entity) |
|
477
|
|
|
def entity_edit_post(request, form, federation, entity): |
|
478
|
|
|
form.save() |
|
479
|
|
|
if federation and not federation in form.instance.federations.all(): |
|
480
|
|
|
form.instance.federations.add(federation) |
|
481
|
|
|
form.instance.save() |
|
482
|
|
|
|
|
483
|
|
|
if entity: |
|
484
|
|
|
messages.success(request, _('Entity modified successfully')) |
|
485
|
|
|
else: |
|
486
|
|
|
messages.success(request, _('Entity created successfully')) |
|
487
|
|
|
|
|
488
|
|
|
return HttpResponseRedirect(form.instance.get_absolute_url()) |
|
489
|
|
|
|
|
490
|
|
|
|
|
491
|
|
|
|
|
492
|
|
|
@user_can_edit(Entity) |
|
493
|
|
|
def entity_edit(request, federation_slug=None, entity_id=None): |
|
494
|
|
|
entity = None |
|
495
|
|
|
federation = None |
|
496
|
|
|
if federation_slug: |
|
497
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
498
|
|
|
if entity_id: |
|
499
|
|
|
entity = get_object_or_404(Entity, id=entity_id, |
|
500
|
|
|
federations__id=federation.id) |
|
501
|
|
|
if entity_id and not federation_slug: |
|
502
|
|
|
entity = get_object_or_404(Entity, id=entity_id) |
|
503
|
|
|
|
|
504
|
|
|
if request.method == 'POST': |
|
505
|
|
|
form = EntityForm(request.POST, request.FILES, instance=entity) |
|
506
|
|
|
if form.is_valid(): |
|
507
|
|
|
return entity_edit_post(request, form, federation, entity) |
|
508
|
|
|
else: |
|
509
|
|
|
messages.error(request, _('Please correct the errors indicated below')) |
|
510
|
|
|
else: |
|
511
|
|
|
form = EntityForm(instance=entity) |
|
512
|
|
|
|
|
513
|
|
|
return render_to_response('metadataparser/entity_edit.html', |
|
514
|
|
|
{'settings': settings, |
|
515
|
|
|
'form': form, |
|
516
|
|
|
'federation': federation}, |
|
517
|
|
|
context_instance=RequestContext(request)) |
|
518
|
|
|
|
|
519
|
|
|
|
|
520
|
|
|
@user_can_edit(Entity, True) |
|
521
|
|
|
def entity_delete(request, entity_id): |
|
522
|
|
|
entity = get_object_or_404(Entity, id=entity_id) |
|
523
|
|
|
messages.success(request, |
|
524
|
|
|
_(u"%(entity)s entity was deleted successfully" |
|
525
|
|
|
% {'entity': unicode(entity)})) |
|
526
|
|
|
entity.delete() |
|
527
|
|
|
return HttpResponseRedirect(reverse('index')) |
|
528
|
|
|
|
|
529
|
|
|
|
|
530
|
|
|
def entity_comment(request, federation_slug=None, entity_id=None): |
|
531
|
|
|
entity = None |
|
532
|
|
|
federation = None |
|
533
|
|
|
if federation_slug: |
|
534
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
535
|
|
|
if entity_id: |
|
536
|
|
|
entity = get_object_or_404(Entity, id=entity_id, |
|
537
|
|
|
federations__id=federation.id) |
|
538
|
|
|
if entity_id and not federation_slug: |
|
539
|
|
|
entity = get_object_or_404(Entity, id=entity_id) |
|
540
|
|
|
|
|
541
|
|
|
if request.method == 'POST': |
|
542
|
|
|
form = EntityCommentForm(request.POST, request.FILES, instance=entity) |
|
543
|
|
|
if form.is_valid(): |
|
544
|
|
|
mail_config = getattr(settings, "MAIL_CONFIG") |
|
545
|
|
|
try: |
|
546
|
|
|
subject = mail_config['comment_subject'] %entity |
|
547
|
|
|
send_mail(form.data['email'], subject, form.data['comment']) |
|
548
|
|
|
messages.success(request, _('Comment posted successfully')) |
|
549
|
|
|
except Exception, errorMessage: |
|
550
|
|
|
messages.error(request, _('Comment could not be posted successfully: %s' %errorMessage)) |
|
551
|
|
|
|
|
552
|
|
|
return HttpResponseRedirect(form.instance.get_absolute_url()) |
|
553
|
|
|
|
|
554
|
|
|
else: |
|
555
|
|
|
messages.error(request, _('Please correct the errors indicated' |
|
556
|
|
|
' below')) |
|
557
|
|
|
else: |
|
558
|
|
|
form = EntityCommentForm(instance=entity) |
|
559
|
|
|
|
|
560
|
|
|
return render_to_response('metadataparser/entity_comment.html', |
|
561
|
|
|
{'settings': settings, |
|
562
|
|
|
'form': form, |
|
563
|
|
|
}, |
|
564
|
|
|
context_instance=RequestContext(request)) |
|
565
|
|
|
|
|
566
|
|
|
|
|
567
|
|
|
def entity_proposal(request, federation_slug=None, entity_id=None): |
|
568
|
|
|
entity = None |
|
569
|
|
|
federation = None |
|
570
|
|
|
if federation_slug: |
|
571
|
|
|
federation = get_object_or_404(Federation, slug=federation_slug) |
|
572
|
|
|
if entity_id: |
|
573
|
|
|
entity = get_object_or_404(Entity, id=entity_id, |
|
574
|
|
|
federations__id=federation.id) |
|
575
|
|
|
if entity_id and not federation_slug: |
|
576
|
|
|
entity = get_object_or_404(Entity, id=entity_id) |
|
577
|
|
|
|
|
578
|
|
|
if request.method == 'POST': |
|
579
|
|
|
form = EntityProposalForm(request.POST, request.FILES, instance=entity) |
|
580
|
|
|
|
|
581
|
|
|
if form.is_valid(): |
|
582
|
|
|
mail_config = getattr(settings, "MAIL_CONFIG") |
|
583
|
|
|
try: |
|
584
|
|
|
subject = mail_config['proposal_subject'] %entity |
|
585
|
|
|
my_dict = dict(form.data.iterlists()) |
|
586
|
|
|
body = mail_config['proposal_body'] % (entity, ', '.join(my_dict['federations']), form.data['comment']) |
|
587
|
|
|
send_mail(form.data['email'], subject, body) |
|
588
|
|
|
messages.success(request, _('Proposal posted successfully')) |
|
589
|
|
|
except Exception, errorMessage: |
|
590
|
|
|
messages.error(request, _('Proposal could not be posted successfully: %s' %errorMessage)) |
|
591
|
|
|
|
|
592
|
|
|
return HttpResponseRedirect(form.instance.get_absolute_url()) |
|
593
|
|
|
|
|
594
|
|
|
else: |
|
595
|
|
|
messages.error(request, _('Please correct the errors indicated' |
|
596
|
|
|
' below')) |
|
597
|
|
|
else: |
|
598
|
|
|
form = EntityProposalForm(instance=entity) |
|
599
|
|
|
|
|
600
|
|
|
return render_to_response('metadataparser/entity_proposal.html', |
|
601
|
|
|
{'settings': settings, |
|
602
|
|
|
'form': form, |
|
603
|
|
|
}, |
|
604
|
|
|
context_instance=RequestContext(request)) |
|
605
|
|
|
|
|
606
|
|
|
def search_service(request): |
|
607
|
|
|
filters = {} |
|
608
|
|
|
objects = [] |
|
609
|
|
|
|
|
610
|
|
|
if 'entityid' in request.GET: |
|
611
|
|
|
form = ServiceSearchForm(request.GET) |
|
612
|
|
|
if form.is_valid(): |
|
613
|
|
|
entityid = form.cleaned_data['entityid'] |
|
614
|
|
|
entityid = entityid.strip() |
|
615
|
|
|
filters['entityid__icontains'] = entityid |
|
616
|
|
|
else: |
|
617
|
|
|
form = ServiceSearchForm() |
|
618
|
|
|
entity_type = request.GET.get('entity_type', None) |
|
619
|
|
|
|
|
620
|
|
|
if entity_type: |
|
621
|
|
|
filters['entity_type'] = entity_type |
|
622
|
|
|
|
|
623
|
|
|
if filters: |
|
624
|
|
|
objects = Entity.objects.filter(**filters) |
|
625
|
|
|
|
|
626
|
|
|
if objects and 'format' in request.GET.keys(): |
|
627
|
|
|
return export_query_set(request.GET.get('format'), objects, |
|
628
|
|
|
'entities_search_result', ('entityid', 'types', 'federations')) |
|
629
|
|
|
|
|
630
|
|
|
entities = [] |
|
631
|
|
|
for entity in objects: |
|
632
|
|
|
entities.append({ |
|
633
|
|
|
'entityid': entity.entityid, |
|
634
|
|
|
'name': entity.name, |
|
635
|
|
|
'absolute_url': entity.get_absolute_url(), |
|
636
|
|
|
'types': [unicode(item) for item in entity.types.all()], |
|
637
|
|
|
'federations': [(unicode(item.name), item.get_absolute_url()) for item in entity.federations.all()], |
|
638
|
|
|
}) |
|
639
|
|
|
|
|
640
|
|
|
return render_to_response('metadataparser/service_search.html', |
|
641
|
|
|
{'settings': settings, |
|
642
|
|
|
'searchform': form, |
|
643
|
|
|
'object_list': entities, |
|
644
|
|
|
'show_filters': False, |
|
645
|
|
|
}, context_instance=RequestContext(request)) |
|
646
|
|
|
|
|
647
|
|
|
def met_logout(request): |
|
648
|
|
|
logout(request) |
|
649
|
|
|
return HttpResponseRedirect(request.GET.get("next", "/")) |
|
650
|
|
|
|
|
651
|
|
|
|
|
652
|
|
|
@profile(name='Search entities') |
|
653
|
|
|
def search_entities(request): |
|
654
|
|
|
if request.method == 'POST': |
|
655
|
|
|
form = SearchEntitiesForm(request.POST) |
|
656
|
|
|
|
|
657
|
|
|
if form.is_valid(): |
|
658
|
|
|
filters = {} |
|
659
|
|
|
args = () |
|
660
|
|
|
|
|
661
|
|
|
entity_type = form.cleaned_data['entity_type'] |
|
662
|
|
|
if entity_type and entity_type != 'All': |
|
663
|
|
|
filters['types__name'] = entity_type |
|
664
|
|
|
|
|
665
|
|
|
entity_category = form.cleaned_data['entity_category'] |
|
666
|
|
|
if entity_category and entity_category != 'All': |
|
667
|
|
|
filters['entity_categories__category_id'] = entity_category |
|
668
|
|
|
|
|
669
|
|
|
federations = form.cleaned_data['federations'] |
|
670
|
|
|
if federations and not 'All' in federations: |
|
671
|
|
|
q_list = [Q(federations__id=f) for f in federations] |
|
672
|
|
|
args = (reduce(operator.or_, q_list),) |
|
673
|
|
|
|
|
674
|
|
|
entity_id = form.cleaned_data['entityid'] |
|
675
|
|
|
if entity_id and entity_id != '': |
|
676
|
|
|
filters['entityid__icontains'] = entity_id |
|
677
|
|
|
|
|
678
|
|
|
ob_entities = Entity.objects.all() |
|
679
|
|
|
if args: |
|
680
|
|
|
ob_entities = ob_entities.filter(*args) |
|
681
|
|
|
if filters: |
|
682
|
|
|
ob_entities = ob_entities.filter(**filters) |
|
683
|
|
|
|
|
684
|
|
|
ob_entities = ob_entities.prefetch_related('types', 'federations') |
|
685
|
|
|
pagination = _paginate_fed(ob_entities, form.cleaned_data['page']) |
|
686
|
|
|
|
|
687
|
|
|
entities = [] |
|
688
|
|
|
for entity in ob_entities: |
|
689
|
|
|
entities.append({ |
|
690
|
|
|
'entityid': entity.entityid, |
|
691
|
|
|
'name': entity.name, |
|
692
|
|
|
'absolute_url': entity.get_absolute_url(), |
|
693
|
|
|
'types': [unicode(item) for item in entity.types.all()], |
|
694
|
|
|
'federations': [(unicode(item.name), item.get_absolute_url()) for item in entity.federations.all()], |
|
695
|
|
|
}) |
|
696
|
|
|
|
|
697
|
|
|
export_format = form.cleaned_data['export_format'] |
|
698
|
|
|
if export_format: |
|
699
|
|
|
return export_query_set(export_format, entities, |
|
700
|
|
|
'entities_search_result', ('entityid', 'types', 'federations')) |
|
701
|
|
|
|
|
702
|
|
|
return render_to_response('metadataparser/search_entities.html', |
|
703
|
|
|
{'settings': settings, |
|
704
|
|
|
'form': form, |
|
705
|
|
|
'object_list': entities, |
|
706
|
|
|
'show_filters': False, |
|
707
|
|
|
'pagination': pagination, |
|
708
|
|
|
}, context_instance=RequestContext(request)) |
|
709
|
|
|
|
|
710
|
|
|
else: |
|
711
|
|
|
messages.error(request, _('Please correct the errors indicated' |
|
712
|
|
|
' below')) |
|
713
|
|
|
else: |
|
714
|
|
|
form = SearchEntitiesForm() |
|
715
|
|
|
|
|
716
|
|
|
return render_to_response('metadataparser/search_entities.html', |
|
717
|
|
|
{'form': form}, |
|
718
|
|
|
context_instance=RequestContext(request)) |
|
719
|
|
|
|
|
720
|
|
|
|
|
721
|
|
|
|