Merge "Create timeline events for boards and worklists"

This commit is contained in:
Jenkins 2016-08-18 16:50:41 +00:00 committed by Gerrit Code Review
commit 064fc81e9a
7 changed files with 633 additions and 36 deletions

View File

@ -13,12 +13,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from oslo_config import cfg
from pecan import abort
from pecan import request
from pecan import response
from pecan import rest
from pecan.secure import secure
import six
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
@ -27,6 +30,8 @@ from storyboard.api.v1 import wmodels
from storyboard.common import decorators
from storyboard.common import exception as exc
from storyboard.db.api import boards as boards_api
from storyboard.db.api import timeline_events as events_api
from storyboard.db.api import users as users_api
from storyboard.db.api import worklists as worklists_api
from storyboard.openstack.common.gettextutils import _ # noqa
@ -34,6 +39,13 @@ from storyboard.openstack.common.gettextutils import _ # noqa
CONF = cfg.CONF
def serialize_lane(lane):
return {
"worklist_id": lane.list_id,
"position": lane.position
}
def get_lane(list_id, board):
for lane in board['lanes']:
if lane.list_id == list_id:
@ -51,17 +63,66 @@ def update_lanes(board_dict, board_id):
new_lane = get_lane(lane.list_id, board_dict)
if lane.position != new_lane.position:
del new_lane.worklist
original = copy.deepcopy(lane)
boards_api.update_lane(
board, lane, new_lane.as_dict(omit_unset=True))
updated = {
"old": serialize_lane(original),
"new": serialize_lane(new_lane)
}
events_api.board_lanes_changed_event(board_id,
request.current_user_id,
updated=updated)
for lane in board_dict['lanes']:
if lane.list_id not in existing_list_ids:
lane.worklist = None
boards_api.add_lane(board, lane.as_dict(omit_unset=True))
events_api.board_lanes_changed_event(board_id,
request.current_user_id,
added=serialize_lane(lane))
board = boards_api.get(board_id)
del board_dict['lanes']
def post_timeline_events(original, updated):
author_id = request.current_user_id
if original.title != updated.title:
events_api.board_details_changed_event(
original.id,
author_id,
'title',
original.title,
updated.title)
if original.description != updated.description:
events_api.board_details_changed_event(
original.id,
author_id,
'description',
original.description,
updated.description)
if original.private != updated.private:
events_api.board_details_changed_event(
original.id,
author_id,
'private',
original.private,
updated.private)
if original.archived != updated.archived:
events_api.board_details_changed_event(
original.id,
author_id,
'archived',
original.archived,
updated.archived)
class PermissionsController(rest.RestController):
"""Manages operations on board permissions."""
@ -91,9 +152,18 @@ class PermissionsController(rest.RestController):
:param permission: The dict to use to create the permission.
"""
if boards_api.editable(boards_api.get(board_id),
request.current_user_id):
return boards_api.create_permission(board_id)
user_id = request.current_user_id
if boards_api.editable(boards_api.get(board_id), user_id):
created = boards_api.create_permission(board_id, permission)
users = [{user.id: user.full_name} for user in created.users]
events_api.board_permission_created_event(board_id,
user_id,
created.id,
created.codename,
users)
return created
else:
raise exc.NotFound(_("Board %s not found") % board_id)
@ -108,9 +178,36 @@ class PermissionsController(rest.RestController):
:param permission: The new contents of the permission.
"""
if boards_api.editable(boards_api.get(board_id),
request.current_user_id):
return boards_api.update_permission(board_id, permission).codename
user_id = request.current_user_id
board = boards_api.get(board_id)
old = None
for perm in board.permissions:
if perm.codename == permission['codename']:
old = perm
if old is None:
raise exc.NotFound(_("Permission with codename %s not found")
% permission['codename'])
old_users = {user.id: user.full_name for user in old.users}
if boards_api.editable(board, user_id):
updated = boards_api.update_permission(board_id, permission)
new_users = {user.id: user.full_name for user in updated.users}
added = [{id: name} for id, name in six.iteritems(new_users)
if id not in old_users]
removed = [{id: name} for id, name in six.iteritems(old_users)
if id not in new_users]
if added or removed:
events_api.board_permissions_changed_event(board.id,
user_id,
updated.id,
updated.codename,
added,
removed)
else:
raise exc.NotFound(_("Board %s not found") % board_id)
@ -233,8 +330,15 @@ class BoardsController(rest.RestController):
del board_dict['due_dates']
created_board = boards_api.create(board_dict)
events_api.board_created_event(created_board.id,
user_id,
created_board.title,
created_board.description)
for lane in lanes:
boards_api.add_lane(created_board, lane.as_dict())
events_api.board_lanes_changed_event(created_board.id,
user_id,
added=serialize_lane(lane))
edit_permission = {
'name': 'edit_board_%d' % created_board.id,
@ -246,8 +350,22 @@ class BoardsController(rest.RestController):
'codename': 'move_cards',
'users': users
}
boards_api.create_permission(created_board.id, edit_permission)
boards_api.create_permission(created_board.id, move_permission)
edit = boards_api.create_permission(created_board.id, edit_permission)
move = boards_api.create_permission(created_board.id, move_permission)
event_owners = [{id: users_api.user_get(id).full_name}
for id in owners]
event_users = [{id: users_api.user_get(id).full_name}
for id in users]
events_api.board_permission_created_event(created_board.id,
user_id,
edit.id,
edit.codename,
event_owners)
events_api.board_permission_created_event(created_board.id,
user_id,
move.id,
move.codename,
event_users)
return wmodels.Board.from_db_model(created_board)
@ -262,9 +380,15 @@ class BoardsController(rest.RestController):
"""
user_id = request.current_user_id
if not boards_api.editable(boards_api.get(id), user_id):
original = boards_api.get(id)
if not boards_api.editable(original, user_id):
raise exc.NotFound(_("Board %s not found") % id)
# We use copy here because we only need to check changes
# to the related objects, just the board's own attributes.
# Also, deepcopy trips up on the lanes' backrefs.
original = copy.copy(original)
board_dict = board.as_dict(omit_unset=True)
update_lanes(board_dict, id)
@ -274,6 +398,8 @@ class BoardsController(rest.RestController):
updated_board = boards_api.update(id, board_dict)
post_timeline_events(original, updated_board)
if boards_api.visible(updated_board, user_id):
board_model = wmodels.Board.from_db_model(updated_board)
board_model.resolve_lanes(updated_board)
@ -296,9 +422,21 @@ class BoardsController(rest.RestController):
if not boards_api.editable(board, user_id):
raise exc.NotFound(_("Board %s not found") % id)
boards_api.update(id, {"archived": True})
# We use copy here because we only need to check changes
# to the related objects, just the board's own attributes.
# Also, deepcopy trips up on the lanes' backrefs.
original = copy.copy(board)
updated = boards_api.update(id, {"archived": True})
post_timeline_events(original, updated)
for lane in board.lanes:
original = copy.deepcopy(worklists_api.get(lane.worklist.id))
worklists_api.update(lane.worklist.id, {"archived": True})
if not original.archived:
events_api.worklist_details_changed_event(
lane.worklist.id, user_id, 'archived', original.archived,
True)
permissions = PermissionsController()

View File

@ -13,12 +13,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from oslo_config import cfg
from pecan import abort
from pecan import request
from pecan import response
from pecan import rest
from pecan.secure import secure
import six
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
@ -28,6 +31,8 @@ from storyboard.common import decorators
from storyboard.common import exception as exc
from storyboard.db.api import stories as stories_api
from storyboard.db.api import tasks as tasks_api
from storyboard.db.api import timeline_events as events_api
from storyboard.db.api import users as users_api
from storyboard.db.api import worklists as worklists_api
from storyboard.db import models
from storyboard.openstack.common.gettextutils import _ # noqa
@ -36,6 +41,58 @@ from storyboard.openstack.common.gettextutils import _ # noqa
CONF = cfg.CONF
def serialize_filter(filter):
serialized = {
"id": filter.id,
"type": filter.type,
"criteria": []
}
for criterion in filter.criteria:
serialized['criteria'].append({
"title": criterion.title,
"negative": criterion.negative,
"value": criterion.value,
"field": criterion.field
})
return serialized
def post_timeline_events(original, updated):
author_id = request.current_user_id
if original.title != updated.title:
events_api.worklist_details_changed_event(
original.id,
author_id,
'title',
original.title,
updated.title)
if original.private != updated.private:
events_api.worklist_details_changed_event(
original.id,
author_id,
'private',
original.private,
updated.private)
if original.automatic != updated.automatic:
events_api.worklist_details_changed_event(
original.id,
author_id,
'automatic',
original.automatic,
updated.automatic)
if original.archived != updated.archived:
events_api.worklist_details_changed_event(
original.id,
author_id,
'archived',
original.archived,
updated.archived)
class PermissionsController(rest.RestController):
"""Manages operations on worklist permissions."""
@ -75,9 +132,18 @@ class PermissionsController(rest.RestController):
:param permission: The dict to use to create the permission.
"""
if worklists_api.editable(worklists_api.get(worklist_id),
request.current_user_id):
return worklists_api.create_permission(worklist_id)
user_id = request.current_user_id
if worklists_api.editable(worklists_api.get(worklist_id), user_id):
created = worklists_api.create_permission(worklist_id, permission)
users = [{user.id: user.full_name} for user in created.users]
events_api.worklist_permission_created_event(worklist_id,
user_id,
created.id,
created.codename,
users)
return created.codename
else:
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
@ -92,14 +158,58 @@ class PermissionsController(rest.RestController):
TODO
This takes a dict in the form::
{
"codename": "my-permission",
"users": [
1,
2,
3
]
}
The given codename must match an existing permission's
codename.
:param worklist_id: The ID of the worklist.
:param permission: The new contents of the permission.
"""
if worklists_api.editable(worklists_api.get(worklist_id),
request.current_user_id):
return worklists_api.update_permission(
worklist_id, permission).codename
user_id = request.current_user_id
worklist = worklists_api.get(worklist_id)
old = None
for perm in worklist.permissions:
if perm.codename == permission['codename']:
old = perm
if old is None:
raise exc.NotFound(_("Permission with codename %s not found")
% permission['codename'])
old_users = {user.id: user.full_name for user in old.users}
if worklists_api.editable(worklist, user_id):
updated = worklists_api.update_permission(
worklist_id, permission)
new_users = {user.id: user.full_name for user in updated.users}
added = [{id: name} for id, name in six.iteritems(new_users)
if id not in old_users]
removed = [{id: name} for id, name in six.iteritems(old_users)
if id not in new_users]
if added or removed:
events_api.worklist_permissions_changed_event(
worklist_id,
user_id,
updated.id,
updated.codename,
added,
removed)
return updated.codename
else:
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
@ -179,6 +289,12 @@ class FilterSubcontroller(rest.RestController):
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
created = worklists_api.create_filter(worklist_id, filter.as_dict())
added = serialize_filter(created)
events_api.worklist_filters_changed_event(worklist_id,
user_id,
added=added)
model = wmodels.WorklistFilter.from_db_model(created)
model.resolve_criteria(created)
return model
@ -204,10 +320,23 @@ class FilterSubcontroller(rest.RestController):
if not worklists_api.editable(worklist, user_id):
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
old = serialize_filter(worklists_api.get_filter(filter_id))
update_dict = filter.as_dict(omit_unset=True)
updated = worklists_api.update_filter(filter_id, update_dict)
return wmodels.WorklistFilter.from_db_model(updated)
changes = {
"old": old,
"new": serialize_filter(updated)
}
events_api.worklist_filters_changed_event(worklist_id,
user_id,
updated=changes)
updated_model = wmodels.WorklistFilter.from_db_model(updated)
updated_model.resolve_criteria(updated)
return updated_model
@decorators.db_exceptions
@secure(checks.authenticated)
@ -228,6 +357,12 @@ class FilterSubcontroller(rest.RestController):
if not worklists_api.editable(worklist, user_id):
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
filter = serialize_filter(worklists_api.get_filter(filter_id))
events_api.worklist_filters_changed_event(worklist_id,
user_id,
removed=filter)
worklists_api.delete_filter(filter_id)
@ -303,6 +438,16 @@ class ItemsSubcontroller(rest.RestController):
id, item_id, item_type, list_position,
current_user=request.current_user_id)
added = {
"worklist_id": id,
"item_id": item_id,
"item_title": item.title,
"item_type": item_type,
"position": list_position
}
events_api.worklist_contents_changed_event(id, user_id, added=added)
return wmodels.WorklistItem.from_db_model(
worklists_api.get_item_at_position(id, list_position))
@ -346,6 +491,26 @@ class ItemsSubcontroller(rest.RestController):
raise exc.NotFound(_("Item %s refers to a non-existent task or "
"story.") % item_id)
old = {
"worklist_id": card.list_id,
"item_id": card.item_id,
"item_title": item.title,
"item_type": card.item_type,
"position": card.list_position,
"due_date_id": card.display_due_date
}
new = {
"item_id": card.item_id,
"item_title": item.title,
"item_type": card.item_type
}
if list_position != card.list_position and list_position is not None:
new['position'] = list_position
if list_id != card.list_id and list_id is not None:
new['worklist_id'] = list_id
worklists_api.move_item(id, item_id, list_position, list_id)
if display_due_date is not None:
@ -355,6 +520,15 @@ class ItemsSubcontroller(rest.RestController):
'display_due_date': display_due_date
}
worklists_api.update_item(item_id, update_dict)
new['due_date_id'] = display_due_date
updated = {
"old": old,
"new": new
}
events_api.worklist_contents_changed_event(id,
user_id,
updated=updated)
updated = worklists_api.get_item_by_id(item_id)
result = wmodels.WorklistItem.from_db_model(updated)
@ -379,13 +553,32 @@ class ItemsSubcontroller(rest.RestController):
worklist = worklists_api.get(id)
if not worklists_api.editable_contents(worklist, user_id):
raise exc.NotFound(_("Worklist %s not found") % id)
item = worklists_api.get_item_by_id(item_id)
if item is None:
card = worklists_api.get_item_by_id(item_id)
if card is None:
raise exc.NotFound(_("Item %s seems to have already been deleted,"
" try refreshing your page.") % item_id)
worklists_api.update_item(item_id, {'archived': True})
worklists_api.normalize_positions(worklist)
item = None
if card.item_type == 'story':
item = stories_api.story_get(
card.item_id, current_user=user_id)
elif card.item_type == 'task':
item = tasks_api.task_get(
card.item_id, current_user=user_id)
if item is None:
item.title = ''
removed = {
"worklist_id": id,
"item_id": card.item_id,
"item_title": item.title
}
events_api.worklist_contents_changed_event(id,
user_id,
removed=removed)
class WorklistsController(rest.RestController):
"""Manages operations on worklists."""
@ -553,6 +746,9 @@ class WorklistsController(rest.RestController):
users = []
created_worklist = worklists_api.create(worklist_dict)
events_api.worklist_created_event(created_worklist.id,
user_id,
created_worklist.title)
edit_permission = {
'name': 'edit_worklist_%d' % created_worklist.id,
@ -564,13 +760,35 @@ class WorklistsController(rest.RestController):
'codename': 'move_items',
'users': users
}
worklists_api.create_permission(created_worklist.id, edit_permission)
worklists_api.create_permission(created_worklist.id, move_permission)
edit = worklists_api.create_permission(
created_worklist.id, edit_permission)
move = worklists_api.create_permission(
created_worklist.id, move_permission)
event_owners = [{id: users_api.user_get(id).full_name}
for id in owners]
event_users = [{id: users_api.user_get(id).full_name}
for id in users]
events_api.worklist_permission_created_event(created_worklist.id,
user_id,
edit.id,
edit.codename,
event_owners)
events_api.worklist_permission_created_event(created_worklist.id,
user_id,
move.id,
move.codename,
event_users)
if worklist_dict['automatic']:
for filter in filters:
worklists_api.create_filter(created_worklist.id,
filter.as_dict())
created_filter = worklists_api.create_filter(
created_worklist.id, filter.as_dict())
added = serialize_filter(created_filter)
events_api.worklist_filters_changed_event(created_worklist.id,
user_id,
added=added)
return wmodels.Worklist.from_db_model(created_worklist)
@ -602,8 +820,10 @@ class WorklistsController(rest.RestController):
worklist_dict = worklist.as_dict(omit_unset=True)
original = copy.deepcopy(worklists_api.get(id))
updated_worklist = worklists_api.update(id, worklist_dict)
post_timeline_events(original, updated_worklist)
if worklists_api.visible(updated_worklist, user_id):
worklist_model = wmodels.Worklist.from_db_model(updated_worklist)
worklist_model.resolve_items(updated_worklist)
@ -630,11 +850,14 @@ class WorklistsController(rest.RestController):
"""
worklist = worklists_api.get(worklist_id)
original = copy.deepcopy(worklist)
user_id = request.current_user_id
if not worklists_api.editable(worklist, user_id):
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
worklists_api.update(worklist_id, {"archived": True})
updated = worklists_api.update(worklist_id, {"archived": True})
post_timeline_events(original, updated)
items = ItemsSubcontroller()
permissions = PermissionsController()

View File

@ -1,4 +1,5 @@
# Copyright (c) 2014 Mirantis Inc.
# Copyright (c) 2016 Codethink Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -32,6 +33,27 @@ TASK_PRIORITY_CHANGED = "task_priority_changed"
TASK_ASSIGNEE_CHANGED = "task_assignee_changed"
TASK_DELETED = "task_deleted"
WORKLIST_CREATED = "worklist_created"
# WORKLIST_DETAILS_CHANGED should occur when a value in any of the fields
# in the `worklists` database table is changed. Changes in related tables
# such as worklist_permissions, worklist_filters, and worklist_items have
# their own event types.
WORKLIST_DETAILS_CHANGED = "worklist_details_changed"
WORKLIST_PERMISSION_CREATED = "worklist_permission_created"
WORKLIST_PERMISSIONS_CHANGED = "worklist_permissions_changed"
WORKLIST_FILTERS_CHANGED = "worklist_filters_changed"
WORKLIST_CONTENTS_CHANGED = "worklist_contents_changed"
BOARD_CREATED = "board_created"
# BOARD_DETAILS_CHANGED should occur when a value in any of the fields
# in the `boards` database table is changed. Changes in related tables
# such as board_permissions, and board_worklists have their own event
# types.
BOARD_DETAILS_CHANGED = "board_details_changed"
BOARD_PERMISSION_CREATED = "board_permission_created"
BOARD_PERMISSIONS_CHANGED = "board_permissions_changed"
BOARD_LANES_CHANGED = "board_lanes_changed"
ALL = (
STORY_CREATED,
STORY_DETAILS_CHANGED,
@ -43,5 +65,16 @@ ALL = (
TASK_DETAILS_CHANGED,
TASK_STATUS_CHANGED,
TASK_PRIORITY_CHANGED,
TASK_DELETED
TASK_DELETED,
WORKLIST_CREATED,
WORKLIST_DETAILS_CHANGED,
WORKLIST_PERMISSION_CREATED,
WORKLIST_PERMISSIONS_CHANGED,
WORKLIST_FILTERS_CHANGED,
WORKLIST_CONTENTS_CHANGED,
BOARD_CREATED,
BOARD_DETAILS_CHANGED,
BOARD_PERMISSION_CREATED,
BOARD_PERMISSIONS_CHANGED,
BOARD_LANES_CHANGED
)

View File

@ -105,6 +105,8 @@ def subscription_get_all_subscriber_ids(resource, resource_id, session=None):
resource_id,
session=session)
if event:
if event.story_id is None:
return set()
resource = 'story'
resource_id = event.story_id
else:

View File

@ -1,4 +1,5 @@
# Copyright (c) 2014 Mirantis Inc.
# Copyright (c) 2016 Codethink Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -222,3 +223,196 @@ def tags_deleted_event(story_id, author_id, story_title, tags):
"event_type": event_types.TAGS_DELETED,
"event_info": json.dumps(event_info)
})
def worklist_created_event(worklist_id, author_id, worklist_title):
event_info = {
"worklist_id": worklist_id,
"author_id": author_id,
"worklist_title": worklist_title
}
return event_create({
"worklist_id": worklist_id,
"author_id": author_id,
"event_type": event_types.WORKLIST_CREATED,
"event_info": json.dumps(event_info)
})
def worklist_details_changed_event(worklist_id, author_id, updated, old, new):
event_info = {
"worklist_id": worklist_id,
"author_id": author_id,
"field": updated,
"old_value": old,
"new_value": new
}
return event_create({
"worklist_id": worklist_id,
"author_id": author_id,
"event_type": event_types.WORKLIST_DETAILS_CHANGED,
"event_info": json.dumps(event_info)
})
def worklist_permission_created_event(worklist_id, author_id, permission_id,
codename, users):
event_info = {
"worklist_id": worklist_id,
"permission_id": permission_id,
"author_id": author_id,
"codename": codename,
"users": users
}
return event_create({
"worklist_id": worklist_id,
"author_id": author_id,
"event_type": event_types.WORKLIST_PERMISSION_CREATED,
"event_info": json.dumps(event_info)
})
def worklist_permissions_changed_event(worklist_id, author_id, permission_id,
codename, added=[], removed=[]):
event_info = {
"worklist_id": worklist_id,
"permission_id": permission_id,
"author_id": author_id,
"codename": codename,
"added": added,
"removed": removed
}
return event_create({
"worklist_id": worklist_id,
"author_id": author_id,
"event_type": event_types.WORKLIST_PERMISSIONS_CHANGED,
"event_info": json.dumps(event_info)
})
def worklist_filters_changed_event(worklist_id, author_id, added=None,
removed=None, updated=None):
event_info = {
"worklist_id": worklist_id,
"author_id": author_id,
"added": added,
"removed": removed,
"updated": updated
}
return event_create({
"worklist_id": worklist_id,
"author_id": author_id,
"event_type": event_types.WORKLIST_FILTERS_CHANGED,
"event_info": json.dumps(event_info)
})
def worklist_contents_changed_event(worklist_id, author_id, added=None,
removed=None, updated=None):
event_info = {
"worklist_id": worklist_id,
"author_id": author_id,
"added": added,
"removed": removed,
"updated": updated
}
return event_create({
"worklist_id": worklist_id,
"author_id": author_id,
"event_type": event_types.WORKLIST_CONTENTS_CHANGED,
"event_info": json.dumps(event_info)
})
def board_created_event(board_id, author_id, board_title, board_description):
event_info = {
"board_id": board_id,
"author_id": author_id,
"board_title": board_title,
"board_description": board_description
}
return event_create({
"board_id": board_id,
"author_id": author_id,
"event_type": event_types.BOARD_CREATED,
"event_info": json.dumps(event_info)
})
def board_details_changed_event(board_id, author_id, updated, old, new):
event_info = {
"board_id": board_id,
"author_id": author_id,
"field": updated,
"old_value": old,
"new_value": new
}
return event_create({
"board_id": board_id,
"author_id": author_id,
"event_type": event_types.BOARD_DETAILS_CHANGED,
"event_info": json.dumps(event_info)
})
def board_permission_created_event(board_id, author_id, permission_id,
codename, users):
event_info = {
"board_id": board_id,
"permission_id": permission_id,
"author_id": author_id,
"codename": codename,
"users": users
}
return event_create({
"board_id": board_id,
"author_id": author_id,
"event_type": event_types.BOARD_PERMISSION_CREATED,
"event_info": json.dumps(event_info)
})
def board_permissions_changed_event(board_id, author_id, permission_id,
codename, added=[], removed=[]):
event_info = {
"board_id": board_id,
"permission_id": permission_id,
"author_id": author_id,
"codename": codename,
"added": added,
"removed": removed
}
return event_create({
"board_id": board_id,
"author_id": author_id,
"event_type": event_types.BOARD_PERMISSIONS_CHANGED,
"event_info": json.dumps(event_info)
})
def board_lanes_changed_event(board_id, author_id, added=None, removed=None,
updated=None):
event_info = {
"board_id": board_id,
"author_id": author_id,
"added": added,
"removed": removed,
"updated": updated
}
return event_create({
"board_id": board_id,
"author_id": author_id,
"event_type": event_types.BOARD_LANES_CHANGED,
"event_info": json.dumps(event_info)
})

View File

@ -553,6 +553,8 @@ def create_filter(worklist_id, filter_dict):
criteria = filter_dict.pop('filter_criteria')
filter_dict['list_id'] = worklist_id
filter = api_base.entity_create(models.WorklistFilter, filter_dict)
# Create criteria for the filter
filter = api_base.entity_get(models.WorklistFilter, filter.id)
filter.criteria = []
for criterion in criteria:
@ -567,20 +569,23 @@ def create_filter(worklist_id, filter_dict):
def update_filter(filter_id, update):
old_filter = api_base.entity_get(models.WorklistFilter, filter_id)
if 'filter_criteria' in update:
new_ids = [criterion.id for criterion in update['filter_criteria']]
# Change the criteria for this filter. If an ID is provided, change
# the criterion to match the provided criterion. If no ID is provided,
# create a new criterion and add it to the filter.
for criterion in update['filter_criteria']:
criterion_dict = criterion.as_dict(omit_unset=True)
if 'id' in criterion_dict:
existing = api_base.entity_get(models.FilterCriterion,
criterion['id'])
if existing.as_dict() != criterion_dict:
api_base.entity_update(models.FilterCriterion,
criterion_dict['id'],
criterion_dict)
id = criterion_dict.pop('id')
api_base.entity_update(models.FilterCriterion,
id, criterion_dict)
else:
created = api_base.entity_create(models.FilterCriterion,
criterion_dict)
criterion.id = created
old_filter.criteria.append(created)
# Remove criteria which aren't in the provided set
new_ids = [criterion.id for criterion in update['filter_criteria']]
for criterion in old_filter.criteria:
if criterion.id not in new_ids:
old_filter.criteria.remove(criterion)

View File

@ -51,9 +51,11 @@ class Subscription(WorkerTaskBase):
if resource == 'timeline_event':
event = db_api.entity_get(models.TimeLineEvent, resource_id,
session=session)
subscribers = sub_api.subscription_get_all_subscriber_ids(
'story', event.story_id, session=session)
self.handle_timeline_events(session, event, author, subscribers)
if event.story_id is not None:
subscribers = sub_api.subscription_get_all_subscriber_ids(
'story', event.story_id, session=session)
self.handle_timeline_events(
session, event, author, subscribers)
elif resource == 'project_group':
subscribers = sub_api.subscription_get_all_subscriber_ids(