Example #1
0
def dailyscrum(request):
    """ Prepare for /daily/scrum/ page.
        This is the interface for the daily scrum meeting.
        Before the meeting, team members can visit this page to enter the answers to the three typical questions as a reminder.
        During the meeting, the team can sit in front of their monitor looking at their answers to talk one by one.
        When anybody needs to select a task to do, she can switch to the tasks page and click on the interested task.
        Once selected, only the scrum master can make changes. The answers are saved in the Daily table.
    """
    product = get_active_product(request)
    if not product:
        return render(request, 'error', {'err': _('No product for you.')}, '')
    # get the latest active sprint
    sprint = get_active_sprint(product)
    if not sprint:
        sps = product.sprint_set.get_query_set().filter(
            start__lte=datetime.now())
        if sps.count() > 0:
            sprint = sps[0]
        else:
            return render(request, 'error',
                          {'err': _('No sprint for the product.')}, '')
    daily = None
    dailys = Daily.objects.filter(sprint=sprint.pk,
                                  member=request.user)  #latest on top
    if len(dailys) > 0:
        daily = dailys[0]
    sbacklog = Backlog.objects.filter(sprint=sprint)
    data = {
        'product': product,
        'sprint': sprint,
        'daily': daily,
        'items': sbacklog
    }
    return render(request, 'daily', data, 'daily/')
Example #2
0
def select_backlog(request, sid):
    """ Select a group of backlog items to fit in the sprint timebox based on the team velocity.
        Velocity is passed as argument or fetched from the team or global settings.
    """
    vs = request.GET.get('v', None)
    if not vs:
        velocity = get_velocity(get_active_product(request))
    else:
        velocity = float(vs)
    est = 0.0
    items = []
    sp = get_object_or_404(Sprint, pk=sid)
    # collect existing sprint backlog items
    for itm in sp.backlog_set.get_query_set():
        est += itm.estimate
        if est > velocity:
            # remove if overruns velocity
            itm.sprint = None
            itm.save()
        else:
            items.append(itm)
    if est < velocity:
        # add more if velocity allows
        for itm in Backlog.objects.filter(sprint=None):
            est += itm.estimate
            if est > velocity:
                break
            items.append(itm)
            itm.sprint = sp
            itm.save()
    data = {'backlog':items, 'sprint':sp, 'product':sp.product}
    return render(request, 'sprint_backlog', data, 'sprint/')
Example #3
0
def edit_sprint(request, sid=None, pid=None):
    """ Add a new Sprint entity or edit an existing one.
    """
    if pid:
        product = get_object_or_404(Product, pk=pid)
    else:
        product = get_active_product(request)
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err':_('Permission denied')})
    if sid:
        sprint = get_object_or_404(Sprint, pk=sid)
        if sprint.product != product:
            logger.error('edit_sprint, sprint %s not belong to active product'%sid)
            return render(request, 'error', {'err':_('Not active product')})
    else:
        sprint = None
    if request.method == 'POST':
        form = SprintEditForm(request.POST, instance=sprint)
        if form.is_valid():
            sp = form.save(commit=False)
            if not hasattr(sp, 'product'):
                setattr(sp, 'product', product)
            if not hasattr(sp, 'master'):
                setattr(sp, 'master', request.user)
            sp.save()
            form.save_m2m()
            return HttpResponseRedirect('/sprint/%s' % sid or form.cleaned_data['pk'])
        logger.debug('edit_sprint invalid form')
    else:
        form = SprintEditForm(instance=sprint)
    params = {'form':form, 'sid':sid, 'product':product, 'sprint':sprint}
    return render(request, 'sprint_edit', params, 'sprint/')
Example #4
0
def edit_sprint(request, sid=None, pid=None):
    """ Add a new Sprint entity or edit an existing one.
    """
    if pid:
        product = get_object_or_404(Product, pk=pid)
    else:
        product = get_active_product(request)
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err': _('Permission denied')})
    if sid:
        sprint = get_object_or_404(Sprint, pk=sid)
        if sprint.product != product:
            logger.error(
                'edit_sprint, sprint %s not belong to active product' % sid)
            return render(request, 'error', {'err': _('Not active product')})
    else:
        sprint = None
    if request.method == 'POST':
        form = SprintEditForm(request.POST, instance=sprint)
        if form.is_valid():
            sp = form.save(commit=False)
            if not hasattr(sp, 'product'):
                setattr(sp, 'product', product)
            if not hasattr(sp, 'master'):
                setattr(sp, 'master', request.user)
            sp.save()
            form.save_m2m()
            return HttpResponseRedirect('/sprint/%s' % sid
                                        or form.cleaned_data['pk'])
        logger.debug('edit_sprint invalid form')
    else:
        form = SprintEditForm(instance=sprint)
    params = {'form': form, 'sid': sid, 'product': product, 'sprint': sprint}
    return render(request, 'sprint_edit', params, 'sprint/')
Example #5
0
def select_backlog(request, sid):
    """ Select a group of backlog items to fit in the sprint timebox based on the team velocity.
        Velocity is passed as argument or fetched from the team or global settings.
    """
    vs = request.GET.get('v', None)
    if not vs:
        velocity = get_velocity(get_active_product(request))
    else:
        velocity = float(vs)
    est = 0.0
    items = []
    sp = get_object_or_404(Sprint, pk=sid)
    # collect existing sprint backlog items
    for itm in sp.backlog_set.get_query_set():
        est += itm.estimate
        if est > velocity:
            # remove if overruns velocity
            itm.sprint = None
            itm.save()
        else:
            items.append(itm)
    if est < velocity:
        # add more if velocity allows
        for itm in Backlog.objects.filter(sprint=None):
            est += itm.estimate
            if est > velocity:
                break
            items.append(itm)
            itm.sprint = sp
            itm.save()
    data = {'backlog': items, 'sprint': sp, 'product': sp.product}
    return render(request, 'sprint_backlog', data, 'sprint/')
Example #6
0
def edit_story(request, bid=None, pid=None):
    """ Add a new or edit an existing backlog story.
        A HTTP Get request will return a form to edit a story, and a HTTP POST will save the form.
        If bid is given, the backlog item is loaded otherwise a new item is created.
        User permission required: add_backlog
    """
    nextpg = None
    if pid:
        product = get_object_or_404(Product, pk=pid)
    else:
        product = get_active_product(request)
    if request.method == 'POST':
        nextpg = request.POST.get('next','/backlog/')
        if bid:
            item = get_object_or_404(Backlog, pk=bid)
            if item.product != product:
                logger.error('Backlog.product %s is not active product %s' % (item.product, product))
                return render(request, 'error', {'err':_('Not current active product')})
            if request.user not in [item.product.owner, item.product.master]:
                logger.warning('Not scrum master or product owner')
                return render(request, 'error', {'err':_('Permission denied')})
            form = BacklogForm(request.POST, instance=item)
        else:
            form = BacklogForm(request.POST)
        if form.is_valid():
            blog = form.save(commit=False)
            if not bid:
                blog.product = product
            try:
                blog.save()
                #form.save_m2m()
            except Exception,e:
                logger.error('backlog.views.edit_story, form.save() error: %s'%e)
            return HttpResponseRedirect(nextpg)
Example #7
0
def dailyscrum(request):
    """ Prepare for /daily/scrum/ page.
        This is the interface for the daily scrum meeting.
        Before the meeting, team members can visit this page to enter the answers to the three typical questions as a reminder.
        During the meeting, the team can sit in front of their monitor looking at their answers to talk one by one.
        When anybody needs to select a task to do, she can switch to the tasks page and click on the interested task.
        Once selected, only the scrum master can make changes. The answers are saved in the Daily table.
    """
    product = get_active_product(request)
    if not product:
        return render(request, "error", {"err": _("No product for you.")}, "")
    # get the latest active sprint
    sprint = get_active_sprint(product)
    if not sprint:
        sps = product.sprint_set.get_query_set().filter(start__lte=datetime.now())
        if sps.count() > 0:
            sprint = sps[0]
        else:
            return render(request, "error", {"err": _("No sprint for the product.")}, "")
    daily = None
    dailys = Daily.objects.filter(sprint=sprint.pk, member=request.user)  # latest on top
    if len(dailys) > 0:
        daily = dailys[0]
    sbacklog = Backlog.objects.filter(sprint=sprint)
    data = {"product": product, "sprint": sprint, "daily": daily, "items": sbacklog}
    return render(request, "daily", data, "daily/")
Example #8
0
 def get_queryset(self):
     """ Get a queryset of all backlogs for a product.
         Product ID can be specified with either url kwargs['pid'] or GET parameter ?pid=n.
         If not given, current active product in session is used.
     """
     pid = self.kwargs.get('pid', None)
     if not pid:
         pid = self.request.GET.get('pid', None)
     if pid:
         self.product = get_object_or_404(Product, pk=pid)
     else:
         self.product = get_active_product(self.request)
     return Backlog.objects.filter(product=self.product)
Example #9
0
 def get_queryset(self):
     """ Get a queryset of all backlogs for a product.
         Product ID can be specified with either url kwargs['pid'] or GET parameter ?pid=n.
         If not given, current active product in session is used.
     """
     pid = self.kwargs.get('pid', None)
     if not pid:
         pid = self.request.GET.get('pid', None)
     if pid:
         self.product = get_object_or_404(Product, pk=pid)
     else:
         self.product = get_active_product(self.request)
     return Backlog.objects.filter(product=self.product)
Example #10
0
def bulkload_stories(request):
    """ Bulk input a list of stories into the product backlog.
        User permission required: add_backlog
    """
    product = get_active_product(request)
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err':_('Permission denied')})
    if request.method == 'POST':
        stories = request.POST.get('stories', None)
        if not stories:
            return render(request, 'error', {'err':_('No stories')})
        for story in stories.split('\n'):
            if story.strip():
                Backlog.objects.create(product=product,story=story.strip())
        return HttpResponseRedirect('/backlog/')
    else:
        params = {'product':product,'url':request.path, 'inputname':'stories'}
        params['subtitle'] = _('Bulk input user stories')
        return render(request, 'backlog_load', params, 'backlog/')
Example #11
0
def taskboard(request):
    """ Prepare for /daily/taskboard/ page.
        Team members view the sprint backlog items and their detailed tasks.
        The page consists of three columns: undone item tasks, on-hand tasks, finished tasks.
        Each row is an item, with tasks as labels (story, points,[who]).
        There is a burndown chart on the right-hand side of the page.
    """
    product = get_active_product(request)
    sprint = get_active_sprint(product)
    if not sprint:
        sps = product.sprint_set.get_query_set().filter(start__lte=datetime.now())
        if sps.count() > 0:
            sprint = sps[0]
    if sprint:
        sbacklog = sprint.backlog_set.get_query_set()
    else:
        sbacklog = None
    data = {"product": product, "sprint": sprint, "items": sbacklog}
    return render(request, "taskboard", data, "daily/")
Example #12
0
def remove_sprint(request, sid):
    """ Remove Sprint entity by ID.
        User permission: delete_sprint, and must also be scrum master for this sprint.
    """
    if not sid:
        return render(request, 'error', {'err':'No sid'})
    product = get_active_product(request)
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err':_('Permission denied')})
    try:
        sp = get_object_or_404(Sprint, pk=sid)
        if sp.product != product:
            logger.error('remove_sprint, sprint %s not belong to active product'%sid)
            return render(request, 'error', {'err':_('Not active product')})
        if sp.master != request.user:
            return render(request, 'error', {'err':_('Permission denied')})
        sp.delete()
    except Exception,e:
        logger.error('sprint.views.remove_sprint(%s) error: %s' % (sid, e))
Example #13
0
def remove_sprint(request, sid):
    """ Remove Sprint entity by ID.
        User permission: delete_sprint, and must also be scrum master for this sprint.
    """
    if not sid:
        return render(request, 'error', {'err': 'No sid'})
    product = get_active_product(request)
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err': _('Permission denied')})
    try:
        sp = get_object_or_404(Sprint, pk=sid)
        if sp.product != product:
            logger.error(
                'remove_sprint, sprint %s not belong to active product' % sid)
            return render(request, 'error', {'err': _('Not active product')})
        if sp.master != request.user:
            return render(request, 'error', {'err': _('Permission denied')})
        sp.delete()
    except Exception, e:
        logger.error('sprint.views.remove_sprint(%s) error: %s' % (sid, e))
Example #14
0
def taskboard(request):
    """ Prepare for /daily/taskboard/ page.
        Team members view the sprint backlog items and their detailed tasks.
        The page consists of three columns: undone item tasks, on-hand tasks, finished tasks.
        Each row is an item, with tasks as labels (story, points,[who]).
        There is a burndown chart on the right-hand side of the page.
    """
    product = get_active_product(request)
    sprint = get_active_sprint(product)
    if not sprint:
        sps = product.sprint_set.get_query_set().filter(
            start__lte=datetime.now())
        if sps.count() > 0:
            sprint = sps[0]
    if sprint:
        sbacklog = sprint.backlog_set.get_query_set()
    else:
        sbacklog = None
    data = {'product': product, 'sprint': sprint, 'items': sbacklog}
    return render(request, 'taskboard', data, 'daily/')
Example #15
0
def edit_story(request, bid=None, pid=None):
    """ Add a new or edit an existing backlog story.
        A HTTP Get request will return a form to edit a story, and a HTTP POST will save the form.
        If bid is given, the backlog item is loaded otherwise a new item is created.
        User permission required: add_backlog
    """
    nextpg = None
    if pid:
        product = get_object_or_404(Product, pk=pid)
    else:
        product = get_active_product(request)
    if request.method == 'POST':
        nextpg = request.POST.get('next', '/backlog/')
        if bid:
            item = get_object_or_404(Backlog, pk=bid)
            if item.product != product:
                logger.error('Backlog.product %s is not active product %s' %
                             (item.product, product))
                return render(request, 'error',
                              {'err': _('Not current active product')})
            if request.user not in [item.product.owner, item.product.master]:
                logger.warning('Not scrum master or product owner')
                return render(request, 'error',
                              {'err': _('Permission denied')})
            form = BacklogForm(request.POST, instance=item)
        else:
            form = BacklogForm(request.POST)
        if form.is_valid():
            blog = form.save(commit=False)
            if not bid:
                blog.product = product
            try:
                blog.save()
                #form.save_m2m()
            except Exception, e:
                logger.error(
                    'backlog.views.edit_story, form.save() error: %s' % e)
            return HttpResponseRedirect(nextpg)
Example #16
0
def bulkload_stories(request):
    """ Bulk input a list of stories into the product backlog.
        User permission required: add_backlog
    """
    product = get_active_product(request)
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err': _('Permission denied')})
    if request.method == 'POST':
        stories = request.POST.get('stories', None)
        if not stories:
            return render(request, 'error', {'err': _('No stories')})
        for story in stories.split('\n'):
            if story.strip():
                Backlog.objects.create(product=product, story=story.strip())
        return HttpResponseRedirect('/backlog/')
    else:
        params = {
            'product': product,
            'url': request.path,
            'inputname': 'stories'
        }
        params['subtitle'] = _('Bulk input user stories')
        return render(request, 'backlog_load', params, 'backlog/')
Example #17
0
 def get_queryset(self):
     self.product = get_active_product(self.request)
     self.item = get_object_or_404(Backlog, pk=self.kwargs['bid'])
     return self.item.tasks.get_query_set()
Example #18
0
 def get_queryset(self):
     self.product = get_active_product(self.request)
     self.item = get_object_or_404(Backlog, pk=self.kwargs['bid'])
     return self.item.tasks.get_query_set()