Skip to content

Commit

Permalink
major bug fix, new feature for forms:
Browse files Browse the repository at this point in the history
 - test config
 - moonsheep token - templatetag that needs to be included by apps using
 moonsheep so sent form was aware of resolved task
 - resolves #95
 - resolves #101
  • Loading branch information
ppeczek committed Dec 20, 2017
1 parent e8f97e4 commit 7b9bf0c
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 92 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
.idea/
__pycache__/
node_modules/

*.pyc
.tox/
.coverage
*.egg-info/
*.egg-info
.eggs/
build/
37 changes: 37 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
language: python
matrix:
include:
- python: 3.3
env: TOXENV=py33-18
- python: 3.4
env: TOXENV=py34-18
- python: 3.4
env: TOXENV=py34-19
- python: 3.4
env: TOXENV=py34-110
- python: 3.4
env: TOXENV=py34-111
- python: 3.4
env: TOXENV=py34-20
- python: 3.5
env: TOXENV=py35-18
- python: 3.5
env: TOXENV=py35-19
- python: 3.5
env: TOXENV=py35-110
- python: 3.5
env: TOXENV=py35-111
- python: 3.5
env: TOXENV=py35-20
- python: 3.6
env: TOXENV=py36-111
- python: 3.6
env: TOXENV=py36-20
# command to install dependencies
install:
- pip install tox
# command to run tests
script:
- tox -e $TOXENV
# containers
sudo: false
8 changes: 4 additions & 4 deletions moonsheep/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def decompress(self, value):
return value

def format_output(self, rendered_widgets):
widget_context = {'min': rendered_widgets[0], 'max': rendered_widgets[1],}
widget_context = {'min': rendered_widgets[0], 'max': rendered_widgets[1]}
return render_to_string('widgets/range_widget.html', widget_context)


Expand All @@ -25,8 +25,8 @@ class RangeField(forms.MultiValueField):
}

def __init__(self, field_class, widget=forms.TextInput, *args, **kwargs):
if not 'initial' in kwargs:
kwargs['initial'] = ['','']
if 'initial' not in kwargs:
kwargs['initial'] = ['', '']

fields = (field_class(), field_class())

Expand All @@ -38,6 +38,6 @@ def __init__(self, field_class, widget=forms.TextInput, *args, **kwargs):

def compress(self, data_list):
if data_list:
return [self.fields[0].clean(data_list[0]),self.fields[1].clean(data_list[1])]
return [self.fields[0].clean(data_list[0]), self.fields[1].clean(data_list[1])]

return None
1 change: 1 addition & 0 deletions moonsheep/tasks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import importlib
import pbclient

from .verifiers import MIN_CONFIDENCE, DEFAULT_DICT_VERIFIER


Expand Down
10 changes: 0 additions & 10 deletions moonsheep/templates/dummy.html

This file was deleted.

8 changes: 0 additions & 8 deletions moonsheep/templates/task.html

This file was deleted.

2 changes: 2 additions & 0 deletions moonsheep/templates/token.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<input type="hidden" name="_task_id" value="{{ task_id }}" />
<input type="hidden" name="_project_id" value="{{ project_id }}" />
11 changes: 11 additions & 0 deletions moonsheep/templatetags/moonsheep_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.template import Library

register = Library()


@register.inclusion_tag('token.html')
def moonsheep_token(task):
return {
'task_id': task.id,
'project_id': task.project_id
}
8 changes: 0 additions & 8 deletions moonsheep/templatetags/tasks.py

This file was deleted.

4 changes: 3 additions & 1 deletion moonsheep/tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
SECRET_KEY = 'fake-key'

INSTALLED_APPS = [
"moonsheep",
]

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}
}
130 changes: 78 additions & 52 deletions moonsheep/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,10 @@ class TaskView(FormView):
# TODO either we should have full template in Moonsheep or have that template in project_template
template_name = 'task.html'
form_template_name = None
task = None
form_class = None

def __init__(self, *args, **kwargs):
# TODO: don't get task for each creation
self.task = self._get_task()

# Template showing a task: presenter and the form, can be overridden by setting task_template in your Task
# By default it uses moonsheep/templates/task.html
if hasattr(self.task, 'task_template'):
self.template_name = self.task.task_template

if hasattr(self.task, 'task_form_template'):
self.form_template_name = self.task.task_form_template
if hasattr(self.task, 'task_form'):
self.form_class = self.task.task_form

if self.form_class is None and self.form_template_name is None:
raise TaskWithNoTemplateNorForm(self.task.__class__)

super(TaskView, self).__init__(*args, **kwargs)

def get_context_data(self, **kwargs):
# TODO: update docstring
def get(self, request, *args, **kwargs):
"""
Returns form for a this task
Expand All @@ -74,29 +56,12 @@ def get_context_data(self, **kwargs):
4. Otherwise return error suggesting to implement 2 or 3
:return: path to the template (string) or Django's Form class
"""
context = super(TaskView, self).get_context_data(**kwargs)
context.update({
'presenter': self.task.get_presenter(),
'task': self.task,
})
return context
self.task = self._get_task()

# =====================
# Override FormView to adapt for a case when user hasn't defined form for a given task
# and to process form in our own manner
self._get_form_class_data()

def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view.
Overrides django.views.generic.edit.FormMixin to adapt for a case
when user hasn't defined form for a given task.
"""
if form_class is None:
form_class = self.get_form_class()

if form_class is None:
return None
return form_class(**self.get_form_kwargs())
context = self.get_context_data(**kwargs)
return self.render_to_response(context)

def post(self, request, *args, **kwargs):
"""
Expand All @@ -106,11 +71,19 @@ def post(self, request, *args, **kwargs):
Overrides django.views.generic.edit.ProcessFormView to adapt for a case
when user hasn't defined form for a given task.
"""
if '_task_type_' not in request.POST:
# TODO handle specific task generation, ask @ppeczek if it's right
# we would also need task id! and maybe project id!
# return HttpResponseBadRequest('Missing _task_type field')
pass
if '_task_id' not in request.POST:
return HttpResponseBadRequest('Missing _task_id field. Include moonsheep_token template tag!')

if '_project_id' not in request.POST:
return HttpResponseBadRequest('Missing _project_id field. Include moonsheep_token template tag!')

self.task = self._get_task(
new=False,
project_id=request.POST['_project_id'],
task_id=request.POST['_task_id']
)

self._get_form_class_data()
form = self.get_form()

# no form defined in the task
Expand All @@ -127,6 +100,48 @@ def post(self, request, *args, **kwargs):
else:
return self.form_invalid(form)

def get_context_data(self, **kwargs):
context = super(TaskView, self).get_context_data(**kwargs)
context.update({
'presenter': self.task.get_presenter(),
'task': self.task,
})
return context

def _get_form_class_data(self):
# Template showing a task: presenter and the form, can be overridden by setting task_template in your Task
# By default it uses moonsheep/templates/task.html
if hasattr(self.task, 'task_template'):
self.template_name = self.task.task_template

if hasattr(self.task, 'task_form_template'):
self.form_template_name = self.task.task_form_template
if hasattr(self.task, 'task_form'):
self.form_class = self.task.task_form

if self.form_class is None and self.form_template_name is None:
raise TaskWithNoTemplateNorForm(self.task.__class__)

# =====================
# Override FormView to adapt for a case when user hasn't defined form for a given task
# and to process form in our own manner

def get_form_class(self):
return self.task.task_form if hasattr(self.task, 'task_form') else None

def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view.
Overrides django.views.generic.edit.FormMixin to adapt for a case
when user hasn't defined form for a given task.
"""
if form_class is None:
form_class = self.get_form_class()

if form_class is None:
return None
return form_class(**self.get_form_kwargs())

def form_valid(self, form):
self._send_task(form.cleaned_data)
return super(TaskView, self).form_valid(form)
Expand All @@ -139,7 +154,18 @@ def form_invalid(self, form):
# End of FormView override
# ========================

def _get_task(self):
def _get_task(self, new=True, project_id=None, task_id=None):
if new:
task_data = self._get_new_task()
else:
task_data = pbclient.get_task(
project_id=project_id,
task_id=task_id
)[0]

return AbstractTask.create_task_instance(task_data['info']['type'], **task_data)

def _get_new_task(self):
"""
Mechanism responsible for getting tasks. Points to PyBossa API and collects task.
Task structure contains type, url and metadata that might be displayed in template.
Expand All @@ -150,14 +176,14 @@ def _get_task(self):
if TASK_SOURCE == RANDOM_SOURCE:
task = self.get_random_mocked_task()
elif TASK_SOURCE == PYBOSSA_SOURCE:
task = self.get_pybossa_task()
task = self.get_random_pybossa_task()
else:
raise TaskSourceNotDefined

if not task:
raise NoTasksLeft

return AbstractTask.create_task_instance(task['info']['type'], **task)
return task

__mocked_task_counter = 0

Expand Down Expand Up @@ -193,7 +219,7 @@ def get_random_mocked_task(self):
else:
return default_params

def get_pybossa_task(self):
def get_random_pybossa_task(self):
"""
Method for obtaining task structure from distant source, i.e. PyBossa
Expand Down Expand Up @@ -232,7 +258,7 @@ def dispatch(self, request, *args, **kwargs):

def get(self, request):
# empty response so pybossa can set webhook to this endpoint
return HttpResponse()
return HttpResponse(status=201)

def post(self, request):
"""
Expand Down
12 changes: 8 additions & 4 deletions runtests.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
#!/usr/bin/env python
import os
import sys

import django

from django.conf import settings
from django.test.utils import get_runner

if __name__ == "__main__":

def runtests():
os.environ['DJANGO_SETTINGS_MODULE'] = 'moonsheep.tests.test_settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
failures = test_runner.run_tests(["moonsheep.tests"])
sys.exit(bool(failures))
sys.exit(bool(failures))


if __name__ == "__main__":
runtests()
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
author_email='[email protected]',
url='https://github.com/TransparenCEE/moonsheep/',
download_url='',
test_suite='runtests.runtests',
license='AGPL-3.0',
install_requires=[
'Django>=1.11',
Expand Down
Binary file removed test_settings.pyc
Binary file not shown.
17 changes: 13 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
[tox]
envlist = py27, py33, flake8
envlist =
py33-{18}
py34-{18,19,110,111,20}
py35-{18,19,110,111,20}
py36-{111,20}

[testenv]
commands =
pip install -e .
django-admin.py test --settings=test_settings
deps =
18: Django >= 1.8, < 1.9
19: Django >= 1.9, < 1.10
110: Django >= 1.10, < 1.11
111: Django >= 1.11, < 2.0
20: Django >= 2.0, < 2.1
commands = python setup.py test
skip_missing_interpreters = true

1 comment on commit 7b9bf0c

@KrzysztofMadejski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moonsheep_token.py nazwałbym moonsheep.py tak zeby tam wrzucic w przyszlosci wiele tagów i robic

{% load moonsheep %}
{{ moonsheep_token }}

Please sign in to comment.