Implemented the ability of mark some history entries as hidden.
Snapshots with order fields (that are considered not important for preview) are automatically marked as hidden.remotes/origin/enhancement/email-actions
parent
92276a1589
commit
23f349a73f
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('history', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='historyentry',
|
||||
name='is_hidden',
|
||||
field=models.BooleanField(default=False),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
|
@ -48,8 +48,6 @@ class HistoryEntry(models.Model):
|
|||
user = JsonField(blank=True, default=None, null=True)
|
||||
created_at = models.DateTimeField(default=timezone.now)
|
||||
type = models.SmallIntegerField(choices=HISTORY_TYPE_CHOICES)
|
||||
is_snapshot = models.BooleanField(default=False)
|
||||
|
||||
key = models.CharField(max_length=255, null=True, default=None, blank=True)
|
||||
|
||||
# Stores the last diff
|
||||
|
@ -68,9 +66,15 @@ class HistoryEntry(models.Model):
|
|||
delete_comment_date = models.DateTimeField(null=True, blank=True, default=None)
|
||||
delete_comment_user = JsonField(blank=True, default=None, null=True)
|
||||
|
||||
@cached_property
|
||||
def is_comment(self):
|
||||
return self.type == HistoryType.comment
|
||||
# Flag for mark some history entries as
|
||||
# hidden. Hidden history entries are important
|
||||
# for save but not important to preview.
|
||||
# Order fields are the good example of this fields.
|
||||
is_hidden = models.BooleanField(default=False)
|
||||
|
||||
# Flag for mark some history entries as complete
|
||||
# snapshot. The rest are partial snapshot.
|
||||
is_snapshot = models.BooleanField(default=False)
|
||||
|
||||
@cached_property
|
||||
def owner(self):
|
||||
|
|
|
@ -55,6 +55,13 @@ _freeze_impl_map = {}
|
|||
# Dict containing registred containing with their values implementation.
|
||||
_values_impl_map = {}
|
||||
|
||||
# Not important fields for models (history entries with only
|
||||
# this fields are marked as hidden).
|
||||
_not_important_fields = {
|
||||
"userstories.userstory": frozenset(["backlog_order", "sprint_order", "kanban_order"]),
|
||||
"tasks.task": frozenset(["us_order", "taskboard_order"]),
|
||||
}
|
||||
|
||||
log = logging.getLogger("taiga.history")
|
||||
|
||||
|
||||
|
@ -130,7 +137,30 @@ def freeze_model_instance(obj:object) -> FrozenObj:
|
|||
|
||||
key = make_key_from_model_object(obj)
|
||||
impl_fn = _freeze_impl_map[typename]
|
||||
return FrozenObj(key, impl_fn(obj))
|
||||
snapshot = impl_fn(obj)
|
||||
assert isinstance(snapshot, dict), "freeze handlers should return always a dict"
|
||||
|
||||
return FrozenObj(key, snapshot)
|
||||
|
||||
|
||||
def is_hidden_snapshot(obj:FrozenDiff) -> bool:
|
||||
"""
|
||||
Check if frozen object is considered
|
||||
hidden or not.
|
||||
"""
|
||||
content_type, pk = obj.key.rsplit(":", 1)
|
||||
snapshot_fields = frozenset(obj.diff.keys())
|
||||
|
||||
if content_type not in _not_important_fields:
|
||||
return False
|
||||
|
||||
nfields = _not_important_fields[content_type]
|
||||
result = snapshot_fields - nfields
|
||||
|
||||
if len(result) == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def make_diff(oldobj:FrozenObj, newobj:FrozenObj) -> FrozenDiff:
|
||||
|
@ -235,20 +265,7 @@ def take_snapshot(obj:object, *, comment:str="", user=None, delete:bool=False):
|
|||
else:
|
||||
raise RuntimeError("Unexpected condition")
|
||||
|
||||
kwargs = {
|
||||
"user": {"pk": user_id, "name": user_name},
|
||||
"key": key,
|
||||
"type": entry_type,
|
||||
"comment": "",
|
||||
"comment_html": "",
|
||||
"diff": None,
|
||||
"values": None,
|
||||
"snapshot": None,
|
||||
"is_snapshot": False,
|
||||
}
|
||||
|
||||
fdiff = make_diff(old_fobj, new_fobj)
|
||||
fvals = make_diff_values(typename, fdiff)
|
||||
|
||||
# If diff and comment are empty, do
|
||||
# not create empty history entry
|
||||
|
@ -258,21 +275,29 @@ def take_snapshot(obj:object, *, comment:str="", user=None, delete:bool=False):
|
|||
|
||||
return None
|
||||
|
||||
kwargs.update({
|
||||
fvals = make_diff_values(typename, fdiff)
|
||||
is_hidden = is_hidden_snapshot(fdiff)
|
||||
|
||||
kwargs = {
|
||||
"user": {"pk": user_id, "name": user_name},
|
||||
"key": key,
|
||||
"type": entry_type,
|
||||
"snapshot": fdiff.snapshot if need_real_snapshot else None,
|
||||
"is_snapshot": need_real_snapshot,
|
||||
"diff": fdiff.diff,
|
||||
"values": fvals,
|
||||
"comment": comment,
|
||||
"diff": fdiff.diff,
|
||||
"comment_html": mdrender(obj.project, comment),
|
||||
})
|
||||
"is_hidden": is_hidden,
|
||||
"is_snapshot": need_real_snapshot,
|
||||
}
|
||||
|
||||
return entry_model.objects.create(**kwargs)
|
||||
|
||||
|
||||
# High level query api
|
||||
|
||||
def get_history_queryset_by_model_instance(obj:object, types=(HistoryType.change,)):
|
||||
def get_history_queryset_by_model_instance(obj:object, types=(HistoryType.change,),
|
||||
include_hidden=False):
|
||||
"""
|
||||
Get one page of history for specified object.
|
||||
"""
|
||||
|
@ -280,6 +305,9 @@ def get_history_queryset_by_model_instance(obj:object, types=(HistoryType.change
|
|||
history_entry_model = get_model("history", "HistoryEntry")
|
||||
|
||||
qs = history_entry_model.objects.filter(key=key, type__in=types)
|
||||
if not include_hidden:
|
||||
qs = qs.filter(is_hidden=False)
|
||||
|
||||
return qs.order_by("created_at")
|
||||
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ def test_take_two_snapshots_with_changes():
|
|||
|
||||
qs_all = HistoryEntry.objects.all()
|
||||
qs_created = qs_all.filter(type=HistoryType.create)
|
||||
qs_hidden = qs_all.filter(is_hidden=True)
|
||||
|
||||
assert qs_all.count() == 0
|
||||
|
||||
|
@ -62,6 +63,7 @@ def test_take_two_snapshots_with_changes():
|
|||
services.take_snapshot(issue, user=issue.owner)
|
||||
assert qs_all.count() == 2
|
||||
assert qs_created.count() == 1
|
||||
assert qs_hidden.count() == 0
|
||||
|
||||
|
||||
def test_take_two_snapshots_without_changes():
|
||||
|
@ -69,6 +71,7 @@ def test_take_two_snapshots_without_changes():
|
|||
|
||||
qs_all = HistoryEntry.objects.all()
|
||||
qs_created = qs_all.filter(type=HistoryType.create)
|
||||
qs_hidden = qs_all.filter(is_hidden=True)
|
||||
|
||||
assert qs_all.count() == 0
|
||||
|
||||
|
@ -79,7 +82,7 @@ def test_take_two_snapshots_without_changes():
|
|||
|
||||
assert qs_all.count() == 1
|
||||
assert qs_created.count() == 1
|
||||
|
||||
assert qs_hidden.count() == 0
|
||||
|
||||
def test_take_snapshot_from_deleted_object():
|
||||
issue = f.IssueFactory.create()
|
||||
|
@ -174,3 +177,23 @@ def test_issue_resource_history_test(client):
|
|||
assert qs_created.count() == 1
|
||||
assert qs_changed.count() == 0
|
||||
assert qs_deleted.count() == 1
|
||||
|
||||
|
||||
def test_take_hidden_snapshot():
|
||||
task = f.TaskFactory.create()
|
||||
|
||||
qs_all = HistoryEntry.objects.all()
|
||||
qs_hidden = qs_all.filter(is_hidden=True)
|
||||
|
||||
assert qs_all.count() == 0
|
||||
|
||||
# Two snapshots with modification should
|
||||
# generate two snapshots.
|
||||
services.take_snapshot(task, user=task.owner)
|
||||
task.us_order = 3
|
||||
task.save()
|
||||
|
||||
services.take_snapshot(task, user=task.owner)
|
||||
assert qs_all.count() == 2
|
||||
assert qs_hidden.count() == 1
|
||||
|
||||
|
|
Loading…
Reference in New Issue