diff --git a/taiga/export_import/api.py b/taiga/export_import/api.py index e9da52c02..fe422836a 100644 --- a/taiga/export_import/api.py +++ b/taiga/export_import/api.py @@ -33,6 +33,7 @@ from taiga.base.api.viewsets import GenericViewSet from taiga.projects.models import Project, Membership from taiga.projects.issues.models import Issue +from taiga.projects.tasks.models import Task from taiga.projects.serializers import ProjectSerializer from . import mixins @@ -238,6 +239,9 @@ def task(self, request, *args, **kwargs): project = self.get_object_or_none() self.check_permissions(request, 'import_item', project) + signals.pre_save.disconnect(sender=Task, + dispatch_uid="set_finished_date_when_edit_task") + task = service.store_task(project, request.DATA.copy()) errors = service.get_errors() diff --git a/taiga/projects/tasks/apps.py b/taiga/projects/tasks/apps.py index d495c9645..a38947e64 100644 --- a/taiga/projects/tasks/apps.py +++ b/taiga/projects/tasks/apps.py @@ -23,6 +23,10 @@ from . import signals as handlers def connect_tasks_signals(): + # Finished date + signals.pre_save.connect(handlers.set_finished_date_when_edit_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="set_finished_date_when_edit_task") # Tags signals.pre_save.connect(generic_handlers.tags_normalization, sender=apps.get_model("tasks", "Task"), diff --git a/taiga/projects/tasks/migrations/0009_auto_20151104_1131.py b/taiga/projects/tasks/migrations/0009_auto_20151104_1131.py new file mode 100644 index 000000000..da11ea7b3 --- /dev/null +++ b/taiga/projects/tasks/migrations/0009_auto_20151104_1131.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import connection, migrations, models + +def set_finished_date_for_tasks(apps, schema_editor): + # Updates the finished date from tasks according to the history_entries associated + # It takes the last history change updateing the status of a task and if it's a closed + # one it updates the finished_date attribute + sql=""" +WITH status_update AS( + WITH status_update AS( + WITH history_entries AS ( + SELECT + diff#>>'{status, 1}' new_status_id, + regexp_split_to_array(key, ':') as split_key, + created_at as date + FROM history_historyentry + WHERE diff#>>'{status, 1}' != '' + ) + SELECT + split_key[2] as object_id, + new_status_id::int, + MAX(date) as status_change_datetime + FROM history_entries + WHERE split_key[1] = 'tasks.task' + GROUP BY object_id, new_status_id, date + ) + SELECT status_update.* + FROM status_update + INNER JOIN projects_taskstatus + ON projects_taskstatus.id = new_status_id AND projects_taskstatus.is_closed = True +) +UPDATE tasks_task +SET finished_date = status_update.status_change_datetime +FROM status_update +WHERE tasks_task.id = status_update.object_id::int + """ + cursor = connection.cursor() + cursor.execute(sql) + +class Migration(migrations.Migration): + + dependencies = [ + ('tasks', '0008_remove_task_watchers'), + ] + + operations = [ + migrations.RunPython(set_finished_date_for_tasks), + ] diff --git a/taiga/projects/tasks/signals.py b/taiga/projects/tasks/signals.py index 07a7738d0..f45de6214 100644 --- a/taiga/projects/tasks/signals.py +++ b/taiga/projects/tasks/signals.py @@ -16,6 +16,7 @@ from contextlib import suppress from django.core.exceptions import ObjectDoesNotExist +from django.utils import timezone #################################### # Signals for cached prev task @@ -92,3 +93,13 @@ def _try_to_close_milestone_when_delete_task(instance): with suppress(ObjectDoesNotExist): if instance.milestone_id and services.calculate_milestone_is_closed(instance.milestone): services.close_milestone(instance.milestone) + +#################################### +# Signals for set finished date +#################################### + +def set_finished_date_when_edit_task(sender, instance, **kwargs): + if instance.status.is_closed and not instance.finished_date: + instance.finished_date = timezone.now() + elif not instance.status.is_closed and instance.finished_date: + instance.finished_date = None