diff --git a/smash/web/api_urls.py b/smash/web/api_urls.py index 6776cbcef22c105d28233688b1ff58581f153aa1..e6f8f746b77ac19011e44a46cee9f7fbe6af5c17 100644 --- a/smash/web/api_urls.py +++ b/smash/web/api_urls.py @@ -15,7 +15,7 @@ Including another URLconf """ from django.conf.urls import url -from web.api_views import worker, location, subject, appointment_type, appointment +from web.api_views import worker, location, subject, appointment_type, appointment, configuration urlpatterns = [ # appointments @@ -24,6 +24,11 @@ urlpatterns = [ # appointment types url(r'^appointment_types$', appointment_type.appointment_types, name='web.api.appointment_types'), + # configuration items + url(r'^configuration_items$', configuration.configuration_items, name='web.api.configuration_items'), + url(r'^configuration_items/update$', configuration.update_configuration_item, + name='web.api.update_configuration_item'), + # subjects data url(r'^cities$', subject.cities, name='web.api.cities'), url(r'^countries$', subject.countries, name='web.api.countries'), diff --git a/smash/web/api_views/configuration.py b/smash/web/api_views/configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..305cbef01bc87a15ddec0ca735c18b63866a3b9f --- /dev/null +++ b/smash/web/api_views/configuration.py @@ -0,0 +1,58 @@ +from django.contrib.auth.decorators import login_required +from django.http import JsonResponse + +from web.models import ConfigurationItem + + +@login_required +def configuration_items(request): + # id of the query from dataTable: https://datatables.net/manual/server-side + draw = int(request.GET.get("draw", "-1")) + + start = int(request.GET.get("start", "0")) + length = int(request.GET.get("length", "10")) + + items = ConfigurationItem.objects.all() + count = items.count() + count_filtered = count + + sliced_items = items[start:(start + length)] + + data = [] + for configuration_item in sliced_items: + data.append({ + "id": configuration_item.id, + "name": configuration_item.name, + "value": configuration_item.value + }) + return JsonResponse({ + "draw": draw, + "recordsTotal": count, + "recordsFiltered": count_filtered, + "data": data + }) + + +@login_required +def update_configuration_item(request): + id = int(request.GET.get("id", "-1")) + value = request.GET.get("value", None) + + if (id is None) or (value is None): + return JsonResponse({ + "status": "error", + "message": "id and value are obligatory" + }) + + items = ConfigurationItem.objects.filter(id=id) + if len(items) == 0: + return JsonResponse({ + "status": "error", + "message": "item with given id doesn't exist" + }) + item = items[0] + item.value = value + item.save() + return JsonResponse({ + "status": "ok", + }) diff --git a/smash/web/migrations/0024_configurationitem.py b/smash/web/migrations/0024_configurationitem.py new file mode 100644 index 0000000000000000000000000000000000000000..ccaf21dc692e80529fdea85edd4191ff591d7108 --- /dev/null +++ b/smash/web/migrations/0024_configurationitem.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2017-04-04 09:43 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0023_auto_20170404_1047'), + ] + + operations = [ + migrations.CreateModel( + name='ConfigurationItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(editable=False, max_length=50, verbose_name=b'Type')), + ('name', models.CharField(editable=False, max_length=50, verbose_name=b'Name')), + ('value', models.CharField(max_length=50, verbose_name=b'Value')), + ], + ), + ] diff --git a/smash/web/models/__init__.py b/smash/web/models/__init__.py index 0f14c5c3518e834baad80edae36fe72c80e15dbe..abc2f3e1918ff632b6b498798ddd362dd3296bd9 100644 --- a/smash/web/models/__init__.py +++ b/smash/web/models/__init__.py @@ -18,6 +18,7 @@ from item import Item from language import Language from subject import Subject from contact_attempt import ContactAttempt +from configuration_item import ConfigurationItem def get_current_year(): @@ -25,4 +26,4 @@ def get_current_year(): __all__ = [FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room, Subject, - Visit, Worker, ContactAttempt] + Visit, Worker, ContactAttempt, ConfigurationItem] diff --git a/smash/web/models/configuration_item.py b/smash/web/models/configuration_item.py new file mode 100644 index 0000000000000000000000000000000000000000..2d982b06aa3f37b9cbd78310b39d3ee61bc3de9b --- /dev/null +++ b/smash/web/models/configuration_item.py @@ -0,0 +1,23 @@ +# coding=utf-8 +from django.db import models + + +class ConfigurationItem(models.Model): + type = models.CharField(max_length=50, + verbose_name='Type', + editable=False + ) + name = models.CharField(max_length=50, + verbose_name='Name', + editable=False + ) + + value = models.CharField(max_length=50, + verbose_name='Value', + ) + + def __str__(self): + return "%s %s" % (self.name, self.value) + + def __unicode__(self): + return "%s %s" % (self.name, self.value) diff --git a/smash/web/templates/configuration/breadcrumb.html b/smash/web/templates/configuration/breadcrumb.html new file mode 100644 index 0000000000000000000000000000000000000000..236d1bc335add0ec4427809a9c3c7e94a6490f26 --- /dev/null +++ b/smash/web/templates/configuration/breadcrumb.html @@ -0,0 +1,4 @@ +<li><a href="{% url 'web.views.appointments' %}"><i class="fa fa-dashboard"></i> Dashboard</a></li> +<li class="active"> + <a href="{% url 'web.views.configuration' %}">Configuration</a> +</li> diff --git a/smash/web/templates/configuration/index.html b/smash/web/templates/configuration/index.html new file mode 100644 index 0000000000000000000000000000000000000000..0b89158255f34e2027b3a624b14db6fb052333f6 --- /dev/null +++ b/smash/web/templates/configuration/index.html @@ -0,0 +1,112 @@ +{% extends "_base.html" %} +{% load static %} + + +{% block styles %} + {{ block.super }} + <!-- DataTables --> + <link rel="stylesheet" href="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.css' %}"> + +{% endblock styles %} + +{% block ui_active_tab %}'configuration'{% endblock ui_active_tab %} +{% block page_header %}Configuration{% endblock page_header %} +{% block page_description %}{% endblock page_description %} + +{% block title %}{{ block.super }} - Configuration{% endblock %} + +{% block breadcrumb %} + {% include "configuration/breadcrumb.html" %} +{% endblock breadcrumb %} + +{% block maincontent %} + + <div> + <div id="message"> + </div> + <table id="table" class="table table-bordered table-striped"> + <thead> + <tr> + <th>Name</th> + <th>Value</th> + <th></th> + </tr> + </thead> + <tbody> + </tbody> + </table> + + <hr/> + + + </div> +{% endblock maincontent %} + +{% block scripts %} + {{ block.super }} + + <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script> + <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script> + <script src="{% static 'AdminLTE/plugins/moment.js/moment.min.js' %}"></script> + + <script> + function update(id) { + var value = $("#value-" + id).val(); + var data = { + "id": id, + "value": value, + "_": Math.random() + }; + $.ajax({ + type: "GET", + url: "{% url 'web.api.update_configuration_item' %}", + data: data, + success: function (response) { + var message; + var classString; + if (response.status == "ok") { + message = "Element modified successfully"; + classString = "alert alert-success" + } else { + message = "Problem with modifying element: " + response.message; + classString = "alert alert-danger" + } + $("#message").html(message); + $("#message").attr('class', classString); + }, + }); + + } + $(function () { + $(function () { + var table = $('#table').DataTable({ + serverSide: true, + processing: true, + ordering: false, + ajax: "{% url 'web.api.configuration_items' %}", + columns: [ + {"data": "name"}, + {"data": "value"}, + ], + columnDefs: [ + { + render: function (data, type, row) { + return '<button class="btn btn-block btn-default" onclick="update(' + row.id + ')">Save</button>' + }, + targets: 2 + }, + { + render: function (data, type, row) { + return '<input id="value-' + row.id + '" type="text" value="' + row.value + '" class="form-control"/>' + }, + targets: 1 + } + + ] + }); + + $('#table_filter').css("display", "none"); + }); + }); + </script> +{% endblock scripts %} diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html index 7dbbeb016be68b380ca0b7516eea1f9b56dea4c3..19cd2afa61d442d314a2d0d5e97871f7a1352ff9 100644 --- a/smash/web/templates/sidebar.html +++ b/smash/web/templates/sidebar.html @@ -58,4 +58,11 @@ </a> </li> + <li data-desc="configuration"> + <a href="{% url 'web.views.configuration' %}"> + <i class="fa fa-wrench"></i> + <span>Configuration</span> + </a> + </li> + </ul> \ No newline at end of file diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py index 2088fa6aa8c049210159cae8d68799be987d77b4..fea131ec68825d924ccc2e8ec60e6d0d5035d88d 100644 --- a/smash/web/tests/functions.py +++ b/smash/web/tests/functions.py @@ -2,7 +2,7 @@ import datetime from django.contrib.auth.models import User -from web.models import Location, AppointmentType, Subject, Worker, Visit, Appointment +from web.models import Location, AppointmentType, Subject, Worker, Visit, Appointment, ConfigurationItem from web.models.constants import SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL from web.views.notifications import get_today_midnight_date @@ -89,3 +89,12 @@ def create_appointment(visit=None, when=None): length=30, location=get_test_location(), datetime_when=when) + + +def create_configuration_item(): + item = ConfigurationItem.objects.create() + item.type = "TEST" + item.value = "xxx" + item.name = "yyy" + item.save() + return item; diff --git a/smash/web/tests/test_api_configuration_item.py b/smash/web/tests/test_api_configuration_item.py new file mode 100644 index 0000000000000000000000000000000000000000..a550ff27cb88b64d95630dc0c032ffaee2695d13 --- /dev/null +++ b/smash/web/tests/test_api_configuration_item.py @@ -0,0 +1,33 @@ +# coding=utf-8 + + +from django.urls import reverse + +from web.models import ConfigurationItem +from web.tests.functions import create_configuration_item +from . import LoggedInTestCase + + +class TestConfigurationItemApi(LoggedInTestCase): + def test_configuration_items(self): + create_configuration_item() + response = self.client.get(reverse('web.api.configuration_items')) + self.assertEqual(response.status_code, 200) + + def test_configuration_modify_invalid(self): + response = self.client.get(reverse('web.api.update_configuration_item')) + self.assertEqual(response.status_code, 200) + self.assertTrue("error" in response.content) + + def test_configuration_modify_invalid_2(self): + response = self.client.get(reverse('web.api.update_configuration_item'), {'id': "-15", 'value': "XXX"}) + self.assertEqual(response.status_code, 200) + self.assertTrue("error" in response.content) + + def test_configuration_modify(self): + item = create_configuration_item() + new_val = 'new val' + response = self.client.get(reverse('web.api.update_configuration_item'), {'id': item.id, 'value': new_val}) + self.assertEqual(response.status_code, 200) + updated_item = ConfigurationItem.objects.get(id=item.id) + self.assertEqual(new_val, updated_item.value) diff --git a/smash/web/tests/test_view_configuration.py b/smash/web/tests/test_view_configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..4408228562166b9b96553fb6e293d43a965d5e13 --- /dev/null +++ b/smash/web/tests/test_view_configuration.py @@ -0,0 +1,16 @@ +import datetime + +from django.urls import reverse + +from functions import create_subject, create_visit, create_appointment, create_appointment_type +from web.forms import VisitDetailForm, VisitAddForm +from web.models import Visit +from web.views.notifications import get_today_midnight_date +from . import LoggedInTestCase + + +class ConfigurationViewTests(LoggedInTestCase): + def test_visit_details_request(self): + response = self.client.get(reverse('web.views.configuration')) + self.assertEqual(response.status_code, 200) + diff --git a/smash/web/urls.py b/smash/web/urls.py index 0a9c9be7130d5a27ae2d6e313a5c93858f312551..83a16c80987041c894842f77b1427cf6420aeb34 100644 --- a/smash/web/urls.py +++ b/smash/web/urls.py @@ -115,6 +115,12 @@ urlpatterns = [ url(r'^export$', views.export.export, name='web.views.export'), url(r'^export/(?P<type>[A-z]+)$', views.export.export_to_csv2, name='web.views.export_to_csv2'), + #################### + # CONFIGURATION # + #################### + + url(r'^configuration$', views.configuration_item.configuration_items, name='web.views.configuration'), + #################### # AUTH # #################### diff --git a/smash/web/views/__init__.py b/smash/web/views/__init__.py index 49820e91e6c47e45ffee4bc5d23f646f0c8364fd..e78ca58cbcfbf9adab1177766bbcedea96fd51ca 100644 --- a/smash/web/views/__init__.py +++ b/smash/web/views/__init__.py @@ -61,3 +61,4 @@ import mails import statistics import export import contact_attempt +import configuration_item diff --git a/smash/web/views/configuration_item.py b/smash/web/views/configuration_item.py new file mode 100644 index 0000000000000000000000000000000000000000..039e6290b3c198f47048800c4fb1377618a0341f --- /dev/null +++ b/smash/web/views/configuration_item.py @@ -0,0 +1,6 @@ +# coding=utf-8 +from . import wrap_response + + +def configuration_items(request): + return wrap_response(request, "configuration/index.html", {})