Browse Source

Implemented rough UI for accessing report results

Jeremy Stretch 7 years ago
parent
commit
79fdf641c0

+ 4 - 0
netbox/extras/management/commands/runreport.py

@@ -32,6 +32,10 @@ class Command(BaseCommand):
                     report = report_cls()
                     results = report.run()
 
+                    # Record the results
+                    ReportResult.objects.filter(report=report_name_full).delete()
+                    ReportResult(report=report_name_full, failed=report.failed, data=results).save()
+
                     # Report on success/failure
                     status = self.style.ERROR('FAILED') if report.failed else self.style.SUCCESS('SUCCESS')
                     for test_name, attrs in results.items():

+ 3 - 2
netbox/extras/migrations/0008_reports.py

@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.11.4 on 2017-09-21 20:31
+# Generated by Django 1.11.4 on 2017-09-22 15:21
 from __future__ import unicode_literals
 
 from django.conf import settings
@@ -20,8 +20,9 @@ class Migration(migrations.Migration):
             name='ReportResult',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('created', models.DateTimeField(auto_created=True)),
                 ('report', models.CharField(max_length=255, unique=True)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('failed', models.BooleanField()),
                 ('data', django.contrib.postgres.fields.jsonb.JSONField()),
                 ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
             ],

+ 2 - 1
netbox/extras/models.py

@@ -398,8 +398,9 @@ class ReportResult(models.Model):
     This model stores the results from running a user-defined report.
     """
     report = models.CharField(max_length=255, unique=True)
-    created = models.DateTimeField(auto_created=True)
+    created = models.DateTimeField(auto_now_add=True)
     user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='+', blank=True, null=True)
+    failed = models.BooleanField()
     data = JSONField()
 
     class Meta:

+ 15 - 4
netbox/extras/reports.py

@@ -20,7 +20,18 @@ def is_report(obj):
 
 def get_reports():
     """
-    Compile a list of all reports available across all modules in the reports path.
+    Compile a list of all reports available across all modules in the reports path. Returns a list of tuples:
+
+    [
+        (module_name, (
+            (report_name, report_class),
+            (report_name, report_class)
+        ),
+        (module_name, (
+            (report_name, report_class),
+            (report_name, report_class)
+        )
+    ]
     """
     module_list = []
 
@@ -30,8 +41,8 @@ def get_reports():
         report_list = []
 
         # Iterate through all Report classes within the module
-        for report_name, report_cls in inspect.getmembers(module, is_report):
-            report_list.append((report_name, report_cls))
+        for report_name, report_class in inspect.getmembers(module, is_report):
+            report_list.append((report_name, report_class))
 
         module_list.append((module_name, report_list))
 
@@ -92,7 +103,7 @@ class Report(object):
         """
         if level not in LOG_LEVEL_CODES:
             raise Exception("Unknown logging level: {}".format(level))
-        logline = [timezone.now(), level, obj, message]
+        logline = [timezone.now().isoformat(), level, str(obj), message]
         self.results[self.active_test]['log'].append(logline)
 
     def log_success(self, obj, message=None):

+ 15 - 4
netbox/extras/views.py

@@ -1,7 +1,8 @@
 from __future__ import unicode_literals
+from collections import OrderedDict
 
 from django.contrib.auth.mixins import PermissionRequiredMixin
-from django.shortcuts import get_object_or_404, redirect, render
+from django.shortcuts import get_object_or_404, render
 from django.urls import reverse
 from django.views.generic import View
 
@@ -52,9 +53,19 @@ class ReportListView(View):
     def get(self, request):
 
         reports = get_reports()
-        results = {r.name: r for r in ReportResult.objects.all()}
+        results = {r.report: r for r in ReportResult.objects.all()}
+
+        foo = []
+        for module, report_list in reports:
+            module_reports = []
+            for report_name, report_class in report_list:
+                module_reports.append({
+                    'name': report_name,
+                    'description': report_class.description,
+                    'results': results.get('{}.{}'.format(module, report_name), None)
+                })
+            foo.append((module, module_reports))
 
         return render(request, 'extras/report_list.html', {
-            'reports': reports,
-            'results': results,
+            'reports': foo,
         })

+ 46 - 7
netbox/templates/extras/report_list.html

@@ -5,13 +5,52 @@
     <h1>Reports</h1>
     <div class="row">
         <div class="col-md-9">
-            {% for module, report_list in reports %}
-                <h2>{{ module|bettertitle }}</h2>
-                <ul>
-                    {% for name, cls in report_list %}
-                        <li>{{ name }}</li>
-                    {% endfor %}
-                </ul>
+            {% for module, module_reports in reports %}
+                <h3>{{ module|bettertitle }}</h3>
+                <table class="table table-hover">
+                    <thead>
+                        <tr>
+                            <th>Name</th>
+                            <th>Description</th>
+                            <th>Last Run</th>
+                            <th class="text-right">Status</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        {% for report in module_reports %}
+                            <tr>
+                                <td>{{ report.name }}</td>
+                                <td>{{ report.description|default:"" }}</td>
+                                {% if report.results %}
+                                    <td>{{ report.results.created }}</td>
+                                    <td class="text-right">
+                                        {% if report.results.failed %}
+                                            <label class="label label-danger">Failed</label>
+                                        {% else %}
+                                            <label class="label label-success">Passed</label>
+                                        {% endif %}
+                                    </td>
+                                {% else %}
+                                    <td class="text-muted">Never</td>
+                                    <td class="text-muted text-right">&mdash;</td>
+                                {% endif %}
+                            </tr>
+                            {% for method, stats in report.results.data.items %}
+                                <tr>
+                                    <td colspan="4">
+                                        <div class="pull-right">
+                                            <label class="label label-success">{{ stats.success }}</label>
+                                            <label class="label label-info">{{ stats.info }}</label>
+                                            <label class="label label-warning">{{ stats.warning }}</label>
+                                            <label class="label label-danger">{{ stats.failed }}</label>
+                                        </div>
+                                        <span style="font-family: monospace">{{ method }}</span>
+                                    </td>
+                                </tr>
+                            {% endfor %}
+                        {% endfor %}
+                    </tbody>
+                </table>
             {% endfor %}
         </div>
     </div>