-
+
diff --git a/report_manager/owner_report_gen.py b/report_manager/owner_report_gen.py
new file mode 100644
index 0000000..57100be
--- /dev/null
+++ b/report_manager/owner_report_gen.py
@@ -0,0 +1,51 @@
+import datetime
+from map.models import Place
+from event_manager.models import Event
+
+
+# Validating if date is valid
+def is_valid_format(date):
+ passed = False
+ try:
+ start_year = datetime.datetime.strptime(date, "%Y/%m/%d").year
+ passed = True
+ except ValueError:
+ passed = False
+
+ return passed
+
+
+# Getting the place based on its pk
+def get_place(pk):
+ return Place.objects.get(pk=pk)
+
+
+# Getting events in the place based on a date range
+def get_events_in_place(place, start_date, end_date):
+ try:
+ start_year = datetime.datetime.strptime(start_date, "%Y/%m/%d").year
+ end_year = datetime.datetime.strptime(end_date, "%Y/%m/%d").year
+
+ start_month = datetime.datetime.strptime(start_date, "%Y/%m/%d").month
+ end_month = datetime.datetime.strptime(end_date, "%Y/%m/%d").month
+
+ start_day = datetime.datetime.strptime(start_date, "%Y/%m/%d").day
+ end_day = datetime.datetime.strptime(start_date, "%Y/%m/%d").day
+
+ events = Event.objects.filter(sensor__floor__place=place)\
+ .filter(timestamp__gt=datetime.date(start_year, start_month, start_day),
+ timestamp__lt=datetime.date(end_year, end_month, end_day))
+ return events
+
+ except ValueError:
+ return None
+
+
+# Verifying if the end date is greater than the start date
+def is_end_date_greater(start_date, end_date):
+
+ if datetime.datetime.strptime(start_date, "%Y/%m/%d") > \
+ datetime.datetime.strptime(end_date, "%Y/%m/%d"):
+ return False
+ else:
+ return True
\ No newline at end of file
diff --git a/report_manager/templates/events_in_date_range.html b/report_manager/templates/events_in_date_range.html
new file mode 100644
index 0000000..8abad46
--- /dev/null
+++ b/report_manager/templates/events_in_date_range.html
@@ -0,0 +1,51 @@
+{% extends "base.html"%}
+{% load static %}
+
+{% block title %}
+ - Events in a date range
+{% endblock %}
+
+{% block navbar %}
+
My places
+
About Smart Home
+
About Domotina
+{% endblock navbar %}
+
+{% block content %}
+
+
Report of handled events in your place {{place.name}}
+ {% if events %}
+
These are handled events in your place between {{start_date}} and {{end_date}}.
+ {% endif %}
+
+
+ {% if events %}
+
+
+
+ Date |
+ Time |
+ Asset |
+ Description |
+
+
+
+ {% for event in events %}
+
+ {{ event.timestamp.date }} |
+ {{ event.timestamp.time }} |
+ {{ event.sensor }} |
+ Status changed to {{ event }} |
+
+ {% endfor %}
+
+
+ {% else %}
+
+ - There are no handled events between {{start_date}} and {{end_date}}.
+
+ {% endif %}
+
+
View events in another date.
+
+{% endblock %}
\ No newline at end of file
diff --git a/report_manager/templates/events_in_date_range_pdf.html b/report_manager/templates/events_in_date_range_pdf.html
new file mode 100644
index 0000000..e90ffa8
--- /dev/null
+++ b/report_manager/templates/events_in_date_range_pdf.html
@@ -0,0 +1,66 @@
+
+{% load static %}
+
+
+
+
Handled events in {{place.name}}
+
+
+
+
+
+
Report of handled events in your place {{place.name}}
+
These are handled events in your place between {{start_date}} and {{end_date}}.
+
+
+
+
+ Date |
+ Time |
+ Asset |
+ Description |
+
+
+
+
+ {% for event in events %}
+
+ {{ event.timestamp.date }} |
+ {{ event.timestamp.time }} |
+ {{ event.sensor }} |
+ Status changed to {{ event }} |
+
+ {% endfor %}
+
+
+
+
\ No newline at end of file
diff --git a/report_manager/templates/form.html b/report_manager/templates/form.html
new file mode 100644
index 0000000..c545337
--- /dev/null
+++ b/report_manager/templates/form.html
@@ -0,0 +1,66 @@
+{% extends "base.html"%}
+{% load static %}
+
+{% block title %}
+ - Events in a date range
+{% endblock %}
+
+{% block navbar %}
+
My places
+
About Smart Home
+
About Domotina
+{% endblock navbar %}
+
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/report_manager/tests.py b/report_manager/tests.py
index b7252be..d4feb44 100644
--- a/report_manager/tests.py
+++ b/report_manager/tests.py
@@ -1,8 +1,116 @@
# -*- encoding: utf-8 -*-
import unittest
-from report_manager import central_report_gen
+from report_manager import central_report_gen, owner_report_gen as owner_rg
from map.models import Place
+
+class OwnerReportTests(unittest.TestCase):
+
+ def test_us011_scenario1(self):
+ ''' Dado que soy un propietario
+ Cuando seleccione un rango de fechas en el que no hay registros de eventos ocurridos en alguno de mis inmuebles
+ Entonces no debe generar un reporte y debe informar que no hay registro disponibles según los datos ingresados'''
+
+ self.start_date = "2013/01/01"
+ self.end_date = "2013/01/10"
+ self.place_pk = 5
+
+ # Validating if start_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.start_date))
+
+ # Validating if end_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.end_date))
+
+ # Getting the place based on its pk
+ place = owner_rg.get_place(self.place_pk)
+
+ # Getting events in the place based on a date range
+ events = owner_rg.get_events_in_place(place, self.start_date, self.end_date)
+
+ # No events found!
+ if not events or len(events) == 0:
+ self.assertTrue(True)
+ else:
+ self.assertTrue(False)
+
+ def test_us011_scenario2(self):
+ ''' Dado que soy un propietario
+ Cuando seleccione un rango de fechas y hay eventos para el inmueble actual.
+ Entonces se genera un reporte con los eventos ocurridos en mi inmueble detallando principalmente el activo afectado,
+ cambio de estado y el momento en el que ocurrio el evento.'''
+
+ self.start_date = "2013/01/01"
+ self.end_date = "2015/12/31"
+ self.place_pk = 5
+
+ # Validating if start_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.start_date))
+
+ # Validating if end_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.end_date))
+
+ # Getting the place based on its pk
+ place = owner_rg.get_place(self.place_pk)
+
+ # Getting events in the place based on a date range
+ events = owner_rg.get_events_in_place(place, self.start_date, self.end_date)
+
+ # Events do found!
+ if events and len(events) > 0:
+ self.assertTrue(True)
+ else:
+ self.assertTrue(False)
+
+ def test_us011_scenario3(self):
+ ''' Dado que soy un propietario
+ Cuando seleccione unicamente la fecha inicial del rango de fechas y pulse la opción de generar reporte de eventos
+ Entonces no se genera el reporte y se informa al usuario que hace falta ingresar la fecha final y el inmueble para
+ la generacion.'''
+
+ self.start_date = "2015/01/01"
+ self.end_date = ""
+
+ # Validating if start_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.start_date))
+
+ # Validating if end_date is valid
+ self.assertFalse(owner_rg.is_valid_format(self.end_date))
+
+ def test_us011_scenario4(self):
+ ''' Dado que soy un propietario
+ Cuando seleccione unicamente la fecha final del rango de fechas y pulse la opción de generar reporte de eventos
+ Entonces no se genera el reporte y se informa al usuario que hace falta ingresar la fecha inicial y el inmueble
+ para la generacion.'''
+
+ self.start_date = ""
+ self.end_date = "2015/01/20"
+
+ # Validating if start_date is valid
+ self.assertFalse(owner_rg.is_valid_format(self.start_date))
+
+ # Validating if end_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.end_date))
+
+ def test_us011_scenario5(self):
+ ''' Dado que soy un propietario
+ Cuando ingreso el rango de fechas y el inmueble, pero la fecha final es menor que la fecha inicial
+ Entonces no se genera el reporte y se informa al usuario que la fecha final es menor que la fecha inicial y
+ por ende no se puede especificar el rango de fechas para realizar la generacion.'''
+
+ self.start_date = "2015/01/01"
+ self.end_date = "2015/01/20"
+
+ # Validating if start_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.start_date))
+
+ # Validating if end_date is valid
+ self.assertTrue(owner_rg.is_valid_format(self.end_date))
+
+ # Validating if end date is greater than start date
+ self.assertTrue(owner_rg.is_end_date_greater(self.start_date, self.end_date))
+
+
+
class CentralReportTests(unittest.TestCase):
"HU: Generar reporte mensual de incidencias. Narrativa: Como usuario de la central, quiero generar el reporte de incidencias ocurridas en el mes de los inmuebles que administro, organizados por inmueble y cantidad de incidencias generadas."
diff --git a/report_manager/urls.py b/report_manager/urls.py
new file mode 100644
index 0000000..780b0aa
--- /dev/null
+++ b/report_manager/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls import patterns, include, url
+
+
+urlpatterns = patterns('report_manager.views',
+ url(r'^(?P
\d+)/$', 'home', name='report_home'),
+ url(r'^(?P\d+)/events/$', 'events_in_date_range', name='report_events_in_date_range'),
+)
diff --git a/report_manager/views.py b/report_manager/views.py
index 91ea44a..cc1d43e 100644
--- a/report_manager/views.py
+++ b/report_manager/views.py
@@ -1,3 +1,128 @@
-from django.shortcuts import render
+from django.shortcuts import render, redirect, get_object_or_404
+from django.contrib.messages import error
+from xhtml2pdf import pisa
+from django.http import HttpResponse
+from django.template import RequestContext
+from django.template.loader import render_to_string
-# Create your views here.
+from map.models import Place
+from event_manager.models import Event
+
+import datetime, calendar
+import cStringIO as StringIO
+import cgi
+
+def home(request, place_pk):
+ # Getting the place
+ place = get_object_or_404(Place, pk=place_pk)
+
+ context = {'place': place}
+ return render(request, 'form.html', context)
+
+
+def generate_pdf(html):
+ # Generic function to generate a pdf file
+ result = StringIO.StringIO()
+ pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")), result, link_callback=fetch_resources)
+ if not pdf.err:
+ return HttpResponse(result.getvalue(), content_type='application/pdf')
+ return HttpResponse('PDF document cannot be generated: %s' % cgi.escape(html))
+
+
+def fetch_resources(uri, rel):
+ import os.path
+ from django.conf import settings
+ path = os.path.join(
+ "index/static",
+ uri.replace(settings.STATIC_URL, ""))
+
+ return path
+
+
+def events_in_date_range(request, place_pk):
+ # Scenario 1 is validated displaying a message when have no results!
+ # Scenario 2 is validated displaying all events in a place between a date range! (Ideal scenario)
+ # Scenario 3 and 4 are validated via js, indicating dates are required fields!
+
+ start_year = 0
+ #math.fmod()
+ end_year = 0
+ if request.method == 'POST':
+ # Validating if dates are in valid format!
+ try:
+ start_year = datetime.datetime.strptime(request.POST['start_date'], "%Y/%m/%d").year
+ except ValueError:
+ error(request, "The start date must be with valid format.")
+ return redirect('report_home', place_pk=place_pk)
+
+ try:
+ end_year = datetime.datetime.strptime(request.POST['end_date'], "%Y/%m/%d").year
+ except ValueError:
+ error(request, "The end date must be with valid format.")
+ return redirect('report_home', place_pk=place_pk)
+
+ start_date = request.POST['start_date']
+ end_date = request.POST['end_date']
+ as_pdf = request.POST['output'] != 'html'
+
+ else:
+ return redirect('report_home', place_pk=place_pk)
+
+ # Scenario 5 is validated redirecting to form page and indicating that
+ # end date must be greater than start date
+ if datetime.datetime.strptime(start_date, "%Y/%m/%d") > \
+ datetime.datetime.strptime(end_date, "%Y/%m/%d"):
+ error(request, "The end date must be greater than the start date.")
+ return redirect('report_home', place_pk=place_pk)
+
+
+ if not start_year > 1900:
+ error(request, "The year in the start date must be greater than 1900.")
+ return redirect('report_home', place_pk=place_pk)
+
+ if not end_year > 1900:
+ error(request, "The year in the end date must be greater than 1900.")
+ return redirect('report_home', place_pk=place_pk)
+
+
+ start_month = datetime.datetime.strptime(start_date, "%Y/%m/%d").month
+ end_month = datetime.datetime.strptime(end_date, "%Y/%m/%d").month
+
+ if not start_month in range(1, 13):
+ error(request, "The month in the start date must be between 1 and 12.")
+ return redirect('report_home', place_pk=place_pk)
+
+ if not end_month in range(1, 13):
+ error(request, "The month in the end date must be between 1 and 12.")
+ return redirect('report_home', place_pk=place_pk)
+
+
+ start_day = datetime.datetime.strptime(start_date, "%Y/%m/%d").day
+ end_day = datetime.datetime.strptime(end_date, "%Y/%m/%d").day
+
+ if start_day <= 0 or start_day >= 30:
+ start_day = 1
+
+ if end_day <= 0 or end_day >= 30:
+ end_day = calendar.monthrange(end_year, end_month)[1]
+
+
+ # Getting the place to filter events
+ place = get_object_or_404(Place, pk=place_pk)
+
+ # Filtering events in a place and a in a date range
+ events = Event.objects.filter(sensor__floor__place=place)\
+ .filter(timestamp__gt=datetime.date(start_year, start_month, start_day),
+ timestamp__lt=datetime.date(end_year, end_month, end_day))
+
+ context = {'place': place, 'events': events,
+ 'start_date': start_date, 'end_date': end_date}
+
+ # Generating pdf file
+ if as_pdf and events:
+ html = render_to_string('events_in_date_range_pdf.html', {'pagesize':'A4', 'place': place, 'events': events,
+ 'start_date': start_date, 'end_date': end_date}, context_instance=RequestContext(request))
+ return generate_pdf(html)
+
+ # Displaying report in HTML
+ return render(request, 'events_in_date_range.html', context)
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 0cfc908..e7466f3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,12 @@ django-registration==1.0
django-toolbelt==0.0.1
djangorestframework==3.0.5
gunicorn==19.2.1
+html5lib==0.999
nose==1.3.4
+pisa==3.0.33
psycopg2==2.6
+PyPDF2==1.24
static3==0.5.1
reportlab==3.1.44
+six==1.9.0
+xhtml2pdf==0.0.6