#207 Exposition du matériel déployé ou prêté à une date donnée via une API

Open
cecile wants to merge 8 commits from cecile/export-materiel into FFDN/master

+ 25 - 0
coin/members/migrations/0018_auto_20180818_1855.py

@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import coin.members.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('members', '0017_merge'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='rowlevelpermission',
+            options={'verbose_name': 'permission fine', 'verbose_name_plural': 'permissions fines'},
+        ),
+        migrations.AlterModelManagers(
+            name='member',
+            managers=[
+                ('objects', coin.members.models.MemberManager()),
+            ],
+        ),
+    ]

+ 15 - 0
coin/members/migrations/0019_merge.py

@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('members', '0018_auto_20180819_0211'),
+        ('members', '0018_auto_20180818_1855'),
+    ]
+
+    operations = [
+    ]

+ 2 - 1
hardware_provisioning/admin.py

@@ -135,7 +135,8 @@ class ItemChoiceField(ModelChoiceField):
 
 @admin.register(Loan)
 class LoanAdmin(admin.ModelAdmin):
-    list_display = ('item', 'get_mac_and_serial', 'user', 'loan_date', 'loan_date_end')
+    list_display = ('item', 'get_mac_and_serial', 'user', 
+        'loan_date', 'loan_date_end', 'offer_subscription')
     list_filter = (StatusFilter, BorrowerFilter, 'item__designation')
     search_fields = (
         'item__designation',

+ 23 - 0
hardware_provisioning/migrations/0018_auto_20180818_1855.py

@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hardware_provisioning', '0017_item_deployed'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='item',
+            options={'ordering': ['designation', 'mac_address', 'serial'], 'verbose_name': 'objet'},
+        ),
+        migrations.AddField(
+            model_name='item',
+            name='offer_subscription',
+            field=models.ForeignKey(related_name='offersubscription', verbose_name='Abonnement', blank=True, to='hardware_provisioning.ItemType', null=True),
+        ),
+    ]

+ 19 - 0
hardware_provisioning/migrations/0019_auto_20180818_1930.py

@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hardware_provisioning', '0018_auto_20180818_1855'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='item',
+            name='offer_subscription',
+            field=models.ForeignKey(related_name='items', verbose_name='Abonnement', blank=True, to='offers.OfferSubscription', null=True),
+        ),
+    ]

+ 24 - 0
hardware_provisioning/migrations/0020_auto_20190317_1655.py

@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('offers', '0007_offersubscription_comments'),
+        ('hardware_provisioning', '0019_auto_20180818_1930'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='item',
+            name='offer_subscription',
+        ),
+        migrations.AddField(
+            model_name='loan',
+            name='offer_subscription',
+            field=models.ForeignKey(related_name='loans', verbose_name='Abonnement', blank=True, to='offers.OfferSubscription', null=True),
+        ),
+    ]

+ 15 - 0
hardware_provisioning/migrations/0021_merge.py

@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hardware_provisioning', '0018_auto_20180819_0211'),
+        ('hardware_provisioning', '0020_auto_20190317_1655'),
+    ]
+
+    operations = [
+    ]

+ 32 - 6
hardware_provisioning/models.py

@@ -1,10 +1,15 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import unicode_literals
+
+import datetime
+from datetime import datetime
 from django.db import models
 from django.db.models import Q
 from django.conf import settings
 from django.utils import timezone
+from django.db.models import Count, Q
+from coin.offers.models import OfferSubscription
 
 from .fields import MACAddressField
 
@@ -21,8 +26,8 @@ class ItemType(models.Model):
 
 
 class ItemQuerySet(models.QuerySet):
-    def _get_borrowed_pks(self):
-        return Loan.objects.running().values_list('item', flat=True)
+    def _get_borrowed_pks(self, at_date=None):
+        return Loan.objects.running(at_date).values_list('item', flat=True)
 
     def available(self):
         return self.exclude(
@@ -41,7 +46,21 @@ class ItemQuerySet(models.QuerySet):
             Q(pk__in=self._get_borrowed_pks()) |
             Q(deployed=True))
 
+    def summary_item(self):
+        """ Agregates as a count of items per designation
+        """
+        return self.values('designation').annotate(count_item=Count('designation'))
+
+    def running(self, at_date=None):
+        """ Only item at a given date or deployed
+        """
+        if at_date is None:
+            at_date = timezone.now()
+
+        return self.filter(Q(pk__in=self._get_borrowed_pks(at_date)) |
+                           Q(deployed=True))
 
+    
 class Item(models.Model):
     type = models.ForeignKey(ItemType, verbose_name='type de matériel',
                              related_name='items')
@@ -124,13 +143,18 @@ class Item(models.Model):
 class LoanQuerySet(models.QuerySet):
 
     @staticmethod
-    def _running_filter():
+    def _running_filter(at_date=None):
+        if at_date is None:
+            at_date = timezone.now()
+        else:
+            at_date = timezone.make_aware(datetime.strptime(at_date, "%Y-%m-%d"))
         return (
             models.Q(loan_date_end__gt=timezone.now()) |
-            models.Q(loan_date_end__isnull=True))
+            models.Q(loan_date_end__isnull=True) &
+            models.Q(loan_date__lt=at_date))
 
-    def running(self):
-        return self.filter(self._running_filter())
+    def running(self, at_date=None):
+        return self.filter(self._running_filter(at_date))
 
     def finished(self):
         return self.exclude(self._running_filter())
@@ -145,6 +169,8 @@ class Loan(models.Model):
     loan_date_end = models.DateTimeField(verbose_name='date de fin de prêt',
                                      null=True, blank=True)
     notes = models.TextField(null=True, blank=True)
+    offer_subscription = models.ForeignKey(OfferSubscription, verbose_name='Abonnement',
+                             related_name='loans', blank=True, null=True)
 
     def __unicode__(self):
         return 'prêt de {item} à {user}'.format(

+ 2 - 1
hardware_provisioning/urls.py

@@ -3,7 +3,7 @@
 from __future__ import unicode_literals
 from django.conf.urls import url
 from . import views
-
+from hardware_provisioning.views import item_deployed_count_json
 
 urlpatterns = [
     url(r'^$', views.loan_list, name='loan-list'),
@@ -12,4 +12,5 @@ urlpatterns = [
     url(r'^(?P<pk>[0-9]+)/return$', views.loan_return, name='loan-return'),
     url(r'^(?P<pk>[0-9]+)/transfer$', views.loan_transfer, name='loan-transfer'),
     url(r'^(?P<pk>[0-9]+)$', views.loan_detail, name='loan-detail'),
+    url(r'^api/count$', item_deployed_count_json),
 ]

+ 32 - 1
hardware_provisioning/views.py

@@ -2,18 +2,21 @@
 
 from __future__ import unicode_literals
 
+import datetime
+import json
 
 from django.shortcuts import get_object_or_404, render, redirect
 from django.contrib.auth.decorators import login_required
 from django.contrib import messages
 from django.http import HttpResponseForbidden
+from django.http import JsonResponse, HttpResponseServerError
 from django.core.urlresolvers import reverse
 from django.utils import timezone
+from django.db.models import Q, Count
 
 from .forms import LoanDeclareForm, LoanTransferForm, LoanReturnForm
 from .models import Item, Loan
 
-
 @login_required
 def item_list(request):
     items = Item.objects.all().order_by('storage', 'type', 'designation')
@@ -134,3 +137,31 @@ def loan_list(request):
 @login_required
 def loan_detail(request, pk):
     return render(request, 'hardware_provisioning/detail.html', {})
+
+def __getitem__(self, key):
+    return self.item[key]
+
+@login_required
+def item_deployed_count_json(request):
+    output = []
+
+    # Get date form url, or set default
+    date = request.GET.get('date', datetime.date.today())
+
+    # Validate date type
+    if not isinstance(date, datetime.date):
+        try:
+            datetime.datetime.strptime(date, '%Y-%m-%d')
+        except ValueError, TypeError:
+            return HttpResponseServerError("Incorrect date format, should be YYYY-MM-DD")
+
+    # Get hardware deployed
+    itemsdeployed = list(Item.objects.running(date).summary_item())
+    for item in itemsdeployed:
+        output.append({
+            'Name' : item['designation'],
+            'Count' : item['count_item']
+        })
+
+    # Return JSON
+    return JsonResponse(output, safe=False)