diff --git a/delft3dworker/fixtures/default_template.json b/delft3dworker/fixtures/default_template.json index f23354c0..36298cae 100644 --- a/delft3dworker/fixtures/default_template.json +++ b/delft3dworker/fixtures/default_template.json @@ -100,14 +100,14 @@ { "name": "Base level change", "validators": { - "max": 3.0, - "min": -3.0 + "max": 80, + "min": -80 }, "factor": true, - "units": "m", + "units": "%", "type": "numeric", "id": "baselevel", - "description": "Positive values define 'a base level rise', negative values define 'a base level fall'. The magnitude of this value should be based on simulation stop time: the larger the stop time value, the larger the magnitude of this value can be." + "description": "Positive values define 'a base level rise', negative values define 'a base level fall'." } ], "name": "Forcing" @@ -285,15 +285,15 @@ "name": "Base level change", "default": 0, "validators": { - "max": 3.0, + "max": 80.0, "required": true, - "min": -3.0 + "min": -80.0 }, "factor": true, - "units": "m", + "units": "%", "type": "numeric", "id": "baselevel", - "description": "Positive values define 'a base level rise', negative values define 'a base level fall'. The magnitude of this value should be based on simulation stop time: the larger the stop time value, the larger the magnitude of this value can be." + "description": "This is a percentage of the level change produced by the basin slope.

Click on the button below to see a table with the absolute values." } ], "name": "Forcing" @@ -346,4 +346,4 @@ "model": "delft3dworker.template", "pk": 53 } -] +] \ No newline at end of file diff --git a/delft3dworker/models.py b/delft3dworker/models.py index 3ab196d0..16e85f60 100644 --- a/delft3dworker/models.py +++ b/delft3dworker/models.py @@ -1667,14 +1667,18 @@ class Template(models.Model): meta = JSONField(blank=True) sections = JSONField(blank=True) - def save(self, *args, **kwargs): - returnval = super(Template, self).save(*args, **kwargs) + # The following method is disabled as it adds to much garbage + # to the MAIN search template + # TODO: implement proper search template which uses REST list_views + + # def save(self, *args, **kwargs): + # returnval = super(Template, self).save(*args, **kwargs) - # update the MAIN search form after any template save - searchform, created = SearchForm.objects.get_or_create(name="MAIN") - searchform.update() + # # update the MAIN search form after any template save + # searchform, created = SearchForm.objects.get_or_create(name="MAIN") + # searchform.update() - return returnval + # return returnval def __unicode__(self): return self.name diff --git a/delft3dworker/test_models.py b/delft3dworker/test_models.py index 37c03093..328cd971 100644 --- a/delft3dworker/test_models.py +++ b/delft3dworker/test_models.py @@ -1474,38 +1474,40 @@ def setUp(self): ] """) - def test_search_form_builds_on_template_save(self): - """ - Test if saving multiple templates creates and updates the search form. - """ - - template = Template.objects.create( - name='Template 1', - meta='{}', - sections=self.sections_a, - ) - - # first template created non-existing search form - searchforms = SearchForm.objects.filter(name='MAIN') - self.assertEqual(len(searchforms), 1) - - template2 = Template.objects.create( - name='Template 2', - meta='{}', - sections=self.sections_b, - ) - - # second template did not create an additional search form - searchforms = SearchForm.objects.filter(name='MAIN') - self.assertEqual(len(searchforms), 1) - - # all fields are as expected - searchform = searchforms[0] - self.assertEqual( - searchform.templates, - self.templates_res - ) - self.assertEqual( - searchform.sections, - self.sections_res - ) + # TODO: implement proper means to generate search form json + + # def test_search_form_builds_on_template_save(self): + # """ + # Test if saving multiple templates creates and updates the search form. + # """ + + # template = Template.objects.create( + # name='Template 1', + # meta='{}', + # sections=self.sections_a, + # ) + + # # first template created non-existing search form + # searchforms = SearchForm.objects.filter(name='MAIN') + # self.assertEqual(len(searchforms), 1) + + # template2 = Template.objects.create( + # name='Template 2', + # meta='{}', + # sections=self.sections_b, + # ) + + # # second template did not create an additional search form + # searchforms = SearchForm.objects.filter(name='MAIN') + # self.assertEqual(len(searchforms), 1) + + # # all fields are as expected + # searchform = searchforms[0] + # self.assertEqual( + # searchform.templates, + # self.templates_res + # ) + # self.assertEqual( + # searchform.sections, + # self.sections_res + # ) diff --git a/delft3dworker/test_views.py b/delft3dworker/test_views.py index da835e89..b05cc729 100644 --- a/delft3dworker/test_views.py +++ b/delft3dworker/test_views.py @@ -19,6 +19,7 @@ from mock import MagicMock from mock import patch +from delft3dworker.models import Container from delft3dworker.models import Scenario from delft3dworker.models import Scene from delft3dworker.models import Template @@ -524,6 +525,10 @@ def setUp(self): } } self.scene_1.save() + container_1 = Container.objects.create( + version={'d3d': 1}, + scene=self.scene_1 + ) self.scene_2 = Scene.objects.create( name='Testscene 2', @@ -544,6 +549,10 @@ def setUp(self): } } self.scene_2.save() + container_2 = Container.objects.create( + version={'d3d': 2, 'other': 'a'}, + scene=self.scene_2 + ) # Object general assign_perm('view_scenario', self.user_bar, self.scenario) @@ -713,6 +722,38 @@ def test_search_hack(self): query = {'parameter': "hack,mud,grease,more"} self.assertEqual(len(self._request(query)), 2) + def test_search_versions(self): + """ + Test search options + """ + + query = {'versions': "argh"} + self.assertEqual(len(self._request(query)), 2) + + query = {'versions': "{}"} + self.assertEqual(len(self._request(query)), 2) + + query = {'versions': '{"d3d":[1]}'} + self.assertEqual(len(self._request(query)), 1) + + query = {'versions': '{"d3d":[2]}'} + self.assertEqual(len(self._request(query)), 1) + + query = {'versions': '{"d3d":[1,2]}'} + self.assertEqual(len(self._request(query)), 2) + + query = {'versions': '{"d3d":[1,2], "other": "a"}'} + self.assertEqual(len(self._request(query)), 2) + + query = {'versions': '{"d3d":[1], "other": "a"}'} + self.assertEqual(len(self._request(query)), 2) + + query = {'versions': '{"d3d":[2], "other": "a"}'} + self.assertEqual(len(self._request(query)), 1) + + query = {'versions': '{"d3d":None}'} + self.assertEqual(len(self._request(query)), 2) + def _request(self, query): url = reverse('scene-list') self.client.login(username='bar', password='secret') @@ -720,6 +761,113 @@ def _request(self, query): return response.data +class SceneVersionTestCase(APITestCase): + """ + SceneVersionTestCase + Tests the Scenario Django REST API + """ + + def setUp(self): + user_bar = User.objects.create_user( + username='bar', + password='secret' + ) + scenario = Scenario.objects.create( + name='Testscenario', + owner=user_bar, + ) + + scene_1 = Scene.objects.create( + name='Testscene 1', + owner=user_bar + ) + scene_1.scenario.add(scenario) + + scene_2 = Scene.objects.create( + name='Testscene 1', + owner=user_bar + ) + scene_2.scenario.add(scenario) + container_2_1 = Container.objects.create( + version={ 'd3dversion': '1' }, + scene=scene_2 + ) + + scene_3 = Scene.objects.create( + name='Testscene 1', + owner=user_bar + ) + scene_3.scenario.add(scenario) + container_3_1 = Container.objects.create( + version={ 'd3dversion': '2' }, + scene=scene_3 + ) + + scene_4 = Scene.objects.create( + name='Testscene 1', + owner=user_bar + ) + scene_4.scenario.add(scenario) + container_4_1 = Container.objects.create( + version={ 'd3dversion': '1' }, + scene=scene_4 + ) + container_4_2 = Container.objects.create( + version={ 'svnversion': 'a' }, + scene=scene_4 + ) + + scene_5 = Scene.objects.create( + name='Testscene 1', + owner=user_bar + ) + scene_5.scenario.add(scenario) + container_5_1 = Container.objects.create( + version={ 'd3dversion': '1' }, + scene=scene_5 + ) + container_5_2 = Container.objects.create( + version={ 'svnversion': 'b' }, + scene=scene_5 + ) + container_5_3 = Container.objects.create( + version={ 'anything': 'I' }, + scene=scene_5 + ) + + # Object general + assign_perm('view_scenario', user_bar, scenario) + assign_perm('view_scene', user_bar, scene_1) + assign_perm('view_scene', user_bar, scene_2) + assign_perm('view_scene', user_bar, scene_3) + assign_perm('view_scene', user_bar, scene_4) + assign_perm('view_scene', user_bar, scene_5) + + # Model general + user_bar.user_permissions.add( + Permission.objects.get(codename='view_scenario')) + user_bar.user_permissions.add( + Permission.objects.get(codename='view_scene')) + + # Refetch to empty permissions cache + user_bar = User.objects.get(pk=user_bar.pk) + + def test_get_versions(self): + # detail view + url = reverse('scene-versions') + + # foo can see + self.client.login(username='bar', password='secret') + response = self.client.get(url, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + response.render() + self.assertEqual(response.data, { + 'svnversion': set(['a', 'b']), + 'd3dversion': set(['1', '2']), + 'anything': set(['I']) + }) + + class ScenarioTestCase(APITestCase): """ ScenarioTestCase diff --git a/delft3dworker/views.py b/delft3dworker/views.py index 9dee17cb..8049bbff 100644 --- a/delft3dworker/views.py +++ b/delft3dworker/views.py @@ -6,12 +6,14 @@ import datetime import django_filters import io +import json import logging import zipfile from django.contrib.auth.models import Group from django.contrib.auth.models import User from django.core.urlresolvers import reverse_lazy +from django.db.models import Q from django.http import HttpResponse from django.shortcuts import get_object_or_404 from django.utils.dateparse import parse_date @@ -33,6 +35,7 @@ from rest_framework.decorators import list_route from rest_framework.response import Response +from delft3dworker.models import Container from delft3dworker.models import Scenario from delft3dworker.models import Scene from delft3dworker.models import Template @@ -225,6 +228,8 @@ def get_queryset(self): shared = self.request.query_params.getlist('shared', []) users = self.request.query_params.getlist('users', []) + versions = self.request.query_params.get('versions', "\{\}") + created_after = self.request.query_params.get('created_after', '') created_before = self.request.query_params.get('created_before', '') started_after = self.request.query_params.get('started_after', '') @@ -342,6 +347,17 @@ def get_queryset(self): userids = [int(user) for user in users if user.isdigit()] queryset = queryset.filter(owner__in=userids) + if versions != "\{\}": + try: + version_dict = json.loads(versions) + except ValueError: + version_dict = {} + f = Q() + for key, values in version_dict.iteritems(): + for value in values: + f = f | Q(container__version__contains={key: value}) + queryset = queryset.filter(f) + if created_after != '': created_after_date = parse_date(created_after) if created_after_date: @@ -531,6 +547,18 @@ def export_all(self, request): resp['Content-Disposition'] = 'attachment; filename=Delft3DGTFiles.zip' return resp + @list_route(methods=["get"]) + def versions(self, request): + queryset = Container.objects.all() + + resp = {} + for container in queryset: + for key, val in container.version.iteritems(): + resp.setdefault(key, set([])).add(val) + + return Response(resp) + + class SearchFormViewSet(viewsets.ModelViewSet): """ API endpoint that allows search forms to be viewed.