views.py 14.8 KB
Newer Older
1
import collections
Jerome Flesch's avatar
Jerome Flesch committed
2
import logging
3

4
5
import django.db.models
import django.db.models.functions
6
7
8
9
import django.http
import django.views.decorators.http

import backend.models.scannerdb
10
from . import forms
11
12


13
LOGGER = logging.getLogger(__name__)
Jerome Flesch's avatar
Jerome Flesch committed
14
15


16
17
DEFAULT_IMG = "unknown.png"

18
SCANNER_TYPES = {
19
    None: DEFAULT_IMG,
20
    # about 231x171
21
22
    "adf": "adf.png",
    "adf_duplex": "adf.png",
23
24
25
26
27
28
29
30
    "flatbed": "flatbed.png",
    "flatbed_adf": "flatbed_and_feeder.png",
    "flatbed_adf_duplex": "flatbed_and_feeder.png",
    "handheld": "handheld.png",
    "portable": "portable.png",
}


31
32
33
34
35
36
37
38
39
40
41
DEFAULT_CTX = {
    "oses": [],
    "selected_os": None,
    "manufacturers": [],
    "selected_manufacturer": None,
    "scanners": [],
    "selected_scanner": None,
    "reports": [],
    "selected_report": None,
}

Jerome Flesch's avatar
Jerome Flesch committed
42

43
44
45
46
47
48
49
50
def get_operating_systems():
    oses = backend.models.scannerdb.ScanReport.objects.filter(moderated=True)
    oses = oses.values('os').distinct()
    oses = [os['os'].title() for os in oses]
    oses.sort()
    return oses


51
52
53
54
55
56
57
58
59
60
61
62
def is_last_report_successful(oses, base_req):
    out = []
    for os in oses:
        report = base_req.filter(os__iexact=os)
        report = report.order_by("-creation_date")[:1]
        if len(report) <= 0:
            out.append({'os': os, 'success': None})
            continue
        out.append({'os': os, 'success': report[0].successful})
    return out


63
64
65
66
67
68
69
def get_success_rate(oses, base_req):
    """
    Compute the success rate for each scanner manufactuer.
    Problem is that we must only take the latest moderated report into account
    each time.
    """

70
    # TODO(Jflesch): On the homepage, that could probably be optimized
71

72
73
    totals = collections.defaultdict(
        lambda: collections.defaultdict(lambda: 0)
74
    )
75
76
77
78
79
80
81
82
83
84
85
    scanners = base_req.values("scanner__id").distinct()
    for scanner in scanners:
        successes = base_req.filter(scanner=scanner['scanner__id'])
        successes = is_last_report_successful(oses, successes)
        for success in successes:
            if success['success'] is None:
                continue
            if success['success']:
                totals[success['os']]['success'] += 1
            else:
                totals[success['os']]['failed'] += 1
86
87
88

    stats = []
    for os in oses:
89
90
        s = totals[os]['success']
        f = totals[os]['failed']
91
92
93
94
95
96
97
        if s + f == 0:
            stats.append({'os': os, 'percent': None})
            continue
        stats.append({'os': os, 'percent': int((s / (s + f)) * 100)})
    return stats


98
@django.views.decorators.http.require_GET
99
def home(request):
100
    ctx = dict(DEFAULT_CTX)
101
102
103
104
    manufacturers = backend.models.scannerdb.ScannerManufacturer.objects
    manufacturers = manufacturers.filter(moderated=True)
    manufacturers = manufacturers.order_by(
        django.db.models.functions.Lower('name')
105
    )
106
    ctx['manufacturers'] = manufacturers
107

108
    ctx['unmoderated'] = []
109
    if request.user.is_authenticated:
110
111
112
113
114
        ctx['unmoderated'] = (
            backend.models.scannerdb.ScanReport.objects.filter(
                moderated=False,
                admin_comment=None,
            ).order_by('-creation_date')
115
116
        )

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    # TODO(Jflesch): Caching

    stats = {}
    stats['oses'] = get_operating_systems()

    # Compute global stats
    stats['global'] = get_success_rate(
        stats['oses'], backend.models.scannerdb.ScanReport.objects.filter(
            moderated=True,
            scanner__moderated=True,
            scanner__manufacturer__moderated=True,
        )
    )

    # Computer per manufacturer stats
    stats['per_manufacturer'] = []
    for manufacturer in manufacturers:
        stats['per_manufacturer'].append({
            "manufacturer": manufacturer,
            "stats": get_success_rate(
                stats['oses'],
                backend.models.scannerdb.ScanReport.objects.filter(
                    moderated=True,
                    scanner__moderated=True,
                    scanner__manufacturer=manufacturer,
                )
            )
        })
145

146
    ctx['stats'] = stats
147
    return django.shortcuts.render(request, 'scandb_home.html', ctx)
148
149


150
@django.views.decorators.http.require_GET
151
def vendor(request, vendor_id):
152
    ctx = dict(DEFAULT_CTX)
153
    try:
154
155
156
157
        manufacturer = (
            backend.models.scannerdb.ScannerManufacturer.objects.get(
                id=int(vendor_id)
            )
158
        )
159
        ctx['selected_manufacturer'] = manufacturer
160
161
162
163
164
165
166
167
168
169
170
    except (
                backend.models.scannerdb.ScannerManufacturer.DoesNotExist,
                ValueError
            ):
        raise django.http.Http404('Vendor {} not found'.format(vendor_id))

    ctx['manufacturers'] = (
        backend.models.scannerdb.ScannerManufacturer.objects.filter(
            moderated=True
        ).order_by("name")
    )
171
172
173

    scanners = backend.models.scannerdb.Scanner.objects
    scanners = scanners.filter(
174
        moderated=True, manufacturer=ctx['selected_manufacturer']
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    )
    scanners = scanners.order_by(django.db.models.functions.Lower("name"))
    ctx['scanners'] = scanners

    # TODO(Jflesch): Caching

    stats = {}
    stats['oses'] = get_operating_systems()
    stats['global'] = get_success_rate(
        stats['oses'], backend.models.scannerdb.ScanReport.objects.filter(
            scanner__manufacturer=ctx['selected_manufacturer'],
            moderated=True,
            scanner__moderated=True,
            scanner__manufacturer__moderated=True,
        )
    )
191

192
193
194
195
196
197
198
199
200
201
202
203
204
205
    stats['per_scanner'] = []
    for scanner in scanners:
        stats['per_scanner'].append({
            'scanner': scanner,
            'successes': is_last_report_successful(
                stats['oses'],
                backend.models.scannerdb.ScanReport.objects.filter(
                    scanner=scanner,
                    moderated=True,
                    scanner__moderated=True,
                    scanner__manufacturer__moderated=True,
                )
            ),
        })
206

207
    ctx['stats'] = stats
208
    return django.shortcuts.render(request, 'scandb_vendor.html', ctx)
209

210

211
212
def get_scanner_img(scanner):
    try:
213
214
215
        last_report = backend.models.scannerdb.ScanReport.objects.filter(
            scanner=scanner
        ).order_by("-creation_date")[0]
216
        return SCANNER_TYPES.get(last_report.scanner_type, DEFAULT_IMG)
217
218
219
220
221
222
    except Exception:
        return DEFAULT_IMG


@django.views.decorators.http.require_GET
def scanner(request, scanner_id):
223
    ctx = dict(DEFAULT_CTX)
224
    try:
225
        ctx['selected_scanner'] = backend.models.scannerdb.Scanner.objects.get(
226
227
            id=int(scanner_id)
        )
228
229
    except (backend.models.scannerdb.Scanner.DoesNotExist, ValueError):
        raise django.http.Http404('Scanner {} not found'.format(scanner_id))
230
    ctx['selected_manufacturer'] = ctx['selected_scanner'].manufacturer
231
232
233
234
235
236
    ctx['manufacturers'] = (
        backend.models.scannerdb.ScannerManufacturer.objects.filter(
            moderated=True
        ).order_by('name')
    )
    ctx['scanners'] = backend.models.scannerdb.Scanner.objects.filter(
237
        moderated=True, manufacturer=ctx['selected_manufacturer']
238
    ).order_by("name")
239
240

    ctx['reports'] = backend.models.scannerdb.ScanReport.objects.filter(
241
        moderated=True, scanner=ctx['selected_scanner']
242
243
244
245
    ).order_by("-creation_date")
    ctx['scanner_type_img'] = get_scanner_img(ctx['selected_scanner'])

    return django.shortcuts.render(request, 'scandb_scanner.html', ctx)
246

247
248

def get_report(report_id):
249
    try:
250
251
252
253
254
255
256
        report = backend.models.scannerdb.ScanReport.objects.get(
            id=int(report_id)
        )
    except (backend.models.scannerdb.ScanReport.DoesNotExist, ValueError):
        raise django.http.Http404('Report {} not found'.format(report_id))
    return report

257

258
259
260
@django.views.decorators.http.require_GET
def report(request, report_id):
    report_db = get_report(report_id)
261
262
263
264
    ctx = dict(DEFAULT_CTX)
    ctx['selected_report'] = report_db
    ctx['selected_scanner'] = report_db.scanner
    ctx['selected_manufacturer'] = ctx['selected_scanner'].manufacturer
265
266
267
268
269
270
    ctx['manufacturers'] = (
        backend.models.scannerdb.ScannerManufacturer.objects.filter(
            moderated=True
        ).order_by('name')
    )
    ctx['scanners'] = backend.models.scannerdb.Scanner.objects.filter(
271
        moderated=True, manufacturer=ctx['selected_manufacturer']
272
    ).order_by('name')
273
    ctx['reports'] = backend.models.scannerdb.ScanReport.objects.filter(
274
        moderated=True, scanner=ctx['selected_scanner']
275
    ).order_by('-creation_date')
276

277
    ctx['admin_comment_form'] = forms.AdminCommentForm()
278

279
    scanner_type_img = get_scanner_img(ctx['selected_scanner'])
Jerome Flesch's avatar
Jerome Flesch committed
280

281
282
283
284
285
286
    attachments = backend.models.scannerdb.ScanReportAttachment.objects.filter(
        report=report_db
    )

    report_tree = report_db.get_tree(request)

287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
    # Things I missed in the migration ...
    if 'system' in report_tree:
        r_sys = report_tree['system']
        if 'sys_type' in r_sys:
            r_sys['sys_os_name'] = r_sys['sys_type']
        if 'sys_python' in r_sys:
            r_sys['sys_software_python'] = r_sys['sys_python']
        if 'sys_platform_short' in r_sys:
            r_sys['sys_software_system'] = r_sys['sys_platform_short']
        if 'sys_platform_detailed' in r_sys:
            r_sys['sys_platform_distribution'] = r_sys['sys_platform_detailed']
        if 'sys_platform_release' in r_sys:
            r_sys['sys_software_release'] = r_sys['sys_platform_release']
        if 'sys_system' in r_sys:
            r_sys['sys_software_system'] = r_sys['sys_system']
302
303
304
305
306
307
308
309
        if 'sys_nb_cpus' in r_sys:
            r_sys['sys_cpu_count'] = r_sys['sys_nb_cpus']
        if 'sys_proc' in r_sys:
            r_sys['sys_platform_processor'] = r_sys['sys_proc']
        if 'sys_machine' in r_sys:
            r_sys['sys_platform_machine'] = r_sys['sys_machine']
        if 'sys_mem' in r_sys:
            r_sys['sys_platform_mem'] = r_sys['sys_mem']
310
311
312
313
314
        if 'versions' in r_sys:
            r_ver = r_sys['versions']
            if 'ironscanner' in r_ver:
                r_ver['test_program'] = r_ver['ironscanner']

315
316
317
318
    successful = False
    if 'scantest' in report_tree and 'successful' in report_tree['scantest']:
        successful = bool(report_tree['scantest']['successful'])

319
    ctx.update({
320
        'report_db': report_db,
321
        'report': report_tree,
Jerome Flesch's avatar
Jerome Flesch committed
322
        'scanner_type_img': scanner_type_img,
323
324
        # we don't store boolean in the DB, only integer --> for proper
        # display,we must convert the successful status to a boolean
325
        'successful': successful,
326
        'attachments': attachments,
327
    })
328
    return django.shortcuts.render(request, 'scandb_report.html', ctx)
329

330

331
def fix_scanner_name(name):
332
    # TODO(Jflesch)
333
334
335
336
337
338
339
340
341
342
343
344
345
    name = name.lower()
    name = name.replace("_", " ")
    name = name.replace("/", " ")
    # this is marketing crap that is found mostly on HP scanners but on some
    # other too
    name = name.replace(" professional", "")
    name = name.replace(" pro", "")
    name = name.replace(" series", "")
    name = name.replace(" photo", "")
    name = name.strip()
    return name


346
@django.views.decorators.http.require_POST
347
def delete_report(request, report_id):
348
349
    if not request.user.is_authenticated or not request.user.is_superuser:
        return django.http.HttpResponseForbidden()
350
    try:
351
352
353
354
355
        report = backend.models.scannerdb.ScanReport.objects.get(
            id=int(report_id)
        )
    except (backend.models.scannerdb.ScanReport.DoesNotExist, ValueError):
        raise django.http.Http404('Report {} not found'.format(report_id))
356
    report.delete()
357
358
359
    return django.http.HttpResponseRedirect(
        redirect_to=django.urls.reverse("scannerdb:home")
    )
360
361


362
@django.views.decorators.http.require_POST
363
def validate_report(request, report_id):
364
365
    if not request.user.is_authenticated or not request.user.is_superuser:
        return django.http.HttpResponseForbidden()
366
    try:
367
368
369
370
371
        report = backend.models.scannerdb.ScanReport.objects.get(
            id=int(report_id)
        )
    except (backend.models.scannerdb.ScanReport.DoesNotExist, ValueError):
        raise django.http.Http404('Report {} not found'.format(report_id))
372
373
374
375
376
377
378
379

    report.moderated = True
    report.save()
    report.scanner.moderated = True
    report.scanner.save()
    report.scanner.manufacturer.moderated = True
    report.scanner.manufacturer.save()

380
381
382
    return django.http.HttpResponseRedirect(
        redirect_to=django.urls.reverse("scannerdb:report", args=[report.id])
    )
383
384


385
386
@django.views.decorators.http.require_POST
def comment_report(request, report_id):
387
    if (not request.user.is_authenticated or
388
            not request.user.is_superuser):
389
        return django.http.HttpResponseForbidden()
390
    try:
391
392
393
394
395
        report = backend.models.scannerdb.ScanReport.objects.get(
            id=int(report_id)
        )
    except (backend.models.scannerdb.ScanReport.DoesNotExist, ValueError):
        raise django.http.Http404('Report {} not found'.format(report_id))
396

397
    form = forms.AdminCommentForm(request.POST)
398
    if not form.is_valid():
399
        return django.http.HttpResponseForbidden()
400
401

    report.moderated = False
402
    report.admin_comment = form.cleaned_data['reason']
403
404
    report.save()

405
406
407
    return django.http.HttpResponseRedirect(
        redirect_to=django.urls.reverse("scannerdb:report", args=[report.id])
    )
408
409


410
@django.views.decorators.http.require_GET
411
412
413
414
415
416
417
418
def report_traces_colorized(request, report_id, attachment_id):
    report = django.shortcuts.get_object_or_404(
        backend.models.scannerdb.ScanReport.objects,
        pk=int(report_id)
    )
    attachment = django.shortcuts.get_object_or_404(
        backend.models.scannerdb.ScanReportAttachment.objects,
        pk=int(attachment_id)
419
    )
420

421
422
423
424
    if report != attachment.report:
        return django.http.HttpResponseForbidden()
    if attachment.mime_type != "text/plain":
        return django.http.HttpResponseForbidden()
425

426
427
    traces = []
    with attachment.attachment.open("r") as fd:
428
        traces = fd.readlines()
429
430
    for (idx, line) in enumerate(traces):
        traces[idx] = line.strip()
431
432
433
434
435
436

    previous = None
    chunk = []
    colorized = []
    for trace in traces:
        current = trace.split(" ")[0]
437
        if len(current) > 0 and current[0] == '[':
438
            current = current[1:]
439
        if len(current) > 0 and current[-1] == ']':
440
            current = current[:-1]
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460

        if previous is not None and previous != current:
            color = "black"
            if previous == "DEBUG":
                color = "gray"
            elif previous == "WARNING":
                color = "#FFA500"  # orange
            elif previous == "ERROR":
                color = "red"
            colorized.append({
                'type': previous,
                'color': color,
                'lines': chunk,
            })
            chunk = []

        chunk.append(trace)
        previous = current

    ctx = {
461
        'report_db': report,
462
463
464
        'traces': colorized,
    }

465
    return django.shortcuts.render(request, 'scandb_traces.html', ctx)