Skip to content
Snippets Groups Projects
Commit e9524f04 authored by Alice Heaton's avatar Alice Heaton :speech_balloon:
Browse files

Add the add item functionality. Also enforce a default value for item-type on...

Add the add item functionality. Also enforce a default value for item-type on forms. While the API allows for items without an item-type, the form's handling of it was inconsistant. Until we have a use case for items without an item type, a default is enforced.
parent baa4ad35
No related branches found
No related tags found
1 merge request!40Add item frontend
......@@ -6,3 +6,12 @@
ITEM_TYPE_CATEGORY = {
'question': 'ebola-questions'
}
# The default item type used when editing a item. Currently
# nothing prevents creating an item without an item type
# from the API, this is used by the edit form.
DEFAULT_ITEM_TYPE = {
'taxonomy': 'item-types',
'name': 'question',
'long_name': 'Question'
}
......@@ -7,9 +7,10 @@ from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.template.response import TemplateResponse
from django.test import RequestFactory
from django.utils import timezone
import transport
from ..views.item import AddEditItemView
from ..views.item import AddEditItemView, DEFAULT_ITEM_TYPE
from .views_tests import (
assert_message,
......@@ -143,6 +144,18 @@ def generic_item():
}
@pytest.fixture
def item_without_item_type():
return {
'id': 1001,
'body': 'hello',
'created': datetime(2015, 5, 5),
'timestamp': datetime(2016, 6, 6),
'last_updated': datetime(2017, 7, 7),
'terms': []
}
@pytest.fixture
def an_item_type():
return {
......@@ -176,6 +189,25 @@ def test_the_item_type_is_added_to_the_view_on_get_requests(generic_item):
assert view.item_type['name'] == 'generic'
def test_there_is_a_default_item_type_on_get_requests(item_without_item_type):
default_item_type = {
'name': 'a-default-type',
'long_name': 'A Default Type',
'taxonomy': 'item-types'
}
with patch.dict(DEFAULT_ITEM_TYPE, default_item_type):
with patch('hid.views.item.transport.items.get') as get_item:
get_item.return_value = item_without_item_type
(view, response) = make_request(
AddEditItemView,
'edit-item',
kwargs={'item_id': 103}
)
assert view.item_type == default_item_type
def test_the_item_terms_are_added_to_the_view_on_get_requests(generic_item):
with patch('hid.views.item.transport.items.get') as get_item:
get_item.return_value = generic_item
......@@ -233,6 +265,30 @@ def test_the_item_type_is_added_to_the_view_on_post_requests(generic_item):
assert view.item_type['name'] == 'generic'
def test_there_is_a_default_item_type_on_post_requests(item_without_item_type):
default_item_type = {
'name': 'a-default-type',
'long_name': 'A Default Type',
'taxonomy': 'item-types'
}
with patch.dict(DEFAULT_ITEM_TYPE, default_item_type):
with patch('hid.views.item.transport.items.get') as get_item:
get_item.return_value = item_without_item_type
(view, response) = make_request(
AddEditItemView,
'edit-item',
kwargs={'item_id': 103},
request_type='post',
post={
'action': 'cancel',
'next': ''
}
)
assert view.item_type == default_item_type
def test_the_item_terms_are_added_to_the_view_on_post_requests(generic_item):
with patch('hid.views.item.transport.items.get') as get_item:
get_item.return_value = generic_item
......@@ -421,6 +477,81 @@ def test_add_new_item_with_unknown_item_type_redirects():
assert type(response) is HttpResponseRedirect
@pytest.mark.django_db
def test_submiting_form_with_id_equal_0_creates_an_item(item_type):
body = 'Hello, here is a new item'
the_time = datetime(2015, 06, 27, 0, 0)
(view, response) = make_request(
AddEditItemView,
'add-item',
kwargs={'item_type': item_type['name']},
request_type='post',
post={
'action': 'save',
'timestamp': the_time,
'next': '/',
'id': 0,
'body': body
}
)
assert view.item['id'] > 0
item = transport.items.get(view.item['id'])
assert item is not None
@pytest.mark.django_db
def test_submiting_form_creates_an_item_with_correct_fields(item_type):
body = 'Hello, here is a new item'
the_time = datetime(2015, 06, 27, 0, 0)
(view, response) = make_request(
AddEditItemView,
'add-item',
kwargs={'item_type': item_type['name']},
request_type='post',
post={
'action': 'save',
'timestamp': the_time,
'next': '/',
'id': 0,
'body': body
}
)
assert view.item['id'] > 0
item = transport.items.get(view.item['id'])
assert item['body'] == body
assert timezone.make_naive(item['timestamp']) == the_time
@pytest.mark.django_db
def test_submiting_form_creates_an_item_with_a_category(item_type):
body = 'Hello, here is a new item'
(view, response) = make_request(
AddEditItemView,
'add-item',
kwargs={'item_type': item_type['name']},
request_type='post',
post={
'action': 'save',
'timestamp': datetime.now(),
'next': '/',
'id': 0,
'category': 'Ebola updates',
'body': body
}
)
assert view.item['id'] > 0
item = transport.items.get(view.item['id'])
expected_term = {
'taxonomy': 'ebola-questions',
'name': 'Ebola updates',
'long_name': 'What are the current updates on Ebola.'
}
assert expected_term in item['terms']
@pytest.mark.django_db
def test_item_can_be_updated(view, form):
new_text = "What is the cause of Ebola?"
......@@ -483,25 +614,6 @@ def test_item_update_logs_message_and_redirects(view, form):
"Question %s successfully updated." % view.item['id'])
@pytest.mark.django_db
def test_item_update_without_type_logs_message(view, form):
view.item_type = None
view.form_valid(form)
assert_message(view.request,
messages.SUCCESS,
"Item %s successfully updated." % view.item['id'])
@pytest.mark.django_db
def test_no_category_when_item_type_not_set(view):
view.item_type = None
initial = view.get_initial()
assert 'category' not in initial
@pytest.mark.django_db
def test_item_update_transport_exception_logs_message(view, form):
# This could happen if someone else deletes the item when the
......
......@@ -8,7 +8,7 @@ from django.views.generic.edit import FormView
import transport
from ..forms.item import AddEditItemForm
from ..constants import ITEM_TYPE_CATEGORY
from ..constants import ITEM_TYPE_CATEGORY, DEFAULT_ITEM_TYPE
class ItemTypeNotFound(Exception):
......@@ -30,7 +30,8 @@ class AddEditItemView(FormView):
This has a side effect of initializing:
self.item_type to the item type (either from the given type
of from the given item)
of from the given item). If the given item has no item
type, DEFAULT_ITEM_TYPE is assumed
self.item to the item object (or None)
self.item_terms to a dictionary of taxonomy to list of terms
(or {})
......@@ -70,6 +71,10 @@ class AddEditItemView(FormView):
else:
self.item_type = matches[0]
# We guarantee there is always an item type
if self.item_type is None:
self.item_type = DEFAULT_ITEM_TYPE
def get(self, request, *args, **kwargs):
""" get request handler
......@@ -155,12 +160,10 @@ class AddEditItemView(FormView):
'next': self.request.GET.get('next', self.request.path)
}
item_type = getattr(self, 'item_type', None)
if item_type is not None:
taxonomy = ITEM_TYPE_CATEGORY.get(item_type['name'])
if (taxonomy and taxonomy in self.item_terms
and len(self.item_terms[taxonomy]) > 0):
initial['category'] = self.item_terms[taxonomy][0]['name']
taxonomy = ITEM_TYPE_CATEGORY.get(self.item_type['name'])
if (taxonomy and taxonomy in self.item_terms
and len(self.item_terms[taxonomy]) > 0):
initial['category'] = self.item_terms[taxonomy][0]['name']
return initial
......@@ -195,32 +198,26 @@ class AddEditItemView(FormView):
def form_valid(self, form):
""" Form submit handler """
id = int(form.cleaned_data['id'])
if self.item_type:
item_description = self.item_type['long_name']
taxonomy = ITEM_TYPE_CATEGORY.get(self.item_type['name'])
else:
item_description = 'Item'
taxonomy = None
# TODO: Combine terms into single transaction
category = form.cleaned_data.pop('category', None)
item_description = self.item_type['long_name']
taxonomy = ITEM_TYPE_CATEGORY.get(self.item_type['name'])
item_id = int(form.cleaned_data['id'])
try:
transport.items.update(id, form.cleaned_data)
if taxonomy:
if category:
transport.items.add_term(id, taxonomy, category)
else:
transport.items.delete_all_terms(id, taxonomy)
message = _("%s %d successfully updated.") % (
item_description,
id,
)
message_code = messages.SUCCESS
if item_id == 0:
self.item = self._create_item(form, taxonomy)
message = _("%s %d successfully created.") % (
item_description,
self.item['id']
)
message_code = messages.SUCCESS
else:
self._update_item(
item_id, form, taxonomy
)
message = _("%s %d successfully updated.") % (
item_description,
item_id,
)
message_code = messages.SUCCESS
except transport.exceptions.TransportException as e:
message = e.message.get('detail')
if message is None:
......@@ -233,6 +230,60 @@ class AddEditItemView(FormView):
message_code,
message)
def _update_item(self, item_id, form, taxonomy):
""" Update the given item
Args:
item_id (int): Item id to update
form (Form): Valid form object containing fields
taxonomy (str or None): Taxonomy of the item's
category field, if any
Raises:
TransportException: On API errors
"""
data = dict(form.cleaned_data)
category = data.pop('category', None)
transport.items.update(item_id, data)
# TODO: Combine terms into single transaction
if taxonomy:
if category:
transport.items.add_term(item_id, taxonomy, category)
else:
transport.items.delete_all_terms(item_id, taxonomy)
def _create_item(self, form, taxonomy):
""" Create the given item
Args:
item_id (int): Item id to update
form (Form): Valid form object containing fields
taxonomy (str or None): Taxonomy of the item's
category field, if any
Returns:
dict: The created item
Raises:
TransportException: On API errors
"""
data = dict(form.cleaned_data)
data.pop('id', None)
category = data.pop('category', None)
created_item = transport.items.create(data)
# TODO: Combine terms into single transaction
transport.items.add_term(
created_item['id'], 'item-types', self.item_type['name']
)
if taxonomy and category:
transport.items.add_term(created_item['id'], taxonomy, category)
return created_item
def form_invalid(self, form):
""" Form invalid handler """
messages.add_message(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment