storyboard/storyboard/db/api/boards.py

304 lines
11 KiB
Python

# Copyright (c) 2015-2016 Codethink Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from sqlalchemy.orm import aliased, subqueryload
from wsme.exc import ClientSideError
from storyboard._i18n import _
from storyboard.db.api import base as api_base
from storyboard.db.api import teams as teams_api
from storyboard.db.api import users as users_api
from storyboard.db import models
def _board_get(id, session=None):
if not session:
session = api_base.get_session()
query = session.query(models.Board).options(
subqueryload(models.Board.lanes)).filter_by(id=id)
return query.first()
def get(id):
return _board_get(id)
def _build_board_query(title=None, creator_id=None, user_id=None,
project_id=None, archived=False, current_user=None,
session=None, item_type=None, story_id=None,
task_id=None):
query = api_base.model_query(models.Board, session=session).distinct()
query = api_base.apply_query_filters(query=query,
model=models.Board,
title=title,
creator_id=creator_id,
project_id=project_id)
# Filter out boards that the current user can't see
query = api_base.filter_private_boards(query, current_user)
# Filter by boards that a given user has permissions to use
if user_id:
board_permissions = aliased(models.board_permissions)
permissions = aliased(models.Permission)
user_permissions = aliased(models.user_permissions)
users = aliased(models.User)
query = query.join(
(board_permissions,
models.Board.id == board_permissions.c.board_id)
)
query = query.join(
(permissions,
board_permissions.c.permission_id == permissions.id)
)
query = query.join(
(user_permissions,
permissions.id == user_permissions.c.permission_id)
)
query = query.join((users, user_permissions.c.user_id == users.id))
query = query.filter(users.id == user_id)
# Filter by whether or not we want archived boards
query = query.filter(models.Board.archived == archived)
if story_id or task_id:
query = query.join(models.BoardWorklist,
models.Worklist)
# Filter by story id
if story_id:
query = query.join(models.WorklistItem)
stories = query.filter(models.WorklistItem.item_type == 'story')
tasks = query.filter(models.WorklistItem.item_type == 'task')
if item_type == 'story':
query = stories.filter(models.WorklistItem.item_id == story_id)
elif item_type == 'task':
tasks = tasks.join(
(models.Task, models.WorklistItem.item_id == models.Task.id))
query = tasks.filter(models.Task.story_id == story_id)
else:
stories = stories.filter(models.WorklistItem.item_id == story_id)
tasks = tasks.join(
(models.Task, models.WorklistItem.item_id == models.Task.id))
tasks = tasks.filter(models.Task.story_id == story_id)
query = stories.union(tasks)
# Filter by task id
if task_id:
items = aliased(models.WorklistItem)
query = query.join((items, models.Worklist.id == items.list_id))
query = query.filter(items.item_type == 'task')
query = query.filter(items.item_id == task_id)
return query
def get_all(title=None, creator_id=None, user_id=None, project_id=None,
task_id=None, story_id=None, archived=False, offset=None,
limit=None, sort_field=None, sort_dir=None, current_user=None,
item_type=None, **kwargs):
if sort_field is None:
sort_field = 'id'
if sort_dir is None:
sort_dir = 'asc'
boards = _build_board_query(title=title,
creator_id=creator_id,
project_id=project_id,
user_id=user_id,
archived=archived,
current_user=current_user,
item_type=item_type,
story_id=story_id,
task_id=task_id,
**kwargs)
boards = api_base.paginate_query(query=boards,
model=models.Board,
limit=limit,
offset=offset,
sort_key=sort_field,
sort_dir=sort_dir)
return boards.all()
def get_count(title=None, creator_id=None, user_id=None, project_id=None,
task_id=None, story_id=None, item_type=None, archived=False,
current_user=None, **kwargs):
query = _build_board_query(title=title,
creator_id=creator_id,
project_id=project_id,
user_id=user_id,
archived=archived,
current_user=current_user,
story_id=story_id,
task_id=task_id,
item_type=item_type,
**kwargs)
return query.count()
def create(values):
board = api_base.entity_create(models.Board, values)
return board
def update(id, values):
return api_base.entity_update(models.Board, id, values)
def add_lane(board, lane_dict):
# Make sure we're adding the lane to the right board
lane_dict['board_id'] = board.id
if lane_dict.get('list_id') is None:
raise ClientSideError(_("A lane must have a worklist_id."))
if lane_dict.get('position') is None:
lane_dict['position'] = len(board.lanes)
api_base.entity_create(models.BoardWorklist, lane_dict)
return board
def update_lane(board, lane, new_lane):
# Make sure we aren't messing up the board ID
new_lane['board_id'] = board.id
if new_lane.get('list_id') is None:
raise ClientSideError(_("A lane must have a worklist_id."))
api_base.entity_update(models.BoardWorklist, lane.id, new_lane)
def get_from_lane(worklist):
lanes = api_base.entity_get_all(models.BoardWorklist,
list_id=worklist.id)
if lanes:
lane = lanes[0]
return lane.board
# FIXME: This assumes that boards only contain a task or story once, which
# is not actually enforced when creating a card.
def get_card(board, item_type, item_id, archived=False):
for lane in board.lanes:
for card in lane.worklist.items:
if (card.item_type == item_type
and card.item_id == item_id
and card.archived == archived):
return card
def has_card(board, item_type, item_id):
for lane in board.lanes:
for item in lane.worklist.items:
if item.item_id == item_id and item.item_type == item_type:
return True
return False
def get_user_owners(board):
for permission in board.permissions:
if permission.codename == 'edit_board':
return [user.id for user in permission.users]
def get_user_users(board):
for permission in board.permissions:
if permission.codename == 'move_cards':
return [user.id for user in permission.users]
def get_team_owners(board):
for permission in board.permissions:
if permission.codename == 'edit_board':
return [team.id for team in permission.teams]
def get_team_users(board):
for permission in board.permissions:
if permission.codename == 'move_cards':
return [team.id for team in permission.teams]
def get_permissions(board, user_id):
user = users_api.user_get(user_id)
if user is not None:
valid_permissions = set(user.permissions)
for team in user.teams:
valid_permissions.update(team.permissions)
return [permission.codename for permission in board.permissions
if permission in valid_permissions]
return []
def create_permission(board_id, permission_dict, session=None):
board = _board_get(board_id, session=session)
users = permission_dict.pop('users')
teams = permission_dict.pop('teams')
permission = api_base.entity_create(
models.Permission, permission_dict, session=session)
board.permissions.append(permission)
for user_id in users:
user = users_api.user_get(user_id, session=session)
user.permissions.append(permission)
for team_id in teams:
team = teams_api.team_get(team_id, session=session)
team.permissions.append(permission)
return permission
def update_permission(board_id, permission_dict):
board = _board_get(board_id)
id = None
for permission in board.permissions:
if permission.codename == permission_dict['codename']:
id = permission.id
users = permission_dict.pop('users', None)
teams = permission_dict.pop('teams', None)
if users is not None:
permission_dict['users'] = [users_api.user_get(user_id)
for user_id in users]
if teams is not None:
permission_dict['teams'] = [teams_api.team_get(team_id)
for team_id in teams]
if id is None:
raise ClientSideError(_("Permission %s does not exist")
% permission_dict['codename'])
return api_base.entity_update(models.Permission, id, permission_dict)
def visible(board, user=None):
if not board:
return False
if user and board.private:
permissions = get_permissions(board, user)
return any(name in permissions
for name in ['edit_board', 'move_cards'])
return not board.private
def editable(board, user=None):
if not board:
return False
if not user:
return False
return 'edit_board' in get_permissions(board, user)