plotly logs example

logs.urls.py

 1 """Gestion des URLs pour la gestion des logs des (hardware-id, product_name).
 2
 3 - https://gdevops.gitlab.io/tuto_django/tutos/hacksoft/hacksoft.html#urls
 4
 5 """
 6 from django.urls import path, re_path
 7 from . import views
 8
 9 app_name = "logs"
10
11 urlpatterns = [
12     path("plot_program/", views.PlotProgram.as_view(), name="plot_program",),
13     path(
14         "plot_all_programs/", views.PlotAllPrograms.as_view(), name="plot_all_programs",
15     ),
16     path(
17         "plot_program/<str:product_name>/",
18         views.PlotProgram.as_view(),
19         name="plot_program_name",
20     ),
21 ]

logs.plots.py

  1"""
  2logs.plots.py
  3
  4
  5- https://plot.ly/python/pie-charts
  6
  7Traduction de pie chart
  8=========================
  9
 10- https://www.linguee.fr/anglais-francais/traduction/pie+chart.html
 11
 12:pie chart:
 13    diagramme circulaire
 14    graphique circuliare
 15    graphique sectoriel
 16    diagramme en camenbert
 17
 18
 19"""
 20import logging
 21from typing import List
 22
 23import numpy as np
 24import pendulum
 25import plotly.graph_objs as go
 26from logs.models import LogHardwareId
 27from logs.models import StatsProductName
 28from logs.models import StatsProductNameVersion
 29from plotly.offline import plot
 30from plotly.subplots import make_subplots
 31from programs.models import Program
 32
 33logger = logging.getLogger(__name__)
 34
 35
 36def plot_program(product_name: str = "id3 Notaria Autenticacion") -> plot:
 37    """
 38    - https://plot.ly/python/pie-charts/#pie-charts-in-subplots
 39    """
 40    logs_hardware = LogHardwareId.objects.filter(
 41        product_name=product_name,
 42    ).order_by("product_name", "-version")
 43
 44    nb_total_users = logs_hardware.count()
 45
 46    # définition de l'ensemble des versions
 47    set_versions = set()
 48    for log in logs_hardware:
 49        set_versions.add(log.version)
 50
 51    # Exemple: ["2.7.1", "2.6.1"]
 52    labels_versions = list(set_versions)
 53    nb_versions = len(labels_versions)
 54
 55    # calcul du nombre de hardware-id utilisant cette version
 56    values_versions = []
 57    for version in labels_versions:
 58        total_version = logs_hardware.filter(version=version).count()
 59        values_versions.append(total_version)
 60
 61    # on concatène aux labels le nombre d'occurrences et le pourcentage
 62    for i, total_version in enumerate(values_versions):
 63        p = total_version / nb_total_users
 64        labels_versions[
 65            i
 66        ] = f"version {labels_versions[i]} ({str(total_version)} users => {p:.1%})"
 67
 68    fig = go.Figure(
 69        data=[go.Pie(labels=labels_versions, values=values_versions, name=product_name)]
 70    )
 71    # fig.update_traces(hole=0.4, hoverinfo="label+value+name")
 72    fig.update_traces(hole=0.4, hoverinfo="label")
 73    if nb_versions <= 1:
 74        str_nb_versions = f"{nb_versions} version"
 75    else:
 76        str_nb_versions = f"{nb_versions} versions"
 77
 78    if nb_total_users > 0:
 79        title_text = f"{str_nb_versions} for {product_name} ({nb_total_users} users)"
 80    else:
 81        now = pendulum.now()
 82        title_text = (
 83            f"{str_nb_versions} for {product_name} (not used at the moment "
 84            f"{now.year}-{now.month:02}-{now.day:02} {now.hour:02}H{now.minute:02})"
 85        )
 86
 87    fig.update_layout(
 88        title_text=title_text,
 89    )
 90    plot_div = plot(fig, output_type="div", include_plotlyjs=False)
 91    return plot_div
 92
 93
 94def plot_all_programs() -> List[plot]:
 95    """Returns all the program plots"""
 96    plots = []
 97    for product_name in (
 98        Program.objects.filter(version_is_ready=True)
 99        .values_list("product_name", flat=True)
100        .distinct()
101    ):
102        plot = plot_program(product_name)
103        plots.append(plot)
104
105    return plots

logs.models.py

  1"""Logs des requetes sur getLastProductVersion.
  2
  3"""
  4import logging
  5from dataclasses import dataclass
  6from dataclasses import field
  7from typing import List
  8
  9import arrow
 10import django
 11from dateutil import tz
 12from django.db import connection
 13from django.db import models
 14from django.urls import reverse
 15from django.utils import timezone
 16from django.utils.translation import ugettext as _
 17from programs.models import Program
 18
 19# http://crsmithdev.com/arrow/
 20# https://labix.org/python-dateutil
 21# https://docs.djangoproject.com/en/dev/topics/db/sql/
 22# https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-editing/
 23
 24# Get an instance of a logger
 25logger = logging.getLogger(__name__)
 26
 27
 28@dataclass
 29class StatsProductNameVersion:
 30    product_name: str
 31    version: str = ""
 32    nb_logs: int = 0
 33    stats_product_name: "StatsProductName" = None
 34
 35    @property
 36    def percent_logs(self):
 37        percent = ""
 38        p = self.nb_logs / self.stats_product_name.nb_total_logs
 39        percent = f"({p:.2%})"
 40        return percent
 41
 42
 43@dataclass
 44class StatsProductName:
 45    product_name: str
 46    nb_total_logs: int = 0
 47    versions: List[StatsProductNameVersion] = field(default_factory=list)
 48
 49
 50class LogHardwareId(models.Model):
 51    """Description des logs à partir du hardware-id.
 52
 53        http -b "localhost:8005/programs/getLastProductVersion/?product_name=id3 Notaria Autenticacion&version__gt=2.4.0&culture=es-CO&hardware_id=35ESDNN-486MAB5-4F3FWCJ-E5N8E02" 'Authorization: Token c62a063ef406c807f89b5e22706e0ad5de443442'
 54
 55    On logge les informations suivantes lorsqu'une requete arrive sur getLastProductVersion
 56
 57    - hardware_id
 58    - product_name
 59    - date de la requete
 60    - programme
 61
 62    """
 63
 64    class Meta(object):
 65        managed = True
 66        db_table = "log_hardware"
 67        ordering = ["-date_connexion"]
 68        verbose_name = _("log hardware id")
 69        verbose_name_plural = _("Les logs des hardware id")
 70        # https://docs.djangoproject.com/en/dev/ref/models/constraints/#uniqueconstraint
 71        constraints = [
 72            models.UniqueConstraint(
 73                fields=["hardware_id", "product_name"],
 74                name="unique_hardware_id_productname",
 75            )
 76        ]
 77        indexes = [
 78            models.Index(fields=["date_connexion"], name="index_date_connexion"),
 79            models.Index(fields=["hardware_id"], name="index_hardware_id"),
 80        ]
 81
 82    id = models.AutoField(primary_key=True)
 83    hardware_id = models.CharField(max_length=100, help_text=_("Identifiant du PC"))
 84    date_connexion = models.DateTimeField(
 85        help_text=_("Dernière date de connexion"), auto_now_add=True
 86    )
 87    product_name = models.CharField(
 88        max_length=200,
 89        default="",
 90        help_text=_("Le nom du programme"),
 91    )
 92    version = models.CharField(
 93        max_length=50,
 94        default="0.1.0",
 95        help_text=_("La version du programme. Ex: 0.2.0. Voir http://semver.org/"),
 96        verbose_name=_("Version"),
 97    )
 98    program = models.ForeignKey(
 99        Program,
100        on_delete=models.CASCADE,
101        blank=True,
102        null=True,
103        related_name="programs",
104        help_text=_("Le programme"),
105    )
106    error = models.CharField(
107        max_length=200,
108        default="",
109        blank=True,
110        null=True,
111        help_text=_("Erreur"),
112    )
113
114    def __str__(self):
115        """
116        Affichage des logs hardware-id + product_name.
117        """
118        message = f"{self.id=} {self.date_connexion=} {self.product_name=} {self.version=} {self.program=}"
119        return message

logs.views.py

 1 """
 2 logs.views
 3
 4 """
 5
 6 from django.shortcuts import render
 7 import logging
 8
 9 from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
10 from django.views.generic import TemplateView
11 from . import plots
12
13 logger = logging.getLogger(__name__)
14
15
16 class PlotProgram(TemplateView):
17     """
18
19     - https://plot.ly/python/pie-charts/#pie-charts-in-subplots
20
21     """
22
23     template_name = "logs/plot.html"
24
25     def get_context_data(self, **kwargs):
26         context = super().get_context_data(**kwargs)
27         product_name = None
28         if "product_name" in self.kwargs:
29             product_name = self.kwargs["product_name"]
30             logger.info(f"PlotPrograms {product_name=}")
31             plot = plots.plot_program(product_name)
32         else:
33             plot = plots.plot_program()
34
35         context["plot"] = plot
36         if product_name is None:
37             product_name = "i version"
38         context["product_name"] = product_name
39         return context
40
41
42 class PlotAllPrograms(TemplateView):
43     """
44
45     - https://plot.ly/python/pie-charts/#pie-charts-in-subplots
46
47     """
48
49     template_name = "logs/plot_all_programs.html"
50
51     def get_context_data(self, **kwargs):
52         context = super().get_context_data(**kwargs)
53         les_plots = plots.plot_all_programs()
54         context["les_plots"] = les_plots
55         return context

template

 1 <div class="dropdown-menu" aria-labelledby="navbarDropdown">
 2   <div class="dropdown-divider"></div>
 3   {% url 'logs:plot_all_programs' as url_plot_all_programs %}
 4   <a class="dropdown-item" href="{{ url_plot_all_programs }}">All programs</a>
 5   <div class="dropdown-divider"></div>
 6   {% url 'logs:plot_program' as url_plot_program %}
 7   <a class="dropdown-item" href="{{ url_plot_program }}i version">Program1 version</a>
 8   <a class="dropdown-item" href="{{ url_plot_program }}i version - alpha">Program1 version PIN</a>
 9   <div class="dropdown-divider"></div>
10   <a class="dropdown-item" href="{{ url_plot_program }}i Firma">Program1 Firma</a>
11   <a class="dropdown-item" href="{{ url_plot_program }}i Firma - PIN">Program1 Firma PIN</a>
12   <div class="dropdown-divider"></div>
13   <a class="dropdown-item" href="{{ url_plot_program }}i Usuarios">Program1 Usuarios</a>
14   <a class="dropdown-item" href="{{ url_plot_program }}i Usuarios - PIN">Program1 Usuarios PIN</a>
15   <div class="dropdown-divider"></div>
16   <a class="dropdown-item" href="{{ url_plot_program }}i Web Services">Program1 Web Services</a>
17 </div>