views.py 15.8 KB
Newer Older
stef's avatar
stef committed
1
from forms import AddViolation, SearchViolation
2
3
from django.views.generic import ListView, FormView, DetailView
from django.views.generic.list import MultipleObjectMixin
stef's avatar
stef committed
4
from django.http import HttpResponse, HttpResponseRedirect, Http404
Okhin's avatar
Okhin committed
5
from django.shortcuts import render_to_response, get_object_or_404, redirect, render
6
from django.template import RequestContext, loader, Context
stef's avatar
stef committed
7
from django.core.files import File
8
from django.core.servers.basehttp import FileWrapper
Okhin's avatar
Okhin committed
9
from django.core import serializers
10
from django.conf import settings
stef's avatar
stef committed
11
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
12
from django.core.exceptions import ObjectDoesNotExist
13
from django.core.mail import send_mail
14
from django.contrib import messages
15
from django.contrib.auth.models import User
stef's avatar
stef committed
16
from django.utils.translation import ugettext_lazy as _
stef's avatar
stef committed
17
from django.db.models import Count
18
from haystack.generic_views import SearchView
Okhin's avatar
Okhin committed
19
from haystack.query import SearchQuerySet
20
from models import Violation, Attachment, Comment, Confirmation, COUNTRIES, STATUS, Operator, FeaturedCase
stef's avatar
stef committed
21
22
from tempfile import mkstemp
from datetime import datetime
23
import hashlib, os, re, json
24
25
from random import randint
from email.mime.text import MIMEText
stef's avatar
stef committed
26
from email.header import Header
27
28
from urlparse import urljoin
from BeautifulSoup import BeautifulSoup, Comment as BComment
stef's avatar
stef committed
29
from operator import itemgetter
30
from itertools import groupby
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

def sanitizeHtml(value, base_url=None):
    rjs = r'[\s]*(&#x.{1,7})?'.join(list('javascript:'))
    rvb = r'[\s]*(&#x.{1,7})?'.join(list('vbscript:'))
    re_scripts = re.compile('(%s)|(%s)' % (rjs, rvb), re.IGNORECASE)
    validTags = 'p i strong b u a h1 h2 h3 pre br img'.split()
    validAttrs = 'href src width height'.split()
    urlAttrs = 'href src'.split() # Attributes which should have a URL
    soup = BeautifulSoup(value)
    for comment in soup.findAll(text=lambda text: isinstance(text, BComment)):
        # Get rid of comments
        comment.extract()
    for tag in soup.findAll(True):
        if tag.name not in validTags:
            tag.hidden = True
        attrs = tag.attrs
        tag.attrs = []
        for attr, val in attrs:
            if attr in validAttrs:
                val = re_scripts.sub('', val) # Remove scripts (vbs & js)
                if attr in urlAttrs:
                    val = urljoin(base_url, val) # Calculate the absolute url
                tag.attrs.append((attr, val))

    return soup.renderContents().decode('utf8')
stef's avatar
stef committed
56

57
def activate(request):
58
59
60
    try:
        v=Violation.objects.get(activationid=request.GET.get('key','asdf'))
    except:
61
62
        messages.add_message(request, messages.INFO, unicode(_('Thank you, this key has been already activated')))
        return HttpResponseRedirect('/') # Redirect after POST
63
64
65
    if v:
        actid = hashlib.sha1(''.join([chr(randint(32, 122)) for x in range(12)])).hexdigest()
        to=[x.email for x in User.objects.filter(groups__name='moderator')]
stef's avatar
stef committed
66
        details='\n'.join(["%s: %s" % (k.capitalize(), val) for k,val in v.__dict__.items() if not k.startswith('_') and val])
67
68
69
70
71
72
73
74
75

        msg = {'from': 'nnmon@respectmynet.eu',
               'to': to,
               'subject': 'NNMon submission approval'.encode("Utf-8"),
               'body': "A new report was submitted. To approve click here: %s/moderate/?key=%s\n\nDetails follow:\n%s\n%s" %
                       (settings.ROOT_URL or 'http://localhost:8001/', actid, details, v.comment_set.get().comment)
               }
        send_mail(msg['subject'], msg['body'], msg['from'], msg['to'], fail_silently=False)

76
77
        v.activationid=actid
        v.save()
Benjamin Sonntag's avatar
Benjamin Sonntag committed
78
        messages.add_message(request, messages.INFO, _('Thank you for verifying your submission. It will be listed shortly, after we\'ve checked that the report is valid.').encode("Utf-8"))
79
80
    return HttpResponseRedirect('/') # Redirect after POST

81
def moderate(request):
82
83
84
    try:
        v=Violation.objects.get(activationid=request.GET.get('key','asdf'))
    except:
85
86
        messages.add_message(request, messages.INFO, unicode(_('Thank you, this key has been already activated')))
        return HttpResponseRedirect('/') # Redirect after POST
87
88
89
90
    if not v:
        messages.add_message(request, messages.INFO, _('No such key'))
        return HttpResponseRedirect('/') # Redirect after POST
    if request.GET.get('action','')=='approve':
stef's avatar
stef committed
91
        messages.add_message(request, messages.INFO, _('Thank you for approving the <a href="%s">submission</a>.' % v.get_absolute_url()))
stef's avatar
stef committed
92

93
94
95
        msg = {'from': 'nnmon@respectmynet.eu',
               'to': [v.comment_set.get().submitter_email],
               'subject': _('NNMon submission approved').encode("Utf-8"),
stef's avatar
stef committed
96
               'body': _("Your report has been approved.\nTo see it, please visit: %s/%s") %
stef's avatar
stef committed
97
                      (settings.ROOT_URL or 'http://localhost:8001/', v.get_absolute_url())
98
99
100
               }
        send_mail(msg['subject'], msg['body'], msg['from'], msg['to'], fail_silently=False)

101
        if settings.TWITTER_API:
stef's avatar
stef committed
102
            try:
stef's avatar
stef committed
103
                settings.TWITTER_API.PostUpdate("New #NetNeutrality violation reported for %s (%s) %s %s/%s" % (v.operator, v.country, v.contract, settings.ROOT_URL or 'http://localhost:8001/', v.id))
stef's avatar
stef committed
104
105
            except:
                pass
106
107
        v.activationid=''
        v.save()
108
        return redirect(v) # Redirect after POST to violation url
109
110
111
112
113
114
    if request.GET.get('action','')=='delete':
        v.delete()
        messages.add_message(request, messages.INFO, _('Thank you for deleting the submission.'))
        return HttpResponseRedirect('/') # Redirect after POST
    return render_to_response('view.html', { 'v': v, 'key': request.GET.get('key') },context_instance=RequestContext(request))

stef's avatar
stef committed
115
116
117
def confirm(request, id, name=None):
    if name:
        if Confirmation.objects.filter(email=name, violation=id).count()==0:
stef's avatar
stef committed
118
119
            msg=_("Thank you for confirming a case. To finalize your confirmation please validate using your confirmation key.\nYour confirmation key is %s/%s%s")
            actid=sendverifymail('confirm/',name, msg)
120
121
122
            try:
                c=Confirmation(key=actid, email=name, violation=Violation.objects.get(pk=id))
            except:
stef's avatar
stef committed
123
                return HttpResponse(unicode(_('Thank you, this has been already confirmed')))
stef's avatar
stef committed
124
            c.save()
stef's avatar
stef committed
125
        return HttpResponse(unicode(_('Thank you for your confirmation')))
stef's avatar
stef committed
126
127
128
    try:
        c = get_object_or_404(Confirmation, key=id)
    except:
129
130
        messages.add_message(request, messages.INFO, unicode(_("Thank you, this has been already confirmed")))
        return HttpResponseRedirect('/') # Redirect after POST
stef's avatar
stef committed
131
132
    c.key=''
    c.save()
stef's avatar
stef committed
133
    messages.add_message(request, messages.INFO, unicode(_('Thank you for verifying your confirmation')))
134
    return HttpResponseRedirect('/') # Redirect after POST
stef's avatar
stef committed
135

stef's avatar
stef committed
136
def sendverifymail(service,to,body):
stef's avatar
stef committed
137
    actid = hashlib.sha1(''.join([chr(randint(32, 122)) for x in range(12)])).hexdigest()
138
139
140
    msg = {'from': 'nnmon@respectmynet.eu',
           'to': [to.encode("Utf-8")],
           'subject': _('NNMon submission verification').encode("Utf-8"),
stef's avatar
stef committed
141
           'body': body % (settings.ROOT_URL or 'http://localhost:8001/', service, actid),
142
143
144
           }
    send_mail(msg['subject'], msg['body'], msg['from'], msg['to'], fail_silently=False)

stef's avatar
stef committed
145
146
    return actid

Okhin's avatar
Okhin committed
147
148
class AddForm(FormView):
    template_name = 'index.html'
149
150
151
152
    form_class = AddViolation
    success_url = '/'

    def form_valid(self, form):
153
154
155
        #msg=_("Thank you for submitting a new report. To finalize your submission please confirm using your validation key.\nYour verification key is %s/%s%s\nPlease note that reports are moderated, it might take some time before your report appears online. Thank you for your patience.")
        #actid=sendverifymail('activate?key=',form.cleaned_data['email'], msg)
        actid = hashlib.sha1(''.join([chr(randint(32, 122)) for x in range(12)])).hexdigest()
156
157
        operator, created = Operator.objects.get_or_create(name=form.cleaned_data['operator'])
        v=Violation(
stef's avatar
stef committed
158
                country = form.cleaned_data['country'],
159
                operator_ref = operator,
stef's avatar
stef committed
160
161
                contract = form.cleaned_data['contract'],
                resource = form.cleaned_data['resource'],
stef's avatar
stef committed
162
                resource_name = form.cleaned_data['resource_name'],
stef's avatar
stef committed
163
164
165
166
                type = form.cleaned_data['type'],
                media = form.cleaned_data['media'],
                temporary = form.cleaned_data['temporary'],
                contractual = form.cleaned_data['contractual'],
167
                contract_excerpt = sanitizeHtml(form.cleaned_data['contract_excerpt']),
168
                loophole = form.cleaned_data['loophole'],
Okhin's avatar
Okhin committed
169
170
                activationid = actid,
                old = False
stef's avatar
stef committed
171
                )
172
        v.save()
173
174
        conf=Confirmation(key=actid, email=form.cleaned_data['email'], violation=v)
        conf.save()
175
176
177
178
179
180
181
182
183
        c = Comment(
            comment=form.cleaned_data['comment'],
            submitter_email=form.cleaned_data['email'],
            submitter_name=form.cleaned_data['nick'],
            consent=form.cleaned_data['consent'],
            timestamp=datetime.now(),
            violation=v,
            )
        c.save()
Okhin's avatar
Okhin committed
184
        for f in self.request.FILES.getlist('attachments[]'):
185
186
187
188
189
190
191
            a=Attachment(comment=c, name=f.name, type=f.content_type)
            m = hashlib.sha256()
            for chunk in f.chunks():
                m.update(chunk)
            sname=m.hexdigest()
            a.storage.save(sname,f)
            a.save()
Okhin's avatar
Okhin committed
192
        messages.add_message(self.request, messages.INFO, _('Thank you for submitting this report, you will receive a verification email immediately, if not check your spam folder.'))
193
        return super(AddForm, self).form_valid(form)
stef's avatar
stef committed
194

195
196
197
    def get_context_data(self, **kwargs):
        context = super(AddForm, self).get_context_data(**kwargs)
        reports = sorted([(i['total'],i['id'])
198
                         for i in Violation.objects.values('id').exclude(state__in=['closed', 'ooscope', 'duplicate']).exclude(old=True).annotate(total=Count('confirmation'))],
stef's avatar
stef committed
199
                    reverse=True)
200
        confirms = sorted([(i['total'],i['country'])
201
                         for i in Violation.objects.values('country').exclude(state__in=['closed', 'ooscope', 'duplicate']).exclude(old=True).annotate(total=Count('confirmation'))],
stef's avatar
stef committed
202
                    reverse=True)
203
        operators = sorted([(i['total'],i['operator_ref__name'])
204
                         for i in Violation.objects.values('operator_ref__name').exclude(state__in=['closed', 'ooscope', 'duplicate']).exclude(old=True).annotate(total=Count('confirmation'))],
stef's avatar
stef committed
205
                     reverse=True)
206
        context['stats'] = [
207
                (_('Total confirmed reports'), len([i for i,z in reports if i>0])),
208
                (_('Countries with some confirmed reports'), len([i for i,z in confirms if i>0])),
Okhin's avatar
Okhin committed
209
                (_('Operators with some confirmed reports'), len([i for i,z in operators if i>0]))]
Okhin's avatar
Okhin committed
210
        context['violations'] = [fc.case for fc in FeaturedCase.objects.all() if not fc.case.old][:3]
211
        return context
stef's avatar
stef committed
212

213
214
215
class ViolationsList(ListView):
    template_name = 'list.html'
    context_object_name = 'violations'
216

217
    def get_queryset(self):
218
        queryset = Violation.objects.exclude(old=True)
219
        if 'operator' in self.kwargs:
Okhin's avatar
Okhin committed
220
            # If i Have operator I have a country
221
222
            queryset = Violation.objects.filter(country=self.kwargs['country'],
                    operator_ref__name=self.kwargs['operator'])
223
        if 'country' in self.kwargs:
224
            queryset = Violation.objects.filter(country=self.kwargs['country'])
Okhin's avatar
Okhin committed
225
        if 'all' not in self.request.GET:
226
            queryset = queryset.exclude(state__in=['ooscope', 'duplicate', 'closed'])
227
        return queryset
stef's avatar
stef committed
228

229
230
231
232
233
    def get_context_data(self, **kwargs):
        context = super(ViolationsList, self).get_context_data(**kwargs)

        if 'country' in self.kwargs:
            context['country'] = self.kwargs['country']
Okhin's avatar
Okhin committed
234
235
        else:
            countries = sorted([(i['total'], i['country'])
236
                for i in Violation.objects.values('country').exclude(state__in=['ooscope', 'duplicate', 'closed']).exclude(old=True).annotate(total=Count('country'))],
Okhin's avatar
Okhin committed
237
238
239
240
                reverse=True)
            countryweights=json.dumps([{'iso2': y, 'w': x} for x, y in countries])
            context['countries'] = countries
            context['countryweights'] = countryweights
241
242
243
244
245
246
247
248
        return context

class ViolationView(DetailView):
    model = Violation
    template_name = 'view.html'
    pk_url_kwarg = 'id'
    context_object_name = 'v'

Okhin's avatar
Okhin committed
249
class LookupView(SearchView):
Okhin's avatar
Okhin committed
250
    searchqueryset = SearchQuerySet().exclude(old=True)
251
252
    form_class = SearchViolation

Okhin's avatar
Okhin committed
253
254
255
256
257
258
259
260
261
    def get_queryset(self):
        return super(LookupView, self).get_queryset().exclude(old=True)

    def get_context_data(self, *args, **kwargs):
        context = super(LookupView, self).get_context_data(*args, **kwargs)
        if 'object_list' in context:
            context['object_list'] = [obj for obj in context['object_list'] if not obj.object.old]
        return context

262
class ViolationSearchView(SearchView):
Okhin's avatar
Okhin committed
263
    searchqueryset = SearchQuerySet().exclude(old=True)
Okhin's avatar
Okhin committed
264
265
    form_class = SearchViolation

Okhin's avatar
Okhin committed
266
267
268
    def get_queryset(self):
        return super(ViolationSearchView, self).get_queryset().exclude(old=True)

269
270
    def get_context_data(self, *args, **kwargs):
        context = super(ViolationSearchView, self).get_context_data(*args, **kwargs)
Okhin's avatar
Okhin committed
271
        countries = sorted([(k, len(list(g)),) for k, g in groupby(sorted([i['country'] for i in self.queryset.values('country')]))])
Okhin's avatar
Okhin committed
272
        countryweights=json.dumps([{'iso2': x, 'w': y} for x, y in countries])
Okhin's avatar
Okhin committed
273
274
        operators = sorted([(k, len(list(g)),) for k, g in groupby(sorted([i['operator_name'] for i in self.queryset.values('operator_name')]))])
        context['operators'] = operators
275
276
277
        context['countries'] = countries
        context['countryweights'] = countryweights
        context['stats'] = [
Okhin's avatar
Okhin committed
278
279
280
                (_('Number of reports in this search'), len(self.queryset.all())),
                (_('Countries with some confirmed reports'), len([c for c,v in countries if v > 0])),
                (_('Operators with some confirmed reports'), len([c for c,v in operators if v > 0])),
281
282
283
                ]
        return context

284
285
286
287
def get_attach(request,id):
    f = get_object_or_404(Attachment, pk=id)
    wrapper = FileWrapper(f.storage)
    response=HttpResponse(wrapper, mimetype=f.type)
288
    response['Content-Disposition'] = 'attachment; filename="%s"' % f.name
289
290
291
    response['Content-Length'] = f.storage.size
    return response

Okhin's avatar
Okhin committed
292
293
294
295
296
297
298
299
300
301
302
303
class ViolationCsvList(ListView):
    model = Violation
    template_name = 'csv.tmpl'
    content_type = 'text/csv'
    csv_name = 'respectmynet.csv'
    queryset = Violation.objects.all().annotate(confirmations=Count('confirmation')).order_by('-confirmations')

    def render_to_response(self, context, **kwargs):
        response = super(ListView, self).render_to_response(context, **kwargs)
        response['Content-Disposition']= 'attachment; filename={0}'.format(self.csv_name)
        return response

304
def ascsv(request):
Okhin's avatar
Okhin committed
305
    response = HttpResponse(content_type='text/csv')
306
307
308
309
310
311
312
313
314
315
316
    response['Content-Disposition'] = 'attachment; filename=respectmynet.csv'

    res=[]
    for v in Violation.objects.filter(activationid=''):
        res.append((v.state, v.country, v.operator, v.contract, v.resource, v.resource_name, v.type, v.media, v.temporary, v.contractual, v.contract_excerpt, v.loophole, v.editorial,v.comment_set.get().comment))
    t = loader.get_template('csv.tmpl')
    c = Context({
        'data': res,
    })
    response.write(t.render(c))
    return response
317
318
319
320
321
322
323
324
325
326

from sheet import save_ods
def asods(request):
    response = HttpResponse(mimetype='application/vnd.oasis.opendocument.spreadsheet')
    response['Content-Disposition'] = 'attachment; filename=respectmynet-ec_berec_tm_questionnaire.ods'
    save_ods()
    f=open('/tmp/ec_berec_tm_questionnaire.ods','r')
    response.write(f.read())
    f.close()
    return response