Élie Bouttier 6 years ago
parent
commit
b206ba74c0

+ 2 - 2
accounts/templates/accounts/profile.html

@@ -1,8 +1,8 @@
 {% extends 'base.html' %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
-{% block profiletab %} class="active"{% endblock %}
+{% block profiletab %} active{% endblock %}
 
 {% block content %}
 <div class="panel panel-primary">

+ 6 - 7
accounts/templates/registration/base.html

@@ -9,15 +9,14 @@
 
 {% block body %}
 
-<div class="container">
-    <div class="jumbotron">
+    <main role="main" class="container">
+      <div class="jumbotron">
         <h1>Espace adhérent·e</h1>
         <h2>Association tetaneutral.net</h2>
-    </div>
-
-    <div class="col-md-6 col-md-offset-3">
+        <p>
 {% block content %}{% endblock %}
-    </div>
-</div>
+        </p>
+      </div>
+    </main>
 
 {% endblock %}

+ 1 - 1
accounts/templates/registration/login.html

@@ -1,6 +1,6 @@
 {% extends 'registration/base.html' %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
 {% block content %}
         <p>Vous pouvez vous connecter avec votre <b>nom d’utilisateur</b>, votre <b>adresse e-mail</b> ou votre <b>numéro d’adhérent·e</b>.</p>

+ 1 - 1
accounts/templates/registration/password_reset_complete.html

@@ -1,6 +1,6 @@
 {% extends 'registration/base.html' %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
 {% block content %}
         <p>Votre mot de passe a été réinitialisé, vous pouvez à présent vous <a href="{% url 'login' %}">connecter</a>.<p/>

+ 1 - 1
accounts/templates/registration/password_reset_confirm.html

@@ -1,6 +1,6 @@
 {% extends 'registration/base.html' %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
 {% block content %}
 {% if validlink %}

+ 1 - 1
accounts/templates/registration/password_reset_form.html

@@ -1,6 +1,6 @@
 {% extends 'registration/base.html' %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
 {% block content %}
         <p>Pour réinitialiser votre mot de passe, veuillez saisir votre adresse e-email.</p>

+ 1 - 1
adhesions/templates/adhesions/user.html

@@ -1,6 +1,6 @@
 {% extends 'adhesions/adherent.html' %}
 
-{% block usertab %} class="active"{% endblock %}
+{% block usertab %} active{% endblock %}
 
 {% block header %}
 <h1>Bienvenue <b>{{ adherent.profile }}</b> !</h1>

+ 1 - 1
banking/templates/banking/payment_detail.html

@@ -5,7 +5,7 @@
 <li class="active">{{ payment.payment_type_verbose }} {{ payment.reason }}</li>
 {% endblock %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
 {% block content %}
 <div class="panel panel-{{ payment.active|yesno:"success,danger,primary"}}">

+ 1 - 1
banking/templates/banking/payment_list.html

@@ -4,7 +4,7 @@
 <li class="active">Paiements</li>
 {% endblock %}
 
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 
 {% block content %}
 <div class="panel panel-primary">

+ 109 - 0
djadhere/admin.py

@@ -0,0 +1,109 @@
+from django.contrib import admin
+from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
+from django.utils.html import escape
+from django.urls import reverse, NoReverseMatch
+from django.contrib.auth.models import User
+
+action_names = {
+    ADDITION: 'Addition',
+    CHANGE:   'Change',
+    DELETION: 'Deletion',
+}
+
+class FilterBase(admin.SimpleListFilter):
+    def queryset(self, request, queryset):
+        if self.value():
+            dictionary = dict(((self.parameter_name, self.value()),))
+            return queryset.filter(**dictionary)
+
+class ActionFilter(FilterBase):
+    title = 'action'
+    parameter_name = 'action_flag'
+    def lookups(self, request, model_admin):
+        return action_names.items()
+
+
+class UserFilter(FilterBase):
+    """Use this filter to only show current users, who appear in the log."""
+    title = 'user'
+    parameter_name = 'user_id'
+    def lookups(self, request, model_admin):
+        return tuple((u.id, u.username)
+            for u in User.objects.filter(pk__in =
+                LogEntry.objects.values_list('user_id').distinct())
+        )
+
+class AdminFilter(UserFilter):
+    """Use this filter to only show current Superusers."""
+    title = 'admin'
+    def lookups(self, request, model_admin):
+        return tuple((u.id, u.username) for u in User.objects.filter(is_superuser=True))
+
+class StaffFilter(UserFilter):
+    """Use this filter to only show current Staff members."""
+    title = 'staff'
+    def lookups(self, request, model_admin):
+        return tuple((u.id, u.username) for u in User.objects.filter(is_staff=True))
+
+
+class LogEntryAdmin(admin.ModelAdmin):
+
+    date_hierarchy = 'action_time'
+
+    readonly_fields = [field.name for field in LogEntry._meta.get_fields()]
+
+    list_filter = [
+        UserFilter,
+        ActionFilter,
+        'content_type',
+        # 'user',
+    ]
+
+    search_fields = [
+        'object_repr',
+        'change_message'
+    ]
+
+
+    list_display = [
+        'action_time',
+        'user',
+        'content_type',
+        'get_object_link',
+        'action_flag',
+        'action_description',
+        'change_message',
+    ]
+
+    def has_add_permission(self, request):
+        return False
+
+    def has_change_permission(self, request, obj=None):
+        return request.user.is_superuser and request.method != 'POST'
+
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+    def get_object_link(self, obj):
+        ct = obj.content_type
+        repr_ = escape(obj.object_repr)
+        try:
+            href = reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=[obj.object_id])
+            link = u'<a href="%s">%s</a>' % (href, repr_)
+        except (NoReverseMatch, AttributeError):
+            link = repr_
+        return link if obj.action_flag != DELETION else repr_
+    get_object_link.allow_tags = True
+    get_object_link.admin_order_field = 'object_repr'
+    get_object_link.short_description = u'object'
+
+    def queryset(self, request):
+        return super(LogEntryAdmin, self).queryset(request) \
+            .prefetch_related('content_type')
+
+    def action_description(self, obj):
+        return action_names[obj.action_flag]
+    action_description.short_description = 'Action'
+
+
+admin.site.register(LogEntry, LogEntryAdmin)

+ 19 - 0
djadhere/routers.py

@@ -0,0 +1,19 @@
+class PonyAirRouter:
+    def db_for_read(self, model, **hints):
+        if model._meta.app_label == 'ponyair':
+            return 'ponyair'
+        return None
+
+    def db_for_write(self, model, **hints):
+        if model._meta.app_label == 'ponyair':
+            return 'ponyair'
+        return None
+
+    def allow_relation(self, obj1, obj2, **hints):
+        return (obj1._meta.app_label == 'ponyair' and obj2._meta.app_label == 'ponyair') \
+                or (obj1._meta.app_label != 'ponyair' and obj2._meta.app_label != 'ponyair')
+
+    def allow_migrate(self, db, app_label, model_name=None, **hints):
+        if db == 'ponyair' and app_label == 'ponyair':
+            return True
+        return False

+ 1 - 1
djadhere/settings.py

@@ -42,7 +42,7 @@ INSTALLED_APPS = [
     'stocking',
     'djadhere',
 
-    'bootstrap3',
+    'bootstrap4',
     'leaflet',
     'mptt',
 

+ 11 - 17
djadhere/templates/_base.html

@@ -1,32 +1,26 @@
-{% load staticfiles bootstrap3 %}
+{% load staticfiles bootstrap4 %}
 <!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
 
-    {% comment %}<link rel="icon" href="{% static 'favicon.ico' %}">{% endcomment %}
-
     <title>{% block title %}tetaneutral.net{% endblock %}</title>
 
+    {% block css %}
     {% bootstrap_css %}
-    {% block css %}{% endblock %}
-
-    {% block js %}{% endblock %}
+    {% endblock %}
 
-    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
-    <!--[if lt IE 9]>
-      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
-      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
-    <![endif]-->
+    {% block js %}
+    {% comment %}{% bootstrap_javascript %}{% endcomment %}
+    {% endblock %}
   </head>
-  <body>
 
-{% block body %}{% endblock %}
+  <body>
+    {% block body %}
+    {% endblock %}
 
-    <script src="{% bootstrap_jquery_url %}"></script>
-    {% bootstrap_javascript %}
-    {% block js_end %}{% endblock %}
+    {% block js_end %}
+    {% endblock %}
   </body>
 </html>

+ 1 - 1
djadhere/templates/_form.html

@@ -1,4 +1,4 @@
-{% load bootstrap3 %}
+{% load bootstrap4 %}
 <form action="" method="post" class="form-horizontal"{% if multipart %} enctype=multipart/form-data{% endif %}>
   {% csrf_token %}
   {% block beforeform %}{% endblock %}

+ 54 - 46
djadhere/templates/base.html

@@ -1,5 +1,5 @@
 {% extends '_base.html' %}
-{% load bootstrap3 staticfiles %} 
+{% load bootstrap4 staticfiles %} 
 
 {% block css %}
 {{ block.super }}
@@ -14,59 +14,67 @@
 {% endblock %}
 
 {% block body %}
-    <nav class="navbar navbar-default" role="navigation">
-      <div class="container">
-        <div class="navbar-header">
-          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
-            <span class="sr-only">Toggle navigation</span>
-            <span class="icon-bar"></span>
-            <span class="icon-bar"></span>
-            <span class="icon-bar"></span>
-          </button>
-          <span class="navbar-brand">tetaneutral.net</span>
-        </div>
-        <div id="navbar" class="navbar-collapse collapse">
-          <ul class="nav navbar-nav">
-            <li{% block usertab %}{% endblock %}><a href="{% url 'adhesion-detail' %}"><span class="glyphicon glyphicon-heart-empty"></span>&nbsp;Mon adhésion</a></li>
-            {% for corp in request.corporations %}
-            {% if forloop.first %}
-            <li class="dropdown{% block corptab %}{% endblock %}">
-                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-                    <span class="glyphicon glyphicon-globe"></span>&nbsp;Mes asso <span class="caret"></span>
-                </a>
-                <ul class="dropdown-menu">
-            {% endif %}
-                    <li><a href="{% url 'corporation-detail' corp.object.pk %}">{{ corp.object.social_reason }}</a></li>
-            {% if forloop.last %}
-                </ul>
-            </li>
-            {% endif %}
-            {% endfor %}
-          </ul>
-          <ul class="nav navbar-nav navbar-right">
-            {% if request.user.is_staff %}
-            <li><a href="{% url 'admin:index' %}"><span class="glyphicon glyphicon-dashboard"></span>&nbsp;Administration</a></li>
-            {% endif %}
-            <li{% block profiletab %}{% endblock %}><a href="{% url 'profile' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;Profil</a></li>
-            <li><a href="{% url 'logout' %}" data-toggle="tooltip" data-placement="bottom" title="Logout"><span class="glyphicon glyphicon-log-out"></span></a></li>
-          </ul>
-        </div>
+    <nav class="navbar navbar-light bg-light">
+      <a class="navbar-brand" href="#">tetaneutral.net</a>
+      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar" aria-label="Toggle navigation">
+        <span class="navbar-toggler-icon"></span>
+      </button>
+      <div id="navbar" class="navbar-collapse collapse">
+        <ul class="navbar-nav mr-auto mt-2 mt-lg-0">
+          <li class="nav-item{% block usertab %}{% endblock %}">
+            <a class="nav-link" href="{% url 'adhesion-detail' %}">
+              <span class="glyphicon glyphicon-heart-empty"></span>&nbsp;Mon adhésion
+            </a>
+          </li>
+          {% for corp in request.corporations %}
+          {% if forloop.first %}
+          <li class="nav-item dropdown{% block corptab %}{% endblock %}">
+            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+              <span class="glyphicon glyphicon-globe"></span>&nbsp;Mes asso <span class="caret"></span>
+            </a>
+            <div class="dropdown-menu" aria-labelledby="navbarDropdown">
+          {% endif %}
+              <a class="dropdown-item" href="{% url 'corporation-detail' corp.object.pk %}">{{ corp.object.social_reason }}</a>
+          {% if forloop.last %}
+            </div>
+          </li>
+          {% endif %}
+          {% endfor %}
+        </ul>
+        <ul class="navbar-nav mt-2 mt-lg-0">
+          {% if request.user.is_staff %}
+          <li class="nav-item">
+            <a class="nav-link" href="{% url 'admin:index' %}"><span class="glyphicon glyphicon-dashboard"></span>&nbsp;Administration</a>
+          </li>
+          {% endif %}
+          <li class="nav-item"{% block profiletab %}{% endblock %}>
+            <a class="nav-link" href="{% url 'profile' %}">
+              <span class="glyphicon glyphicon-user"></span>&nbsp;Profil
+            </a>
+          </li>
+          <li class="nav-item">
+            <a class="nav-link" href="{% url 'logout' %}" data-toggle="tooltip" data-placement="bottom" title="Logout">
+              <span class="glyphicon glyphicon-log-out"></span>
+            </a>
+          </li>
+        </ul>
       </div>
     </nav>
 
-
 {% block container %}
-	<div class="container">
+	<main role="main" class="container">
       {% block breadcrumbol %}{% endblock %}
       {% bootstrap_messages %}
 {% block content %}{% endblock %}
-{% block pagefooter %}
-      <hr>
-      <footer>
-        <p class="text-muted">Propulsé par <a href="https://code.ffdn.org/tetaneutral.net/djadhere">djadhere</a></p>
-      </footer>
+    </main>
 {% endblock %}
-	</div> <!-- container-fluid -->
+
+{% block pagefooter %}
+    <footer class="footer">
+      <div class="container">
+        <span class="text-muted">Propulsé par <a href="https://code.ffdn.org/tetaneutral.net/djadhere">djadhere</a></span>
+      </div>
+    </footer>
 {% endblock %}
 
 {% endblock %}

+ 1 - 0
ponyair

@@ -0,0 +1 @@
+../django-ponyair/ponyair/

+ 2 - 2
requirements.txt

@@ -1,5 +1,5 @@
-django<2.1
-django-bootstrap3
+django<3
+django-bootstrap4
 django-leaflet
 django-geojson
 django-mptt

+ 108 - 0
services/management/commands/disc-air.py

@@ -0,0 +1,108 @@
+from django.core.management.base import BaseCommand
+from django.contrib.gis.geos import Point
+
+from services.models import IPResource, Antenna, AntennaAllocation, Tunnel
+from djadhere.utils import get_active_filter
+
+import os, json, yaml
+from ipaddress import ip_network
+
+
+class Command(BaseCommand):
+    help = 'Import des informatios du cache de disc-air'
+
+    def add_arguments(self, parser):
+        parser.add_argument('disc-air', help='Cache disc-air')
+
+    def handle(self, *args, **options):
+        cache = options['disc-air']
+        for tunnel in Tunnel.objects.all():
+            if not os.path.isdir(os.path.join(cache, tunnel.name)):
+                continue
+            gps_file = os.path.join(cache, '..', 'gps-%s.yaml' % tunnel.name)
+            if os.path.isfile(gps_file):
+                with open(gps_file) as fd:
+                    gps = yaml.load(fd)
+            for network in tunnel.networks.all():
+                for ip in network.ipresource_set.all():
+                    status_file = os.path.join(cache, tunnel.name, ip.ip, 'fd694727ed4810b9d242c45906ea9417')
+                    if not os.path.isfile(status_file):
+                        continue
+                    with open(status_file) as fd:
+                        try:
+                            status = json.load(fd)
+                        except Exception as e:
+                            continue
+                    try:
+                        hostname = status['host']['hostname']
+                        mode = status['wireless']['mode']
+                        ssid = status['wireless']['essid']
+                        mac = status['wireless']['apmac']
+                    except KeyError:
+                        continue
+                    mode = 1 if mode == 'ap' else 2
+                    if ip.in_use:
+                        update = False
+                        antenna = ip.allocations.filter(get_active_filter()).first().antenna # peut faire mieux …
+                        if antenna.label:
+                            if antenna.label != hostname:
+                                #print(ip, ", djadhere:", antenna.label, ", disc-air:", hostname)
+                                continue
+                        else:
+                            #print("update hostname")
+                            antenna.label = hostname
+                            update = True
+                        if antenna.mode:
+                            if antenna.mode != mode:
+                                #print(ip, ", djadhere:", antenna.mode, ", disc-air:", mode)
+                                continue
+                        else:
+                            #print("update mode")
+                            antenna.mode = mode
+                            update = True
+                        if antenna.ssid:
+                            if antenna.ssid != ssid:
+                                #print(ip, ", djadhere:", antenna.ssid, ", disc-air:", ssid)
+                                continue
+                        else:
+                            #print("update ssid")
+                            antenna.ssid = ssid
+                            update = True
+                        if antenna.mac:
+                            if antenna.mac != mac:
+                                #print(ip, ", djadhere:", antenna.mac, ", disc-air:", mac)
+                                continue
+                        else:
+                            #print("update mac")
+                            antenna.mac = mac
+                            update = True
+                        if ip.ip in gps:
+                            coords = gps[ip.ip]
+                            if len(coords) == 3:
+                                latitude, longitude, orientation = coords
+                            else:
+                                latitude, longitude = coords
+                                orientation = None
+                            assert(41 < latitude and latitude < 45)
+                            assert(1 < longitude and longitude < 2)
+                            position = Point(longitude, latitude, srid=4326)
+                            if antenna.position:
+                                if antenna.position != position:
+                                    #print(ip, ", djadhere:", antenna.position, ", disc-air:", position)
+                                    continue
+                            else:
+                                antenna.position = position
+                                update = True
+                            if antenna.orientation:
+                                if antenna.orientation != orientation:
+                                    #print(antenna.orientation, orientation)
+                                    continue
+                            elif orientation:
+                                antenna.orientation = orientation
+                                update = True
+                        if update:
+                            antenna.save()
+                    else:
+                        #print("created")
+                        antenna = Antenna.objects.create(label=hostname, mode=mode, ssid=ssid, mac=mac)
+                        AntennaAllocation.objects.create(resource=ip, antenna=antenna)

+ 1 - 1
services/templates/services/service_detail.html

@@ -1,6 +1,6 @@
 {% extends 'base.html' %}
 
-{% load bootstrap3 humanize %}
+{% load bootstrap4 humanize %}
 
 {% block usertab %}{% if service.adhesion == request.user.adhesion %} class="active"{% endif %}{% endblock %}
 {% block corptab %}{% if service.adhesion != request.user.adhesion %} active{% endif %}{% endblock %}

+ 35 - 0
stocking/migrations/0003_auto_20180531_1457.py

@@ -0,0 +1,35 @@
+# Generated by Django 2.0.2 on 2018-05-31 12:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+import mptt.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('stocking', '0002_auto_20170627_1335'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='category',
+            name='name',
+            field=models.CharField(max_length=64, verbose_name='nom'),
+        ),
+        migrations.AlterField(
+            model_name='equipment',
+            name='category',
+            field=mptt.fields.TreeForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipments', to='stocking.Category', verbose_name='catégorie'),
+        ),
+        migrations.AlterField(
+            model_name='equipment',
+            name='location',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipments', to='stocking.Location', verbose_name='emplacement'),
+        ),
+        migrations.AlterField(
+            model_name='location',
+            name='name',
+            field=models.CharField(max_length=64, verbose_name='nom'),
+        ),
+    ]