Merge "Collate story metadata for status"
This commit is contained in:
@@ -46,23 +46,40 @@ class Story(base.APIBase):
|
||||
is_bug = bool
|
||||
"""Is this a bug or a feature :)"""
|
||||
|
||||
is_active = bool
|
||||
"""Is this an active story, or has it been deleted?"""
|
||||
|
||||
project_id = int
|
||||
"""Optional parameter"""
|
||||
|
||||
creator_id = int
|
||||
"""User ID of the Story creator"""
|
||||
|
||||
todo = int
|
||||
"""The number of tasks remaining to be worked on."""
|
||||
|
||||
inprogress = int
|
||||
"""The number of in-progress tasks for this story."""
|
||||
|
||||
review = int
|
||||
"""The number of tasks in review for this story."""
|
||||
|
||||
merged = int
|
||||
"""The number of merged tasks for this story."""
|
||||
|
||||
invalid = int
|
||||
"""The number of invalid tasks for this story."""
|
||||
|
||||
status = unicode
|
||||
"""The derived status of the story, one of 'active', 'merged', 'invalid'"""
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
return cls(
|
||||
title="Use Storyboard to manage Storyboard",
|
||||
description="We should use Storyboard to manage Storyboard.",
|
||||
is_bug=False,
|
||||
is_active=True,
|
||||
creator_id=1)
|
||||
creator_id=1,
|
||||
todo=0,
|
||||
inprogress=1,
|
||||
review=1,
|
||||
merged=0,
|
||||
invalid=0,
|
||||
status="active")
|
||||
|
||||
|
||||
class StoriesController(rest.RestController):
|
||||
@@ -150,7 +167,7 @@ class StoriesController(rest.RestController):
|
||||
raise ClientSideError("Story %s not found" % id,
|
||||
status_code=404)
|
||||
|
||||
@secure(checks.authenticated)
|
||||
@secure(checks.superuser)
|
||||
@wsme_pecan.wsexpose(Story, int)
|
||||
def delete(self, story_id):
|
||||
"""Delete this story.
|
||||
|
||||
@@ -19,7 +19,7 @@ from storyboard.db import models
|
||||
|
||||
|
||||
def story_get(story_id):
|
||||
return api_base.entity_get(models.Story, story_id)
|
||||
return api_base.entity_get(models.StorySummary, story_id)
|
||||
|
||||
|
||||
def story_get_all(marker=None, limit=None, project_id=None):
|
||||
@@ -28,7 +28,7 @@ def story_get_all(marker=None, limit=None, project_id=None):
|
||||
limit=limit,
|
||||
project_id=project_id)
|
||||
else:
|
||||
return api_base.entity_get_all(models.Story, is_active=True,
|
||||
return api_base.entity_get_all(models.StorySummary,
|
||||
marker=marker, limit=limit)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ def story_get_count(project_id=None):
|
||||
if project_id:
|
||||
return _story_get_count_in_project(project_id)
|
||||
else:
|
||||
return api_base.entity_get_count(models.Story, is_active=True)
|
||||
return api_base.entity_get_count(models.StorySummary)
|
||||
|
||||
|
||||
def _story_get_all_in_project(project_id, marker=None, limit=None):
|
||||
@@ -45,14 +45,13 @@ def _story_get_all_in_project(project_id, marker=None, limit=None):
|
||||
sub_query = api_base.model_query(models.Task.story_id, session) \
|
||||
.filter_by(project_id=project_id) \
|
||||
.distinct(True) \
|
||||
.subquery()
|
||||
.subquery('project_tasks')
|
||||
|
||||
query = api_base.model_query(models.Story, session) \
|
||||
.filter_by(is_active=True) \
|
||||
.join(sub_query, models.Story.tasks)
|
||||
query = api_base.model_query(models.StorySummary, session) \
|
||||
.join(sub_query, models.StorySummary.id == sub_query.c.story_id)
|
||||
|
||||
query = api_base.paginate_query(query=query,
|
||||
model=models.Story,
|
||||
model=models.StorySummary,
|
||||
limit=limit,
|
||||
sort_keys=['id'],
|
||||
marker=marker,
|
||||
@@ -67,11 +66,10 @@ def _story_get_count_in_project(project_id):
|
||||
sub_query = api_base.model_query(models.Task.story_id, session) \
|
||||
.filter_by(project_id=project_id) \
|
||||
.distinct(True) \
|
||||
.subquery()
|
||||
.subquery('project_tasks')
|
||||
|
||||
query = api_base.model_query(models.Story, session) \
|
||||
.filter_by(is_active=True) \
|
||||
.join(sub_query, models.Story.tasks)
|
||||
query = api_base.model_query(models.StorySummary, session) \
|
||||
.join(sub_query, models.StorySummary.id == sub_query.c.story_id)
|
||||
|
||||
return query.count()
|
||||
|
||||
@@ -88,5 +86,4 @@ def story_delete(story_id):
|
||||
story = story_get(story_id)
|
||||
|
||||
if story:
|
||||
story.is_active = False
|
||||
api_base.entity_update(models.Story, story_id, story.as_dict())
|
||||
api_base.entity_hard_delete(models.Story, story_id, story.as_dict())
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""Remove is_active from stories.
|
||||
|
||||
Revision ID: 015
|
||||
Revises: 014
|
||||
Create Date: 2014-04-09 16:52:36.375926
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '015'
|
||||
down_revision = '014'
|
||||
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column(u'stories', u'is_active')
|
||||
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
|
||||
op.add_column(u'stories',
|
||||
sa.Column('is_active', sa.Boolean(), default=True,
|
||||
server_default="1",
|
||||
nullable=False))
|
||||
|
||||
### end Alembic commands ###
|
||||
@@ -28,6 +28,9 @@ from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy import schema
|
||||
from sqlalchemy import select
|
||||
import sqlalchemy.sql.expression as expr
|
||||
import sqlalchemy.sql.functions as func
|
||||
from sqlalchemy import String
|
||||
from sqlalchemy import Table
|
||||
from sqlalchemy import Unicode
|
||||
@@ -171,7 +174,6 @@ class Story(Base):
|
||||
tasks = relationship('Task', backref='story')
|
||||
comments = relationship('Comment', backref='story')
|
||||
tags = relationship('StoryTag', backref='story')
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
_public_fields = ["id", "creator_id", "title", "description", "is_bug",
|
||||
"tasks", "comments", "tags"]
|
||||
@@ -234,3 +236,42 @@ class RefreshToken(Base):
|
||||
|
||||
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
|
||||
refresh_token = Column(Unicode(100), nullable=False)
|
||||
|
||||
|
||||
def _story_build_summary_query():
|
||||
return select([Story,
|
||||
func.cast(
|
||||
func.sum(Task.status == 'todo'), Integer
|
||||
).label('todo'),
|
||||
func.cast(
|
||||
func.sum(Task.status == 'inprogress'), Integer
|
||||
).label('inprogress'),
|
||||
func.cast(
|
||||
func.sum(Task.status == 'review'), Integer
|
||||
).label('review'),
|
||||
func.cast(
|
||||
func.sum(Task.status == 'merged'), Integer
|
||||
).label('merged'),
|
||||
func.cast(
|
||||
func.sum(Task.status == 'invalid'), Integer
|
||||
).label('invalid'),
|
||||
expr.case(
|
||||
[(func.sum(Task.status.in_(
|
||||
['todo', 'inprogress', 'review'])) > 0,
|
||||
'active'),
|
||||
((func.sum(Task.status == 'merged')) > 0, 'merged')],
|
||||
else_='invalid'
|
||||
).label('status')],
|
||||
None,
|
||||
expr.Join(Story, Task, onclause=Story.id == Task.story_id,
|
||||
isouter=True)) \
|
||||
.group_by(Task.story_id) \
|
||||
.alias('story_summary')
|
||||
|
||||
|
||||
class StorySummary(Base):
|
||||
__table__ = _story_build_summary_query()
|
||||
|
||||
_public_fields = ["id", "creator_id", "title", "description", "is_bug",
|
||||
"tasks", "comments", "tags", "todo", "inprogress",
|
||||
"review", "merged", "invalid", "status"]
|
||||
|
||||
Reference in New Issue
Block a user