Skip to content

Commit

Permalink
[#1897] New result indicator models
Browse files Browse the repository at this point in the history
  • Loading branch information
KasperBrandt committed Feb 1, 2016
1 parent bb05503 commit 8d57677
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 61 deletions.
7 changes: 5 additions & 2 deletions akvo/rest/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from .employment import EmploymentSerializer
from .focus_area import FocusAreaSerializer
from .goal import GoalSerializer
from .indicator import IndicatorPeriodSerializer, IndicatorSerializer
from .indicator import (IndicatorSerializer, IndicatorPeriodSerializer,
IndicatorPeriodDataSerializer, IndicatorPeriodDataCommentSerializer)
from .internal_organisation_id import InternalOrganisationIDSerializer
from .invoice import InvoiceSerializer
from .keyword import KeywordSerializer
Expand Down Expand Up @@ -67,8 +68,10 @@
'EmploymentSerializer',
'FocusAreaSerializer',
'GoalSerializer',
'IndicatorPeriodSerializer',
'IndicatorSerializer',
'IndicatorPeriodSerializer',
'IndicatorPeriodDataSerializer',
'IndicatorPeriodDataCommentSerializer',
'InternalOrganisationIDSerializer',
'InvoiceSerializer',
'KeywordSerializer',
Expand Down
19 changes: 18 additions & 1 deletion akvo/rest/serializers/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,30 @@
# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.


from akvo.rsr.models import IndicatorPeriod, Indicator
from akvo.rsr.models import (Indicator, IndicatorPeriod, IndicatorPeriodData,
IndicatorPeriodDataComment)

from .rsr_serializer import BaseRSRSerializer

class IndicatorPeriodDataCommentSerializer(BaseRSRSerializer):

class Meta:
model = IndicatorPeriodDataComment


class IndicatorPeriodDataSerializer(BaseRSRSerializer):

comments = IndicatorPeriodDataCommentSerializer(many=True, required=False,
allow_add_remove=True)

class Meta:
model = IndicatorPeriodData


class IndicatorPeriodSerializer(BaseRSRSerializer):

data = IndicatorPeriodDataSerializer(many=True, required=False, allow_add_remove=True)

class Meta:
model = IndicatorPeriod

Expand Down
7 changes: 5 additions & 2 deletions akvo/rest/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from .employment import EmploymentViewSet, approve_employment, set_group
from .focus_area import FocusAreaViewSet
from .goal import GoalViewSet
from .indicator import IndicatorViewSet, IndicatorPeriodViewSet
from .indicator import (IndicatorViewSet, IndicatorPeriodViewSet, IndicatorPeriodDataViewSet,
IndicatorPeriodDataCommentViewSet)
from .internal_organisation_id import InternalOrganisationIDViewSet
from .invoice import InvoiceViewSet
from .keyword import KeywordViewSet
Expand Down Expand Up @@ -83,8 +84,10 @@
'EmploymentViewSet',
'FocusAreaViewSet',
'GoalViewSet',
'IndicatorPeriodViewSet',
'IndicatorViewSet',
'IndicatorPeriodViewSet',
'IndicatorPeriodDataViewSet',
'IndicatorPeriodDataCommentViewSet',
'InternalOrganisationIDViewSet',
'invite_user',
'InvoiceViewSet',
Expand Down
21 changes: 20 additions & 1 deletion akvo/rest/views/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.


from akvo.rsr.models import Indicator, IndicatorPeriod
from akvo.rsr.models import (Indicator, IndicatorPeriod, IndicatorPeriodData,
IndicatorPeriodDataComment)

from ..serializers import IndicatorSerializer, IndicatorPeriodSerializer
from ..viewsets import PublicProjectViewSet
Expand All @@ -27,3 +28,21 @@ class IndicatorPeriodViewSet(PublicProjectViewSet):
serializer_class = IndicatorPeriodSerializer
filter_fields = ('indicator', )
project_relation = 'indicator__result__project__'


class IndicatorPeriodDataViewSet(PublicProjectViewSet):
"""
"""
queryset = IndicatorPeriodData.objects.all()
serializer_class = IndicatorPeriodSerializer
filter_fields = ('period', 'user', 'relative_data', 'status', 'update_method')
project_relation = 'period__indicator__result__project__'


class IndicatorPeriodDataCommentViewSet(PublicProjectViewSet):
"""
"""
queryset = IndicatorPeriodDataComment.objects.all()
serializer_class = IndicatorPeriodSerializer
filter_fields = ('data', 'user')
project_relation = 'period__indicator__result__project__'
19 changes: 19 additions & 0 deletions akvo/rsr/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1244,3 +1244,22 @@ class ValidationSetAdmin(admin.ModelAdmin):
inlines = (ValidationInline, )

admin.site.register(get_model('rsr', 'ProjectEditorValidationSet'), ValidationSetAdmin)


class IndicatorPeriodDataCommentInline(admin.TabularInline):
model = get_model('rsr', 'IndicatorPeriodDataComment')

def get_extra(self, request, obj=None, **kwargs):
if obj:
return 1 if obj.comments.count() == 0 else 0
else:
return 1


class IndicatorPeriodDataAdmin(admin.ModelAdmin):
model = get_model('rsr', 'IndicatorPeriodData')
list_display = ('period', 'user', 'data', 'relative_data', 'status')
readonly_fields = ('created_at', 'last_modified_at')
inlines = (IndicatorPeriodDataCommentInline, )

admin.site.register(get_model('rsr', 'IndicatorPeriodData'), IndicatorPeriodDataAdmin)
62 changes: 62 additions & 0 deletions akvo/rsr/migrations/0050_auto_20160201_1513.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import sorl.thumbnail.fields
import akvo.rsr.fields
from django.conf import settings
import akvo.rsr.models.indicator


class Migration(migrations.Migration):

dependencies = [
('rsr', '0049_auto_20160128_1605'),
]

operations = [
migrations.CreateModel(
name='IndicatorPeriodData',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created_at', models.DateTimeField(db_index=True, auto_now_add=True, null=True)),
('last_modified_at', models.DateTimeField(db_index=True, auto_now=True, null=True)),
('relative_data', models.BooleanField(default=False, verbose_name='relative data')),
('data', akvo.rsr.fields.ValidXMLCharField(max_length=300, verbose_name='data')),
('status', akvo.rsr.fields.ValidXMLCharField(default=b'D', choices=[(b'D', 'draft'), (b'P', 'pending approval'), (b'R', 'return for revision'), (b'A', 'approved')], max_length=1, blank=True, verbose_name='status', db_index=True)),
('text', akvo.rsr.fields.ValidXMLTextField(verbose_name='text', blank=True)),
('photo', sorl.thumbnail.fields.ImageField(upload_to=akvo.rsr.models.indicator.image_path, verbose_name='photo', blank=True)),
('file', models.FileField(upload_to=akvo.rsr.models.indicator.file_path, verbose_name='file', blank=True)),
('update_method', akvo.rsr.fields.ValidXMLCharField(default=b'W', choices=[(b'W', 'web'), (b'M', 'mobile')], max_length=1, blank=True, verbose_name='update method', db_index=True)),
('period', models.ForeignKey(related_name='data', verbose_name='indicator period', to='rsr.IndicatorPeriod')),
('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'indicator period data',
'verbose_name_plural': 'indicator period data',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='IndicatorPeriodDataComment',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created_at', models.DateTimeField(db_index=True, auto_now_add=True, null=True)),
('last_modified_at', models.DateTimeField(db_index=True, auto_now=True, null=True)),
('comment', akvo.rsr.fields.ValidXMLTextField(verbose_name='comment', blank=True)),
('data', models.ForeignKey(related_name='comments', verbose_name='indicator period data', to='rsr.IndicatorPeriodData')),
('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'indicator period data comment',
'verbose_name_plural': 'indicator period data comments',
},
bases=(models.Model,),
),
migrations.AddField(
model_name='indicatorperiod',
name='locked',
field=models.BooleanField(default=True, db_index=True, verbose_name='locked'),
preserve_default=True,
),
]
43 changes: 43 additions & 0 deletions akvo/rsr/migrations/0051_auto_20160201_1534.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


def move_indicator_period_data(apps, schema_editor):
"""
Move the indicator period data from the ProjectUpdate model to the IndicatorPeriodData model
and delete the old indicator 'updates'.
"""
ProjectUpdate = apps.get_model('rsr', 'ProjectUpdate')
IndicatorPeriodData = apps.get_model('rsr', 'IndicatorPeriodData')

indicator_updates = ProjectUpdate.objects.exclude(indicator_period=None)
for update in indicator_updates:
# Create new indicater period data object
IndicatorPeriodData.objects.create(
period=update.indicator_period,
user=update.user,
relative_data=True,
data=str(update.period_update),
text=update.text,
photo=update.photo,
update_method=update.update_method,
created_at=update.created_at,
last_modified_at=update.last_modified_at,
)
# Delete all (old) indicator 'updates'
indicator_updates.delete()


class Migration(migrations.Migration):

dependencies = [
('rsr', '0050_auto_20160201_1513'),
]

operations = [
migrations.RunPython(
move_indicator_period_data
),
]
22 changes: 22 additions & 0 deletions akvo/rsr/migrations/0052_auto_20160201_1556.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('rsr', '0051_auto_20160201_1534'),
]

operations = [
migrations.RemoveField(
model_name='projectupdate',
name='indicator_period',
),
migrations.RemoveField(
model_name='projectupdate',
name='period_update',
),
]
4 changes: 3 additions & 1 deletion akvo/rsr/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from .iati_import import IatiImport
from .iati_import_job import IatiImportJob, CordaidZipIatiImportJob
from .iati_import_log import IatiImportLog
from .indicator import Indicator, IndicatorPeriod
from .indicator import Indicator, IndicatorPeriod, IndicatorPeriodData, IndicatorPeriodDataComment
from .invoice import Invoice
from .internal_organisation_id import InternalOrganisationID
from .keyword import Keyword
Expand Down Expand Up @@ -91,6 +91,8 @@
'IatiImportLog',
'Indicator',
'IndicatorPeriod',
'IndicatorPeriodData',
'IndicatorPeriodDataComment',
'Invoice',
'InternalOrganisationID',
'Keyword',
Expand Down
4 changes: 2 additions & 2 deletions akvo/rsr/models/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

class Indicator(models.Model):
result = models.ForeignKey('Result', verbose_name=_(u'result'), related_name='indicators')
locked = models.BooleanField(_(u'locked'), default=True)
title = ValidXMLCharField(
_(u'indicator title'), blank=True, max_length=500,
help_text=_(u'Within each result indicators can be defined. Indicators should be items '
Expand Down Expand Up @@ -204,6 +203,7 @@ class Meta:

class IndicatorPeriod(models.Model):
indicator = models.ForeignKey(Indicator, verbose_name=_(u'indicator'), related_name='periods')
locked = models.BooleanField(_(u'locked'), default=True, db_index=True)
period_start = models.DateField(
_(u'period start'), null=True, blank=True,
help_text=_(u'The start date of the reporting period for this indicator.')
Expand Down Expand Up @@ -584,7 +584,7 @@ def delete(self, *args, **kwargs):
deleted, because it could lead to strange scenarios.
"""
if self.status == self.STATUS_APPROVED:
raise FieldError(unicode(_(u'It is not possible to delete an approved update')))
raise FieldError(unicode(_(u'It is not possible to delete an approved data update')))
super(IndicatorPeriodData, self).delete(*args, **kwargs)


Expand Down
52 changes: 0 additions & 52 deletions akvo/rsr/models/project_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,65 +68,13 @@ class ProjectUpdate(TimestampsMixin, models.Model):
help_text=_(u'Universally unique ID set by creating user agent'))
notes = ValidXMLTextField(verbose_name=_(u"Notes and comments"), blank=True, default='')

# Indicator updates
indicator_period = models.ForeignKey('IndicatorPeriod', related_name='updates',
verbose_name=_(u'indicator period'), blank=True, null=True)
period_update = models.DecimalField(_(u'period update'), blank=True, null=True, max_digits=14,
decimal_places=2)

class Meta:
app_label = 'rsr'
get_latest_by = "created_at"
verbose_name = _(u'project update')
verbose_name_plural = _(u'project updates')
ordering = ['-id', ]

def save(self, *args, **kwargs):
if self.indicator_period and self.period_update:
if not self.pk:
# Newly created update to indicator period, update the actual value.
self.indicator_period.update_actual_value(self.period_update)

else:
# Update to already existing indicator period, check if values have been changed.
orig_update = ProjectUpdate.objects.get(pk=self.pk)
if orig_update.indicator_period != self.indicator_period:
# Indicator period has changed. Substract value from old period, and add new
# value to new period.
try:
orig_update.update_actual_value(Decimal(orig_update.period_update) * -1)
except (InvalidOperation, TypeError):
pass
self.indicator_period.update_actual_value(self.period_update)

elif orig_update.period_update != self.period_update:
# Indicator value has changed. Add the difference to it.
try:
self.indicator_period.update_actual_value(
Decimal(self.period_update) - Decimal(orig_update.period_update)
)
except (InvalidOperation, TypeError):
self.indicator_period.update_actual_value(self.period_update)

super(ProjectUpdate, self).save(*args, **kwargs)

def delete(self, *args, **kwargs):
if self.indicator_period and self.period_update:
# Subsctract the value of the update from the actual value of the indicator period
if ProjectUpdate.objects.filter(indicator_period=self.indicator_period).\
exclude(pk=self.pk).exists():
try:
self.indicator_period.update_actual_value(
Decimal(self.period_update) * -1
)
except (InvalidOperation, TypeError):
pass
else:
# There's no other updates for this indicator period, remove actual value
self.indicator_period.actual_value = ''
self.indicator_period.save()
super(ProjectUpdate, self).delete(*args, **kwargs)

def img(self, value=''):
try:
return self.photo.thumbnail_tag
Expand Down

0 comments on commit 8d57677

Please sign in to comment.