-
Notifications
You must be signed in to change notification settings - Fork 285
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP]Due Dates: implement basic backend
This commit implements a basic model definition for due dates with validation, along with an API endpoint to create and delete these.
- Loading branch information
Showing
13 changed files
with
301 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
|
||
from pootle.core.views.api import APIView | ||
|
||
from .forms import DueDateForm | ||
from .models import DueDate | ||
|
||
|
||
class DueDateView(APIView): | ||
model = DueDate | ||
restrict_to_methods = ('POST', 'DELETE', ) | ||
fields = ('due_date', 'modified_by', 'pootle_path', ) | ||
add_form_class = DueDateForm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
from django import forms | ||
|
||
from pootle.core.fields import ISODateTimeField | ||
|
||
from .models import DueDate | ||
|
||
|
||
class DueDateForm(forms.ModelForm): | ||
|
||
due_date = ISODateTimeField() | ||
|
||
class Meta: | ||
model = DueDate | ||
fields = ('due_date', 'pootle_path', 'modified_by', ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import unicode_literals | ||
|
||
from django.db import migrations, models | ||
from django.conf import settings | ||
import duedates.models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='DueDate', | ||
fields=[ | ||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||
('due_date', models.DateTimeField()), | ||
('pootle_path', models.CharField(db_index=True, max_length=255, validators=[duedates.models.validate_pootle_path])), | ||
('modified_on', models.DateTimeField(auto_now_add=True)), | ||
('modified_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), | ||
], | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
from django.conf import settings | ||
from django.core.exceptions import ValidationError | ||
from django.db import models | ||
|
||
INVALID_POOTLE_PATHS = ['/', '/projects/'] | ||
|
||
|
||
def validate_pootle_path(value): | ||
if value in INVALID_POOTLE_PATHS: | ||
raise ValidationError('Cannot set due date for this path.') | ||
|
||
|
||
class DueDate(models.Model): | ||
|
||
due_date = models.DateTimeField() | ||
pootle_path = models.CharField(max_length=255, db_index=True, | ||
validators=[validate_pootle_path]) | ||
modified_by = models.ForeignKey(settings.AUTH_USER_MODEL, db_index=True) | ||
modified_on = models.DateTimeField(auto_now_add=True) | ||
|
||
def save(self, *args, **kwargs): | ||
self.full_clean() | ||
|
||
super(DueDate, self).save(*args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
from django.conf.urls import url | ||
|
||
from .api import DueDateView | ||
|
||
|
||
urlpatterns = [ | ||
url(r'^duedates/?$', | ||
DueDateView.as_view(), | ||
name='pootle-xhr-duedates'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
import pytest | ||
|
||
from django.utils import timezone | ||
|
||
from pytest_pootle.factories import UserFactory | ||
|
||
from duedates.forms import DueDateForm | ||
from duedates.models import INVALID_POOTLE_PATHS | ||
|
||
|
||
@pytest.mark.parametrize('pootle_path', INVALID_POOTLE_PATHS) | ||
def test_duedate_form_validation_invalid_paths(pootle_path): | ||
"""Tests certain path restrictions when validating due date forms.""" | ||
form_data = { | ||
'pootle_path': pootle_path, | ||
} | ||
form = DueDateForm(form_data) | ||
assert not form.is_valid() | ||
|
||
assert 'pootle_path' in form.errors | ||
assert 'Cannot set due date for this path.' in form.errors['pootle_path'] | ||
|
||
|
||
@pytest.mark.django_db | ||
@pytest.mark.parametrize('due_in', [timezone.now(), '2016-09-06T14:19:52.985Z']) | ||
@pytest.mark.parametrize('pootle_path', [ | ||
'/ru/', '/ru/foo/', '/ru/foo/bar/', '/ru/foo/bar/baz.po', | ||
'/projects/foo/', '/projects/foo/bar/', '/projects/foo/bar/baz.po' | ||
]) | ||
def test_duedate_create(due_in, pootle_path): | ||
"""Tests form validation for a set of paths and date time formats.""" | ||
user = UserFactory.create() | ||
|
||
form_data = { | ||
'due_date': due_in, | ||
'modified_by': user.id, | ||
'pootle_path': pootle_path, | ||
} | ||
form = DueDateForm(form_data) | ||
assert form.is_valid() | ||
# TODO: form.save |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
import pytest | ||
|
||
from django.core.exceptions import ValidationError | ||
from django.utils import timezone | ||
|
||
from pytest_pootle.factories import UserFactory | ||
|
||
from duedates.models import INVALID_POOTLE_PATHS, DueDate | ||
|
||
|
||
@pytest.mark.django_db | ||
@pytest.mark.parametrize('pootle_path', INVALID_POOTLE_PATHS) | ||
def test_duedate_create_invalid_paths(pootle_path): | ||
"""Tests certain path restrictions when creating due dates.""" | ||
with pytest.raises(ValidationError) as excinfo: | ||
DueDate.objects.create(pootle_path=pootle_path) | ||
|
||
message_dict = excinfo.value.message_dict | ||
assert 'pootle_path' in message_dict | ||
assert 'Cannot set due date for this path.' in message_dict['pootle_path'] | ||
|
||
|
||
@pytest.mark.django_db | ||
@pytest.mark.parametrize('pootle_path', [ | ||
'/ru/', '/ru/foo/', '/ru/foo/bar/', '/ru/foo/bar/baz.po', | ||
'/projects/foo/', '/projects/foo/bar/', '/projects/foo/bar/baz.po' | ||
]) | ||
def test_duedate_create(pootle_path): | ||
"""Tests due dates creation for valid paths.""" | ||
user = UserFactory.create() | ||
due_in = timezone.now() | ||
|
||
due_date = DueDate.objects.create( | ||
due_date=due_in, | ||
modified_by=user, | ||
pootle_path=pootle_path, | ||
) | ||
assert due_date.id is not None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright (C) Pootle contributors. | ||
# | ||
# This file is a part of the Pootle project. It is distributed under the GPL3 | ||
# or later license. See the LICENSE file for a copy of the license and the | ||
# AUTHORS file for copyright and authorship information. | ||
|
||
import pytest | ||
|
||
from django.utils import timezone | ||
|
||
from pytest_pootle.factories import DueDateFactory, UserFactory | ||
from pytest_pootle.utils import create_api_request | ||
|
||
from pootle.core.utils.json import jsonify | ||
|
||
from duedates.models import DueDate | ||
from duedates.api import DueDateView | ||
|
||
|
||
def test_duedate_get(rf): | ||
"""Tests the due date endpoint cannot be GET'ed.""" | ||
view = DueDateView.as_view() | ||
request = create_api_request(rf, url='/') | ||
response = view(request) | ||
assert response.status_code == 405 | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_duedate_post(rf): | ||
"""Tests due dates can be added.""" | ||
view = DueDateView.as_view() | ||
user = UserFactory.create() | ||
due_in = timezone.now() | ||
pootle_path = '/ru/foo/bar/' | ||
|
||
request_data = { | ||
'due_date': due_in, | ||
'modified_by': user.id, | ||
'pootle_path': pootle_path, | ||
} | ||
request = create_api_request(rf, method='POST', data=request_data) | ||
response = view(request) | ||
assert response.status_code == 200 | ||
|
||
due_date = DueDate.objects.latest('id') | ||
|
||
# Not checking `datetime` objects directly because microseconds are adjusted | ||
# when serializing, so checking the serialized values. | ||
assert jsonify(due_date.due_date) == jsonify(due_in) | ||
assert due_date.modified_by == user | ||
assert due_date.pootle_path == pootle_path | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_duedate_delete(rf): | ||
"""Tests due dates can be deleted.""" | ||
view = DueDateView.as_view() | ||
user = UserFactory.create() | ||
due_in = timezone.now() | ||
pootle_path = '/ru/foo/bar/' | ||
|
||
data = { | ||
'due_date': due_in, | ||
'modified_by': user, | ||
'pootle_path': pootle_path, | ||
} | ||
due_date = DueDateFactory.create(**data) | ||
|
||
assert due_date.id is not None | ||
due_date_count_pre_delete = DueDate.objects.count() | ||
|
||
request = create_api_request(rf, method='DELETE') | ||
view_kwargs = { | ||
'id': due_date.id, | ||
} | ||
response = view(request, **view_kwargs) | ||
assert response.status_code == 200 | ||
assert DueDate.objects.count() == due_date_count_pre_delete - 1 |