Add snapshot migration for userstories to avoid unnecessary history entries

remotes/origin/3.4.0rc
Álex Hermida 2018-04-19 13:47:15 +02:00
parent 6faed218ba
commit 76414c75d4
4 changed files with 50 additions and 4 deletions

View File

@ -93,7 +93,8 @@ def _common_users_values(diff):
if "assigned_to" in diff: if "assigned_to" in diff:
users.update(diff["assigned_to"]) users.update(diff["assigned_to"])
if "assigned_users" in diff: if "assigned_users" in diff:
[users.update(usrs_id) if usrs_id else None for usrs_id in diff["assigned_users"]] [users.update(usrs_ids) for usrs_ids in diff["assigned_users"] if
usrs_ids]
if users: if users:
values["users"] = _get_users_values(users) values["users"] = _get_users_values(users)
@ -335,6 +336,12 @@ def userstory_freezer(us) -> dict:
for rp in rpqsd: for rp in rpqsd:
points[str(rp.role_id)] = rp.points_id points[str(rp.role_id)] = rp.points_id
assigned_users = [u.id for u in us.assigned_users.all()]
# Due to multiple assignment migration, for new snapshots we add to
# assigned users a list with the 'assigned to' value
if us.assigned_to_id and not assigned_users:
assigned_users = [us.assigned_to_id]
snapshot = { snapshot = {
"ref": us.ref, "ref": us.ref,
"owner": us.owner_id, "owner": us.owner_id,
@ -348,7 +355,7 @@ def userstory_freezer(us) -> dict:
"description": us.description, "description": us.description,
"description_html": mdrender(us.project, us.description), "description_html": mdrender(us.project, us.description),
"assigned_to": us.assigned_to_id, "assigned_to": us.assigned_to_id,
"assigned_users": [u.id for u in us.assigned_users.all()], "assigned_users": assigned_users,
"milestone": us.milestone_id, "milestone": us.milestone_id,
"client_requirement": us.client_requirement, "client_requirement": us.client_requirement,
"team_requirement": us.team_requirement, "team_requirement": us.team_requirement,

View File

@ -229,6 +229,31 @@ def get_excluded_fields(typename: str) -> tuple:
return _deprecated_fields.get(typename, ()) return _deprecated_fields.get(typename, ())
def migrate_userstory_diff(obj: FrozenObj) -> FrozenObj:
# Due to multiple assignment migration, for old snapshots we add a list
# with the 'assigned to' value
if 'assigned_users' not in obj.snapshot.keys():
snapshot = deepcopy(obj.snapshot)
snapshot['assigned_users'] = [obj.snapshot['assigned_to']]
obj = FrozenObj(obj.key, snapshot)
return obj
_migrations = {"userstories.userstory": migrate_userstory_diff}
def migrate_to_last_version(typename: str, obj: FrozenObj) -> FrozenObj:
"""""
Adapt old snapshots to the last format in order to generate correct diffs.
:param typename:
:param obj:
:return:
"""
return _migrations.get(typename, lambda x: x)(obj)
def make_diff(oldobj: FrozenObj, newobj: FrozenObj, def make_diff(oldobj: FrozenObj, newobj: FrozenObj,
excluded_keys: tuple = ()) -> FrozenDiff: excluded_keys: tuple = ()) -> FrozenDiff:
""" """
@ -342,6 +367,10 @@ def take_snapshot(obj: object, *, comment: str="", user=None,
new_fobj = freeze_model_instance(obj) new_fobj = freeze_model_instance(obj)
old_fobj, need_real_snapshot = get_last_snapshot_for_key(key) old_fobj, need_real_snapshot = get_last_snapshot_for_key(key)
# migrate diff to latest schema
if old_fobj:
old_fobj = migrate_to_last_version(typename, old_fobj)
entry_model = apps.get_model("history", "HistoryEntry") entry_model = apps.get_model("history", "HistoryEntry")
user_id = None if user is None else user.id user_id = None if user is None else user.id
user_name = "" if user is None else user.get_full_name() user_name = "" if user is None else user.get_full_name()
@ -358,6 +387,7 @@ def take_snapshot(obj: object, *, comment: str="", user=None,
raise RuntimeError("Unexpected condition") raise RuntimeError("Unexpected condition")
excluded_fields = get_excluded_fields(typename) excluded_fields = get_excluded_fields(typename)
fdiff = make_diff(old_fobj, new_fobj, excluded_fields) fdiff = make_diff(old_fobj, new_fobj, excluded_fields)
# If diff and comment are empty, do # If diff and comment are empty, do

View File

@ -90,7 +90,16 @@ class UserStoryListSerializer(ProjectExtraInfoSerializerMixin,
:return: User queryset object representing the assigned users :return: User queryset object representing the assigned users
""" """
return [user.id for user in obj.assigned_users.all()] if not obj.assigned_to:
return set([user.id for user in obj.assigned_users.all()])
assigned_users = [user.id for user in obj.assigned_users.all()] + \
[obj.assigned_to.id]
if not assigned_users:
return None
return set(assigned_users)
def get_epic_order(self, obj): def get_epic_order(self, obj):
include_epic_order = getattr(obj, "include_epic_order", False) include_epic_order = getattr(obj, "include_epic_order", False)

View File

@ -104,7 +104,7 @@ def test_create_userstory_with_assigned_users(client):
response = client.json.post(url, json_data) response = client.json.post(url, json_data)
assert response.status_code == 201 assert response.status_code == 201
assert response.data["assigned_users"] == [user.id, user_watcher.id] assert response.data["assigned_users"] == set([user.id, user_watcher.id])
def test_create_userstory_with_watchers(client): def test_create_userstory_with_watchers(client):