Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCode Raguet <ignacio.code@gmail.com>2013-09-26 16:09:37 (GMT)
committer Code Raguet <ignacio.code@gmail.com>2013-09-26 16:09:37 (GMT)
commita5ffd91bf5d17ee5b3bbfc271c96d9b8aa8886ea (patch)
tree4d833dc14839d2d180d03efef3fd1be7b7e7ebef
parent04523d828911893ad8299bfd3ef661063c3afb8d (diff)
parentdcda2d37e96c38f435aa30839cbf8806a4c26900 (diff)
Merge branch 'DEV'v4.21
-rw-r--r--webapp/polls/exceptions.py5
-rw-r--r--webapp/polls/models.py18
-rw-r--r--webapp/polls/templates/poll-structure-form.html4
-rw-r--r--webapp/polls/tests/poll_tests.py1
-rw-r--r--webapp/polls/tests/result_tests.py16
-rw-r--r--webapp/polls/tests/structure_tests.py83
-rw-r--r--webapp/polls/views.py9
-rw-r--r--webapp/webapp/features/cannot_modify_poll_structure.feature12
-rw-r--r--webapp/webapp/features/terrain.py4
9 files changed, 123 insertions, 29 deletions
diff --git a/webapp/polls/exceptions.py b/webapp/polls/exceptions.py
index 76e1fa0..32262a1 100644
--- a/webapp/polls/exceptions.py
+++ b/webapp/polls/exceptions.py
@@ -4,3 +4,8 @@ class ValidationError(Exception):
class UniqueNameError(Exception):
pass
+
+
+class ReadOnly(Exception):
+ """Exception when trying to write a read only object."""
+ pass
diff --git a/webapp/polls/models.py b/webapp/polls/models.py
index 0090164..9402c94 100644
--- a/webapp/polls/models.py
+++ b/webapp/polls/models.py
@@ -10,9 +10,7 @@ import copy
import warnings
import glob
import errno
-
from distutils.dir_util import copy_tree
-from exceptions import ValidationError, UniqueNameError
from bson import ObjectId, DBRef
from datetime import datetime
@@ -23,8 +21,8 @@ from django.core.exceptions import ValidationError as DjangoValidationError
from utils.mongo_connection import get_db
from utils.mongo_document import Document
from utils.strings import multiple_replace
-
from pollster.models import Pollster
+from polls.exceptions import ReadOnly, ValidationError
def is_image_file(value):
@@ -70,7 +68,6 @@ class AbstracErrorObject(object):
class Poll(Document, AbstracErrorObject):
collection_name = 'polls'
- UniqueNameError = UniqueNameError
OPEN = "Abierta"
CLOSED = "Cerrada"
@@ -1001,8 +998,9 @@ class Group(AbstractObject, ComponentStructure):
class Structure(AbstractObject, ComponentStructure):
+ """Class that handles the structure of a poll.
- """
+ Example of data_structure:
{
'groups': {
'0': {
@@ -1022,6 +1020,8 @@ class Structure(AbstractObject, ComponentStructure):
}
}
"""
+ READ_ONLY_MSG = ('No puede modificar la estructura'
+ ' de una encuesta con resultados.')
def __init__(self, data={}, poll=None, *args, **kwargs):
super(Structure, self).__init__(poll, *args, **kwargs)
@@ -1168,6 +1168,7 @@ class Structure(AbstractObject, ComponentStructure):
return data
def save(self):
+ """Save structure to database. Returns structure's ID."""
structure_id = None
self.validate()
@@ -1177,6 +1178,8 @@ class Structure(AbstractObject, ComponentStructure):
# Prepare dbref to poll object
if not self.poll:
raise ValidationError("Need a parent poll.")
+ elif self.is_read_only():
+ raise ReadOnly(Structure.READ_ONLY_MSG)
else:
dbref = DBRef(Poll.collection_name, ObjectId(self.poll.id))
_dict.update({'poll': dbref})
@@ -1317,6 +1320,11 @@ class Structure(AbstractObject, ComponentStructure):
# TODO: LOG!
pass
+ def is_read_only(self):
+ """Tells if the structure can be modified."""
+ poll = self.poll
+ return not poll.is_open() or poll.has_result()
+
class NodePollResult(object):
diff --git a/webapp/polls/templates/poll-structure-form.html b/webapp/polls/templates/poll-structure-form.html
index 7de54de..0b6ed50 100644
--- a/webapp/polls/templates/poll-structure-form.html
+++ b/webapp/polls/templates/poll-structure-form.html
@@ -38,14 +38,16 @@
{% render_structure structure %}
+ {% if not structure.is_read_only %}
<div class="ps-form-toolbar btn-toolbar clearfix">
<div class="btn-group">
<button name="save" class="btn btn-primary"><i class="icon-white icon-edit"></i>&nbsp;{% trans 'Guardar' %}</button>
</div>
</div>
+ {% endif %}
{% csrf_token %}
</form>
-{% endblock %} \ No newline at end of file
+{% endblock %}
diff --git a/webapp/polls/tests/poll_tests.py b/webapp/polls/tests/poll_tests.py
index 19c36c1..0b3a168 100644
--- a/webapp/polls/tests/poll_tests.py
+++ b/webapp/polls/tests/poll_tests.py
@@ -188,7 +188,6 @@ class PollTests(MongoTestCase):
def test_clone(self):
poll = Poll(data={'name': 'name'})
- poll.status = Poll.CLOSED
poll_id = poll.save()
poll = Poll.get(poll_id)
diff --git a/webapp/polls/tests/result_tests.py b/webapp/polls/tests/result_tests.py
index 57ae49a..0936cf0 100644
--- a/webapp/polls/tests/result_tests.py
+++ b/webapp/polls/tests/result_tests.py
@@ -96,8 +96,7 @@ class PollResultTests(MongoTestCase):
poll_data = {
"name": "test",
- "pollsters": [pollster],
- "status": Poll.CLOSED
+ "pollsters": [pollster]
}
structure_data = {
@@ -325,10 +324,7 @@ class CsvNoRepitePesoParaOpcionesConElMismoTextDescriptivo(MongoTestCase):
"""
def setUp(self):
- poll_data = {
- "name": "test",
- "status": Poll.CLOSED
- }
+ poll_data = {"name": "test"}
question_name = "repite nombre pregunta"
structure_data = {
@@ -397,10 +393,14 @@ class CsvNoRepitePesoParaOpcionesConElMismoTextDescriptivo(MongoTestCase):
poll = Poll(data=poll_data)
poll_id = poll.save()
- self.poll = Poll.get(poll_id)
- structure = Structure(data=structure_data, poll=self.poll)
+ poll = Poll.get(poll_id)
+ structure = Structure(data=structure_data, poll=poll)
structure.save()
+ poll.status = Poll.CLOSED
+ poll_id = poll.save()
+ self.poll = Poll.get(poll_id)
+
def test_get_csv_header_with_order(self):
poll = self.poll
diff --git a/webapp/polls/tests/structure_tests.py b/webapp/polls/tests/structure_tests.py
index 7508248..c9fe0d9 100644
--- a/webapp/polls/tests/structure_tests.py
+++ b/webapp/polls/tests/structure_tests.py
@@ -7,6 +7,7 @@ from mock import Mock
from utils.test import (MongoTestCase, mock_in_memory_image,
remove_option_images_dir)
from polls.tests.poll_tests import MockImageFileInterface
+from polls.exceptions import ReadOnly
class StructureTests(MongoTestCase):
@@ -162,17 +163,6 @@ class StructureTests(MongoTestCase):
self.assertEqual(1, len(structure.errors))
- def test_save(self):
-
- structure = Structure(data=self.data)
-
- self.assertRaises(structure.ValidationError, structure.save)
-
- structure = Structure(data=self.data, poll=self.poll)
- structure.save()
-
- self.assertEqual(1, self.db.structures.count())
-
def test_get(self):
structure = Structure(data=self.data, poll=self.poll)
structure_id = structure.save()
@@ -321,6 +311,39 @@ class StructureTests(MongoTestCase):
self.assertEqual(1, len(structure.get_image_options()))
+ def test_it_should_respond_to_ready_only_msg(self):
+ self.assertTrue(hasattr(Structure, 'READ_ONLY_MSG'))
+
+
+class ReadOnlyTest(MongoTestCase):
+
+ def setUp(self):
+ poll = Poll({"name": "poll name"})
+ poll_id = poll.save()
+ self.poll = poll.get(poll_id)
+
+ def test_it_should_be_read_only_when_poll_has_results(self):
+ poll = self.poll
+ structure = Structure(data={}, poll=poll)
+ self.assertFalse(poll.has_result())
+ self.assertFalse(structure.is_read_only())
+
+ poll.has_result = Mock(return_value=True)
+ self.assertTrue(poll.has_result())
+ self.assertTrue(structure.is_read_only())
+
+ def test_read_only_when_poll_is_closed_and_has_not_results(self):
+ poll = self.poll
+ poll.status = Poll.CLOSED
+ poll_id = poll.save()
+ poll = poll.get(poll_id)
+
+ structure = Structure(data={}, poll=poll)
+ self.assertFalse(poll.has_result())
+ self.assertFalse(poll.is_open())
+
+ self.assertTrue(structure.is_read_only())
+
class UploadOptionImagesTest(MongoTestCase):
@@ -400,3 +423,41 @@ class UploadOptionImagesTest(MongoTestCase):
def tearDown(self):
remove_option_images_dir()
+
+
+class SaveStructureTest(MongoTestCase):
+
+ def setUp(self):
+ poll = Poll(data={'name': 'name'})
+ poll_id = poll.save()
+
+ self.poll = Poll.get(id=poll_id)
+
+ self.data = {
+ 'poll_id': 'POLL_ID',
+ 'groups': {
+ '0': {
+ 'name': 'group_0',
+ 'fields': {
+ '0': {
+ 'widget_type': Field.TextInput,
+ 'name': 'field_0_0'
+ }
+ }
+ }
+ }
+ }
+
+ def test_save(self):
+ structure = Structure(data=self.data)
+ self.assertRaises(structure.ValidationError, structure.save)
+
+ structure = Structure(data=self.data, poll=self.poll)
+ structure.save()
+ self.assertEqual(1, self.db.structures.count())
+
+ def test_it_should_not_save_if_is_read_only(self):
+ poll = self.poll
+ structure = Structure(data=self.data, poll=poll)
+ structure.is_read_only = Mock(return_value=True)
+ self.assertRaises(ReadOnly, structure.save)
diff --git a/webapp/polls/views.py b/webapp/polls/views.py
index db297ac..c19eac1 100644
--- a/webapp/polls/views.py
+++ b/webapp/polls/views.py
@@ -168,7 +168,8 @@ class PollListView(ListView):
),
},
'action_structure_builder': {
- 'disabled': "disabled" if not poll.is_open() else "",
+ 'disabled': ("disabled" if
+ poll.structure.is_read_only() else ""),
'url': reverse(
'sociologist:structure.builder',
kwargs={'poll_id': str(poll.id)}
@@ -199,9 +200,13 @@ class StructureFormView(TemplateView):
def get(self, request, *args, **kwargs):
context = self.get_context_data()
+ structure = self.poll.structure
- context.update({'structure': self.poll.structure})
+ read_only = Structure.READ_ONLY_MSG
+ msg = read_only if structure.is_read_only() else ''
+ messages.add_message(self.request, messages.WARNING, msg)
+ context.update({'structure': structure})
return self.render_to_response(context)
def get_context_data(self, **kwargs):
diff --git a/webapp/webapp/features/cannot_modify_poll_structure.feature b/webapp/webapp/features/cannot_modify_poll_structure.feature
new file mode 100644
index 0000000..a424bdd
--- /dev/null
+++ b/webapp/webapp/features/cannot_modify_poll_structure.feature
@@ -0,0 +1,12 @@
+Feature: Researcher can't modify poll's structure
+ As a Researcher
+ I want to not be able to modify a poll's structure
+ when a poll has results
+ So that there is no chance to get mixed results from a poll
+
+ Scenario: a poll with results
+ Given I am logged in as "Researcher"
+ And "poll" exists
+ And "poll" has "simple.poll_result"
+ When I visit the "Modificar estructura" page for "poll" poll
+ Then I should see a message that says "No puede modificar"
diff --git a/webapp/webapp/features/terrain.py b/webapp/webapp/features/terrain.py
index 632ac37..ed52bf6 100644
--- a/webapp/webapp/features/terrain.py
+++ b/webapp/webapp/features/terrain.py
@@ -36,9 +36,11 @@ def load_fixture(fixture_name):
@before.each_feature
def before_each_feature(feature):
+ """This is the main lettuce hook "@before.each_feature"."""
drop_mongo()
drop_sqlite()
- if feature.name == "Researcher adds images to options":
+ if feature.name in ("Researcher adds images to options",
+ "Researcher can't modify poll's structure"):
load_fixture("generic_researcher")