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>