PK!django_ping_me/__init__.pyPK! django_ping_me/admin.pyfrom django.contrib import admin from .models import Domain, Ping, PingSummary admin.site.register(Domain) admin.site.register(Ping) admin.site.register(PingSummary)PK!xXXdjango_ping_me/apps.pyfrom django.apps import AppConfig class PingMeConfig(AppConfig): name = 'ping_me' PK!`9 9 django_ping_me/cron.pyfrom .models import Domain, Ping, PingSummary from django_cron import CronJobBase, Schedule # https://django-cron.readthedocs.io import aloha.network import datetime import logging import pytz logger = logging.getLogger(__name__) class PingMeCronJob(CronJobBase): RUN_EVERY_MINS = 1 schedule = Schedule(run_every_mins=RUN_EVERY_MINS) code = 'ping_me.cron' def do(self): """Fonction principale d'exécution du cron""" all_domains = Domain.objects.order_by('url') logger.debug("Mise à jour des domaines PingMe! {} domaine(s) trouvé(s) !" .format(len(all_domains))) for domain in all_domains: self.update_domain(domain) def update_domain(self, domain): """Met à jour un domaine""" try: last_ping = Ping.objects.filter(domain=domain).latest('date') except Ping.DoesNotExist: last_ping = None ping = self.create_ping(domain) if last_ping: # Si le domaine ne répond plus au ping if last_ping.success and not ping.success: logger.error("Le domaine {} vient de tomber! :-o".format(domain.url)) # Si le dernier ping date du mois dernier if last_ping.date.month != ping.date.month: self.clean_pings(domain, last_ping) def create_ping(self, domain): """Exécute un nouveau ping et l'enregistre en base de données""" ip_address = aloha.network.resolve_hostname(domain.url) ping = Ping(address=ip_address, domain=domain) if aloha.network.is_http_alive(domain.url, timeout=5): ping.success = True logger.debug("Site {} fonctionnel !".format(domain.url)) else: ping.success = False logger.warning("Site {} cassé ! :-(".format(domain.url)) ping.save() return ping def clean_pings(self, domain, last_ping): """Nettoie les pings du mois précédent et crée un résumé en base de données""" start_date = datetime.datetime( year=last_ping.date.year, month=last_ping.date.month, day=1, tzinfo=pytz.UTC ) end_date = start_date + datetime.timedelta(days=32) end_date = end_date.replace(day=1) all_pings = Ping.objects.filter(domain=domain, date__range=(start_date, end_date)) logger.info("clean_pings: {} pings trouvés pour le mois {}" .format(len(all_pings), start_date.strftime('%m/%Y'))) summary = PingSummary(domain=domain, date=start_date.date()) for ping in all_pings: if ping.success: summary.nb_success += 1 else: summary.nb_errors += 1 summary.save() all_pings.delete()PK!ff)django_ping_me/migrations/0001_initial.py# Generated by Django 2.0.6 on 2018-08-19 19:46 from django.conf import settings from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='Domain', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('url', models.CharField(max_length=255, verbose_name='Adresse')), ('description', models.CharField(max_length=255, verbose_name='Description')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='Ping', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('date', models.DateTimeField(auto_now_add=True)), ('success', models.BooleanField()), ('address', models.GenericIPAddressField()), ('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_ping_me.Domain')), ], ), migrations.CreateModel( name='PingSummary', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('date', models.DateField()), ('nb_success', models.IntegerField(default=0)), ('nb_errors', models.IntegerField(default=0)), ('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_ping_me.Domain')), ], ), ] PK!%django_ping_me/migrations/__init__.pyPK!'i$ django_ping_me/models.pyfrom django.conf import settings from django.db import models import datetime class Domain(models.Model): url = models.CharField(verbose_name='Adresse', max_length=255) description = models.CharField(verbose_name='Description', max_length=255) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) @property def last_ping(self): return Ping.objects.filter(domain=self).latest('date') @property def last_month_summary(self): date = datetime.date.today().replace(day=1) date = date - datetime.timedelta(days=1) date = date.replace(day=1) return PingSummary.objects.get(domain=self, date=date) @property def current_month_summary(self): summary = PingSummary(domain=self) summary.date = datetime.date.today().replace(day=1) all_pings = Ping.objects.filter(domain=self, date__gte=summary.date) for ping in all_pings: if ping.success: summary.nb_success += 1 else: summary.nb_errors += 1 return summary def __str__(self): desc = self.description if self.user.email: desc += " - " + self.user.email return "{} ({})".format(self.url, desc) class Ping(models.Model): date = models.DateTimeField(auto_now_add=True) success = models.BooleanField() address = models.GenericIPAddressField() domain = models.ForeignKey('Domain', on_delete=models.CASCADE) def __str__(self): success = 'success' if self.success else 'fail' return "{:%Y-%m-%d %H:%M:%S} - {} - {} - {}"\ .format(self.date, self.domain.url, self.address, success) class PingSummary(models.Model): date = models.DateField() domain = models.ForeignKey('Domain', on_delete=models.CASCADE) nb_success = models.IntegerField(default=0) nb_errors = models.IntegerField(default=0) @property def nb_attempt(self): return self.nb_success + self.nb_errors @property def percentage(self): if self.nb_attempt <= 0: return 0 return self.nb_success * 100 / self.nb_attempt def to_str(self): return "{:,} succès et {:,} échecs"\ .format(self.nb_success, self.nb_errors)\ .replace(',', ' ') def __str__(self): return "{:%Y:%m} - {} - {}"\ .format(self.date, self.domain.url, self.to_str())PK!es  1django_ping_me/templates/ping_me/domain_list.html{% load static %} {% include 'ping_me/partials/edit_popup.html' %} {% if domain_list %} {% include 'ping_me/partials/delete_popup.html' %} {% if user.is_authenticated and user.is_staff %} {% endif %} {% for domain in domain_list %} {% if user.is_authenticated and user.is_staff %} {% endif %} {% endfor %}
Domain IP Description {{ current_month|date:"F Y"|capfirst }} {{ last_month|date:"F Y"|capfirst }}Bound to Action
{% if domain.last_ping and domain.last_ping.success %} OK {% else %} NOK {% endif %} {{ domain.url }} (see website) {% if domain.last_ping %} {{ domain.last_ping.address }} {% else %} No IP {% endif %} {{ domain.description }} {% include 'ping_me/partials/summary_percentage.html' with summary=domain.current_month_summary %} {% include 'ping_me/partials/summary_percentage.html' with summary=domain.last_month_summary %} {% if domain.user.email %} {{ domain.user.email }} {% else %} {{ domain.user.username }} (no email) {% endif %}
{% else %}

You have no domain to show.

{% endif %} {% if user.is_authenticated and user.is_staff %}
Add a domain
{% endif %} PK!9V:django_ping_me/templates/ping_me/partials/delete_form.html
{% csrf_token %}
PK!~<<;django_ping_me/templates/ping_me/partials/delete_popup.html PK!c8django_ping_me/templates/ping_me/partials/edit_form.html
{% csrf_token %}
PK!bG339django_ping_me/templates/ping_me/partials/edit_popup.html PK!HaaAdjango_ping_me/templates/ping_me/partials/summary_percentage.html {% if summary %} {% if summary.percentage <= 50 %} {% elif summary.percentage <= 90 %} {% elif summary.percentage <= 99 %} {% else %} {% endif %} {{ summary.percentage|floatformat }} % {% else %} N/A {% endif %}PK!'<<django_ping_me/tests.pyfrom django.test import TestCase # Create your tests here. PK!f django_ping_me/urls.pyfrom django.urls import path from .views import ListDomainView, PartialEditView, PartialDeleteView urlpatterns = [ path(r'', ListDomainView.as_view(), name='index'), path(r'ajax/edit-popup/', view=PartialEditView.as_view(), name='ajax-edit-popup'), path(r'ajax/delete-popup/', view=PartialDeleteView.as_view(), name='ajax-delete-popup'), ] PK!3||django_ping_me/views.pyfrom aloha.django import AlohaView from django.core.exceptions import ObjectDoesNotExist from django.forms import ModelForm, TextInput from django.urls import reverse from django.http import HttpResponseRedirect from django_ping_me.models import Domain import datetime import logging logger = logging.getLogger(__name__) class DomainForm(ModelForm): class Meta: model = Domain fields = ['url', 'description'] widgets = { 'description': TextInput(attrs={'style': 'width: 100%;'}) } class PingMeView(AlohaView): def get_domain(self, domain_pk): """Récupère le domaine dont la clé est passée en paramètre""" try: domain = Domain.objects.get(id=domain_pk) return domain except Domain.DoesNotExist: return None def get_domain_list(self): """Récupère la liste des domaines affectés à l'utilisateur courant""" if not self.request.user or not self.request.user.is_authenticated: return [] if self.request.user.is_staff: domains = Domain.objects.all() else: domains = Domain.objects.filter(user=self.request.user) return domains.order_by('url') def get_edit_form(self, domain_pk): """Récupère le formulaire d'édition de domaine""" if domain_pk: domain = self.get_domain(domain_pk) if domain: return DomainForm(self.request.POST or None, instance=domain) return DomainForm(self.request.POST or None) class ListDomainView(PingMeView): title = 'Liste des domaines' template_name = 'ping_me/domain_list.html' def get(self, request, *args, **kwargs): """Retourne la liste des domaines enregistrés""" context = self.get_context_data() context['domain_list'] = self.get_domain_list() context['current_month'] = datetime.date.today().replace(day=1) last_month = datetime.date.today().replace(day=1) last_month = last_month - datetime.timedelta(days=1) last_month = last_month.replace(day=1) context['last_month'] = last_month return self.render_to_response(context) def post(self, request, *args, **kwargs): """Traitement des différentes actions""" # Traitement de l'enregistrement de domaine if request.POST.get('btnSaveDomain') is not None: self.edit_domain() return HttpResponseRedirect(reverse('ping_me:index')) # Traitement de la suppression de domaine if request.POST.get('btnDeleteDomain', None) is not None: self.delete_domain() return HttpResponseRedirect(reverse('ping_me:index')) # Traitement global du parent si aucun traitement n'a été réalisé return super().post(request, *args, **kwargs) def edit_domain(self): """Édite un domaine à partir des données POST""" domain_pk = self.request.POST.get('hdnDomainId', 0) form = self.get_edit_form(domain_pk) if form.is_valid(): try: if not form.instance.user: form.instance.user = self.request.user except ObjectDoesNotExist: form.instance.user = self.request.user form.save() return True return False def delete_domain(self): """Supprime un domaine à partir des données POST""" domain_pk = int(self.request.POST.get('hdnDomainId', 0)) domain = self.get_domain(domain_pk) if not domain: return False logger.info("Suppression du domaine {}".format(domain.url)) domain.delete() return True class PartialEditView(PingMeView): template_name = 'ping_me/partials/edit_form.html' def get(self, request, *args, **kwargs): """Retourne le formulaire d'édition d'un domaine""" domain_pk = kwargs.get('domain_pk', 0) form = self.get_edit_form(domain_pk) context = self.get_context_data() context['form'] = form context['is_edit'] = form.instance.id is not None return self.render_to_response(context) class PartialDeleteView(PingMeView): template_name = 'ping_me/partials/delete_form.html' def get(self, request, *args, **kwargs): """Retourne le formulaire de suppression d'un domaine""" domain_pk = kwargs.get('domain_pk', 0) domain = self.get_domain(domain_pk) if not domain: raise ValueError("Impossible de trouver le domaine") context = self.get_context_data() context['domain'] = domain return self.render_to_response(context) PK!V&django_ping_me-0.0.4.dist-info/LICENSE DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004 Copyright (C) 2004 Sam Hocevar Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. You just DO WHAT THE FUCK YOU WANT TO. PK!HlŃTT$django_ping_me-0.0.4.dist-info/WHEEL A н#J@Z|Jmqvh&#hڭw!Ѭ"J˫( } %PK!HƲ'django_ping_me-0.0.4.dist-info/METADATAU]s7}ׯ?}Ut.睂ŃEWvWEܺO)ks R*0ҹ\5ct螧٭V\;V͑u$G"QYӷNp}a4?޸oAiqow$:ԅ*wFkؠwDvE3\igʂ@}$F ~p pUo'cSd< v4'זh.4^E9eƖ@RD!@ThBtW/8^/xcbN)l+JJZh /_ ۣ_*. BIdjJ~CMd0Fc uUF/Ď9}&Yon ?_jWѓ4Hfgu+ʧ^.¢8'cZ;e -v}Ar,*n^ ]wэ\䉤qېsc닭*3Ziq#Zn7C/3B h,7 Ʈ5MQ;<_Ć (X$i =ɣ:Nlsdn"T̆:1& 7g^̕q;ol"opM~71%-9<7F n8UY8U݆lnW=^-:ni|&=LNOͼ1o[@^Lwo~,K:>OqQZGRٕ|yz ӴYZyG}ƸG(=6iˈюy }ҹ*EB%?37Ex=tI "9N>Qñ?=s 'PK!django_ping_me/__init__.pyPK! 8django_ping_me/admin.pyPK!xXXdjango_ping_me/apps.pyPK!`9 9 django_ping_me/cron.pyPK!ff) django_ping_me/migrations/0001_initial.pyPK!%django_ping_me/migrations/__init__.pyPK!'i$ django_ping_me/models.pyPK!es  1django_ping_me/templates/ping_me/domain_list.htmlPK!9V:**django_ping_me/templates/ping_me/partials/delete_form.htmlPK!~<<;U-django_ping_me/templates/ping_me/partials/delete_popup.htmlPK!c8/django_ping_me/templates/ping_me/partials/edit_form.htmlPK!bG3393django_ping_me/templates/ping_me/partials/edit_popup.htmlPK!HaaA]6django_ping_me/templates/ping_me/partials/summary_percentage.htmlPK!'<<9django_ping_me/tests.pyPK!f 9django_ping_me/urls.pyPK!3||e;django_ping_me/views.pyPK!V&Ndjango_ping_me-0.0.4.dist-info/LICENSEPK!HlŃTT$>Pdjango_ping_me-0.0.4.dist-info/WHEELPK!HƲ'Pdjango_ping_me-0.0.4.dist-info/METADATAPK!Hg@%Tdjango_ping_me-0.0.4.dist-info/RECORDPKX