Exemplo n.º 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/')
Exemplo n.º 2
0
def bulkload_tasks(request, bid):
    """ Bulk input a list of tasks for a backlog item/story.
        User permission required: add_backlog
    """
    if not bid:
        return render(request, 'error', {'err': _('No backlog item')})

    item = get_object_or_404(Backlog, pk=bid)
    product = item.product
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err': _('Permission denied')})

    if request.method == 'POST':
        tasks = request.POST.get('tasks', None)
        if not tasks:
            return render(request, 'error', {'err': _('No stories specified')})
        for task in tasks.split('\n'):
            if task.strip():
                Task.objects.create(item=item, name=task.strip())
        return HttpResponseRedirect('/backlog/%s/tasks' % bid)
    else:
        params = {
            'product': product,
            'backlog': item,
            'url': request.path,
            'inputname': 'tasks'
        }
        params['subtitle'] = _('Bulk input tasks for the backlog item')
        return render(request, 'backlog_load', params, 'backlog/')
Exemplo n.º 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/')
Exemplo n.º 4
0
def edit_product(request, pid=None):
    """ Show an edit form for a Product entity.
    """
    if not pid:
        pid = request.REQUEST.get('pid', None)

    if pid:
        product = get_object_or_404(Product, pk=pid)
        if not product.owner_or_master(request.user):
            return render(request, 'error', {'err': _('Permission denied')})
    else:
        product = None

    if request.method == 'POST':
        form = ProductForm(request.POST, instance=product)
        if form.is_valid():
            pd = form.save(commit=False)
            if not hasattr(pd, 'owner'):
                setattr(pd, 'owner', request.user)
            pd.save()
            #form.save_m2m()
            return HttpResponseRedirect('/product/')
    else:
        if product:
            form = ProductForm(instance=product)
        else:
            form = ProductForm()

    return render(request, 'product_edit', {
        'form': form,
        'pid': pid
    }, 'product/')
Exemplo n.º 5
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/')
Exemplo n.º 6
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/")
Exemplo n.º 7
0
def edit_product(request, pid=None):
    """ Show an edit form for a Product entity.
    """
    if not pid:
        pid = request.REQUEST.get('pid', None)
        
    if pid:
        product = get_object_or_404(Product, pk=pid)
        if not product.owner_or_master(request.user):
            return render(request, 'error', {'err':_('Permission denied')})
    else:
        product = None
        
    if request.method == 'POST':
        form = ProductForm(request.POST, instance=product)
        if form.is_valid():
            pd = form.save(commit=False)
            if not hasattr(pd, 'owner'):
                setattr(pd, 'owner', request.user)
            pd.save()
            #form.save_m2m()
            return HttpResponseRedirect('/product/')
    else:
        if product:
            form = ProductForm(instance=product)
        else:
            form = ProductForm()
        
    return render(request, 'product_edit', {'form':form,'pid':pid}, 'product/')
Exemplo n.º 8
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)
Exemplo n.º 9
0
def remove_story(request, bid=None):
    """ Remove a backlog item specified by ID.
        Only the product owner or scrum master can remove an item from the backlog.
        HTTP GET can be used instead of DELETE for REST framework.
        User permission required: delete_backlog
    """
    if bid:
        try:
            item = Backlog.objects.get(pk=bid)
        except Exception, e:
            logger.warning(
                'scrum.remove_story: Backlog item %s not found: %s' % (bid, e))
            return render(request, 'error',
                          {'err': _('Backlog item not found.')}, '')
        if request.user not in [item.product.owner, item.product.master]:
            return render(request, 'error', {'err': _('Permission denied')})
        try:
            item.delete()
        except Exception, e:
            logger.error('scrum.remove_story: Backlog item delete failed: %s' %
                         e)
            return render(
                request, 'error',
                {'err': _('Backlog delete failed, please try again later.')},
                '')
Exemplo n.º 10
0
def planning_poker(request, tid=None):
    """ Start planning poker page for backlog task tid.
        User permission: developer
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)
        if not tid:
            return error_response(_('No task specified'))
    task = get_object_or_404(Task, pk=tid)
    item = task.item
    product = item.product
    if not product.has_member(request.user):
        return render(request, 'error', {'err':_('Permission denied')})
    params = {'product':product,'backlog':item,'task':task,'master':product.owner_or_master(request.user)}
    return render(request, 'poker', params,'backlog/')
Exemplo n.º 11
0
def finish_task(request, tid=None):
    """ Mark a Task as finished.
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)
        if not tid:
            return render(request,'error',{'err':_('No task given')})
    task = get_object_or_404(Task, pk=tid)
    if task.doer == request.user:
        task.status = 'FINISHED'
        task.end = datetime.now()
        task.actual = (task.end - task.start).seconds / 86400.0
        task.save()
        return HttpResponseRedirect('/backlog/%s/tasks'%task.item.pk)
    return render(request,'error',{'err':_('Permission denied')})
Exemplo n.º 12
0
def finish_task(request, tid=None):
    """ Mark a Task as finished.
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)
        if not tid:
            return render(request, 'error', {'err': _('No task given')})
    task = get_object_or_404(Task, pk=tid)
    if task.doer == request.user:
        task.status = 'FINISHED'
        task.end = datetime.now()
        task.actual = (task.end - task.start).seconds / 86400.0
        task.save()
        return HttpResponseRedirect('/backlog/%s/tasks' % task.item.pk)
    return render(request, 'error', {'err': _('Permission denied')})
Exemplo n.º 13
0
def submit_daily(request):
    """ Team members submit daily questions for reminder.
        If daily record exists for today, update its content, else create a new record.
        Parameters: yesterday, today, problem, serious, spid.
    """
    spid = request.POST.get("spid", None)
    yesterday = request.POST.get("yesterday", "")
    today = request.POST.get("today", "")
    problem = request.POST.get("problem", "")
    serious = request.POST.get("serious", "3")
    try:
        sprint = Sprint.objects.get(pk=spid)
        product = sprint.product
        if not product.has_member(request.user):
            return HttpResponse('{"error":"%s"}' % _("Not a team member"))
        dailys = Daily.objects.filter(sprint=sprint, member=request.user)
        daily = None
        if len(dailys) > 0:
            daily = dailys[0]
            if daily.scrumdate.date() != datetime.now().date():
                daily = None
        if not daily:
            daily = Daily(product=product, sprint=sprint, member=request.user)
        daily.yesterday = yesterday
        daily.today = today
        daily.problem = problem
        daily.serious = int(serious)
        daily.save()
        return HttpResponseRedirect("/daily")
    #        return HttpResponse('{"status":"OK"}')
    except Exception, e:
        logger.exception(e)
        return render(request, "error", {"err": _("Save failed, retry later.")})
Exemplo n.º 14
0
def submit_retro(request):
    """ The scrum master submits the retrospectives for the sprint review.
        request.path: /sprint/retro/submit
        This is done through Ajax call and pass data in JSON.
        User permission: change_sprint
    """
    if request.method == 'POST':
        good = request.POST.get('good', '')
        bad = request.POST.get('bad', '')
        advice = request.POST.get('next', '')
        sid = request.POST.get('sid')
        try:
            sp = Sprint.objects.get(pk=sid)
            if sp.master != request.user:
                return render(request,
                              'error', {'err': _('Permission denied')},
                              fmt='json')
            sp.retro = '{"good":"%s","bad":"%s","next":"%s"}' % (
                jsonstr(good), jsonstr(bad), jsonstr(advice))
            sp.save()
            return HttpResponse('{"status":"OK"}')
        except Exception, e:
            logger.error('submit_retro failed: %s' % e)
            return error_response(_('Error saving submits, retry later.'),
                                  fmt='json')
Exemplo n.º 15
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/')
Exemplo n.º 16
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/')
Exemplo n.º 17
0
def bulk_remove_tasks(request, bid):
    """ Remove a list of tasks of a backlog item.
        HTTP GET can be used instead of DELETE for REST framework.
        User permission required: delete_backlog
    """
    tids = request.REQUEST.get('tids', None)
    if not (bid and tids):
        return HttpResponseRedirect('/backlog/')
    try:
        item = get_object_or_404(Backlog, pk=bid)
    except:
        logger.error('bulk_remove_tasks(bid=%s)'%bid)
        return render(request,'error',{'err':_('Record not found')})
    try:
        item.tasks.get_query_set().filter(pk__in=tids.split(',')).delete()
    except Exception, e:
        logger.error('bulk_remove_tasks(bid=%s,tids=%s) error:%s' % (bid, tids, e))
        return render(request,'error',{'err':_('Error deleting tasks')})
Exemplo n.º 18
0
def bulk_remove(request):
    """ Remove a list of Backlog items.
        HTTP GET can be used instead of DELETE for REST framework.
        User permission required: delete_backlog
    """
    bids = request.REQUEST.get('bids',None)
    if not bids:
        return HttpResponseRedirect('/backlog/')
    try:
        qs = Backlog.objects.filter(pk__in=bids.split(','))
        if qs.count() > 0 and request.user in [qs[0].product.owner, qs[0].product.master]:
            qs.delete()
            return HttpResponseRedirect('/backlog/')
        else:
            return render(request, 'error', {'err':_('Permission denied')})
    except Exception,e:
        logger.error('backlog.views.bulk_remove(bids=%s): %s'%(bids,e))
        return render(request, 'error', {'err':_('Error deleting these items.')})
Exemplo n.º 19
0
def bulk_remove_tasks(request, bid):
    """ Remove a list of tasks of a backlog item.
        HTTP GET can be used instead of DELETE for REST framework.
        User permission required: delete_backlog
    """
    tids = request.REQUEST.get('tids', None)
    if not (bid and tids):
        return HttpResponseRedirect('/backlog/')
    try:
        item = get_object_or_404(Backlog, pk=bid)
    except:
        logger.error('bulk_remove_tasks(bid=%s)' % bid)
        return render(request, 'error', {'err': _('Record not found')})
    try:
        item.tasks.get_query_set().filter(pk__in=tids.split(',')).delete()
    except Exception, e:
        logger.error('bulk_remove_tasks(bid=%s,tids=%s) error:%s' %
                     (bid, tids, e))
        return render(request, 'error', {'err': _('Error deleting tasks')})
Exemplo n.º 20
0
def sprint_tasks(request, sid):
    """ Show a list of all tasks grouped by backlog items for the sprint.
    """
    sprint = get_object_or_404(Sprint, pk=sid)
    tasks = Task.objects.filter(item__sprint=sprint)
    return render(request, 'sprint_tasks', {
        'tasks': tasks,
        'sprint': sprint,
        'product': sprint.product
    }, 'sprint/')
Exemplo n.º 21
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))
Exemplo n.º 22
0
def remove_story(request, bid=None):
    """ Remove a backlog item specified by ID.
        Only the product owner or scrum master can remove an item from the backlog.
        HTTP GET can be used instead of DELETE for REST framework.
        User permission required: delete_backlog
    """
    if bid:
        try:
            item = Backlog.objects.get(pk=bid)
        except Exception, e:
            logger.warning('scrum.remove_story: Backlog item %s not found: %s' % (bid, e))
            return render(request, 'error', {'err':_('Backlog item not found.')}, '')
        if request.user not in [item.product.owner, item.product.master]:
            return render(request, 'error', {'err':_('Permission denied')})
        try:
            item.delete()
        except Exception, e:
            logger.error('scrum.remove_story: Backlog item delete failed: %s' % e)
            return render(request, 'error', {'err':_('Backlog delete failed, please try again later.')}, '')
Exemplo n.º 23
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/')
Exemplo n.º 24
0
def planning_poker(request, tid=None):
    """ Start planning poker page for backlog task tid.
        User permission: developer
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)
        if not tid:
            return error_response(_('No task specified'))
    task = get_object_or_404(Task, pk=tid)
    item = task.item
    product = item.product
    if not product.has_member(request.user):
        return render(request, 'error', {'err': _('Permission denied')})
    params = {
        'product': product,
        'backlog': item,
        'task': task,
        'master': product.owner_or_master(request.user)
    }
    return render(request, 'poker', params, 'backlog/')
Exemplo n.º 25
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))
Exemplo n.º 26
0
def remove_product(request, pid=None):
    """ Remove a product/project by the owner.
    """
    if not pid:
        pid = request.REQUEST.get('pid', None)
    product = get_object_or_404(Product, pk=pid)
    if product.owner == request.user:
        product.delete()
        return HttpResponseRedirect('/products')
    else:
        return render(request, 'error', {'err':_('Permission denied')})
Exemplo n.º 27
0
def select_product(request, pid=None):
    """ Select a product into active session.
    """
    if not pid:
        pid = request.REQUEST.get('pid', None)
    product = get_object_or_404(Product, pk=pid)
    if not product.has_member(request.user):
        return render(request, 'error', {'err':_('Permission denied')})
    
    request.session['ActiveProduct'] = product.pk
    return HttpResponseRedirect('/')
Exemplo n.º 28
0
def select_product(request, pid=None):
    """ Select a product into active session.
    """
    if not pid:
        pid = request.REQUEST.get('pid', None)
    product = get_object_or_404(Product, pk=pid)
    if not product.has_member(request.user):
        return render(request, 'error', {'err': _('Permission denied')})

    request.session['ActiveProduct'] = product.pk
    return HttpResponseRedirect('/')
Exemplo n.º 29
0
def remove_product(request, pid=None):
    """ Remove a product/project by the owner.
    """
    if not pid:
        pid = request.REQUEST.get('pid', None)
    product = get_object_or_404(Product, pk=pid)
    if product.owner == request.user:
        product.delete()
        return HttpResponseRedirect('/products')
    else:
        return render(request, 'error', {'err': _('Permission denied')})
Exemplo n.º 30
0
def bulk_remove(request):
    """ Remove a list of Backlog items.
        HTTP GET can be used instead of DELETE for REST framework.
        User permission required: delete_backlog
    """
    bids = request.REQUEST.get('bids', None)
    if not bids:
        return HttpResponseRedirect('/backlog/')
    try:
        qs = Backlog.objects.filter(pk__in=bids.split(','))
        if qs.count() > 0 and request.user in [
                qs[0].product.owner, qs[0].product.master
        ]:
            qs.delete()
            return HttpResponseRedirect('/backlog/')
        else:
            return render(request, 'error', {'err': _('Permission denied')})
    except Exception, e:
        logger.error('backlog.views.bulk_remove(bids=%s): %s' % (bids, e))
        return render(request, 'error',
                      {'err': _('Error deleting these items.')})
Exemplo n.º 31
0
def register(request):
    """ Register a new user.
        Developers register themselves here and optionally choose to join a team.
        A super user is responsible to promote a user to a product owner or a scrum master.
    """
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/')
    else:
        form = RegisterForm()
    return render(request, 'register', {'form':form}, 'staff/')
Exemplo n.º 32
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)
Exemplo n.º 33
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/')
Exemplo n.º 34
0
def exclude_backlog(request, sid, bid):
    """ Remove a backlog item from this sprint.
        User permission: change_sprint, and must be scrum master of the sprint.
    """
    try:
#        sprint = get_object_or_404(Sprint, pk=sid)
        bitem = get_object_or_404(Backlog, pk=bid)
        if str(bitem.sprint.pk) == sid and request.user == bitem.sprint.master:
            bitem.sprint = None
            bitem.save()
        else:
            logger.error('remove_backlog(sid=%s,bid=%s),Backlog.sprint.id=%s,master=%s,request.user=%s'%(sid,bid,bitem.sprint.pk,bitem.sprint.master,request.user))
            return render(request, 'error', {'err':_('Permission denied')})
    except Exception,e:
        logger.error('sprint.remove_backlog: %s'%e)
Exemplo n.º 35
0
def bulkload_tasks(request, bid):
    """ Bulk input a list of tasks for a backlog item/story.
        User permission required: add_backlog
    """
    if not bid:
        return render(request, 'error', {'err':_('No backlog item')})
    
    item = get_object_or_404(Backlog, pk=bid)
    product = item.product
    if not product.owner_or_master(request.user):
        return render(request, 'error', {'err':_('Permission denied')})
    
    if request.method == 'POST':
        tasks = request.POST.get('tasks', None)
        if not tasks:
            return render(request, 'error', {'err':_('No stories specified')})
        for task in tasks.split('\n'):
            if task.strip():
                Task.objects.create(item=item,name=task.strip())
        return HttpResponseRedirect('/backlog/%s/tasks'%bid)
    else:
        params = {'product':product,'backlog':item,'url':request.path, 'inputname':'tasks'}
        params['subtitle'] = _('Bulk input tasks for the backlog item')
        return render(request, 'backlog_load', params, 'backlog/')
Exemplo n.º 36
0
def remove_task(request, tid):
    """ Remove a task from Task.
        User permission required: delete_backlog
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)
        if not tid:
            return render(request, 'error', {'err': _('No task ID')})
    task = get_object_or_404(Task, pk=tid)
    if not task.item.product.owner_or_master(request.user):
        return error_response(_('Permision denied'))
    try:
        task.delete()
        return HttpResponseRedirect('/backlog/%s/tasks' % task.item.pk)
    except:
        return error_response(_('Error deleting task'))
Exemplo n.º 37
0
def remove_task(request, tid):
    """ Remove a task from Task.
        User permission required: delete_backlog
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)
        if not tid:
            return render(request, 'error', {'err':_('No task ID')})
    task = get_object_or_404(Task, pk=tid)
    if not task.item.product.owner_or_master(request.user):
        return error_response(_('Permision denied'))
    try:
        task.delete()
        return HttpResponseRedirect('/backlog/%s/tasks'%task.item.pk)
    except:
        return error_response(_('Error deleting task'))
Exemplo n.º 38
0
def save_notes(request):
    """ The scrum master saves some notes about a backlog item.
        User permission required: change_backlog
    """
    if request.method=='POST':
        itemid = request.POST.get('item',None)
        notes = request.POST.get('notes','')
        try:
            item = Backlog.objects.get(pk=itemid)
            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')})
            item.notes = notes
            item.save()
            return HttpResponse('{"status":"OK"}')
        except Exception,e:
            logger.exception('save_notes error: %s'%e)
            return error_response(_('Error saving notes'))
Exemplo n.º 39
0
def edit_staff(request):
    """ Edit user profile. """
    staff = request.user.profile
    if request.method == 'POST':
        form = StaffForm(request.POST, instance=staff)
        if form.is_valid():
            form.save()
            data = form.cleaned_data
            if data['team']:
                try:
                    team = Group.objects.get(pk=data['team'])
                    request.user.groups.add(team)
                except:
                    pass
            return HttpResponseRedirect('/staff/%s'%staff.pk)
    else:
        form = StaffForm(instance=staff)
    return render(request, 'staff_edit', {'form':form}, 'staff/')
Exemplo n.º 40
0
def exclude_backlog(request, sid, bid):
    """ Remove a backlog item from this sprint.
        User permission: change_sprint, and must be scrum master of the sprint.
    """
    try:
        #        sprint = get_object_or_404(Sprint, pk=sid)
        bitem = get_object_or_404(Backlog, pk=bid)
        if str(bitem.sprint.pk) == sid and request.user == bitem.sprint.master:
            bitem.sprint = None
            bitem.save()
        else:
            logger.error(
                'remove_backlog(sid=%s,bid=%s),Backlog.sprint.id=%s,master=%s,request.user=%s'
                %
                (sid, bid, bitem.sprint.pk, bitem.sprint.master, request.user))
            return render(request, 'error', {'err': _('Permission denied')})
    except Exception, e:
        logger.error('sprint.remove_backlog: %s' % e)
Exemplo n.º 41
0
def edit_team(request, tid=None):
    """ Add a new Group entity or edit an existing one, and a Team entity is saved along with it.
    """
    if not tid:
        tid = request.REQUEST.get('tid', None)

    if tid:
        team = get_object_or_404(Group, pk=tid)
    else:
        team = None
    
    if request.method == 'POST':
        form = TeamForm(request.POST, instance=team)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/')
    else:
        form = TeamForm(instance=team)
    return render(request, 'team_edit', {'form':form, 'team':team}, 'staff/')
Exemplo n.º 42
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/")
Exemplo n.º 43
0
def save_notes(request):
    """ The scrum master saves some notes about a backlog item.
        User permission required: change_backlog
    """
    if request.method == 'POST':
        itemid = request.POST.get('item', None)
        notes = request.POST.get('notes', '')
        try:
            item = Backlog.objects.get(pk=itemid)
            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')})
            item.notes = notes
            item.save()
            return HttpResponse('{"status":"OK"}')
        except Exception, e:
            logger.exception('save_notes error: %s' % e)
            return error_response(_('Error saving notes'))
Exemplo n.º 44
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/')
Exemplo n.º 45
0
def submit_retro(request):
    """ The scrum master submits the retrospectives for the sprint review.
        request.path: /sprint/retro/submit
        This is done through Ajax call and pass data in JSON.
        User permission: change_sprint
    """
    if request.method == 'POST':
        good = request.POST.get('good','')
        bad = request.POST.get('bad','')
        advice = request.POST.get('next','')
        sid = request.POST.get('sid')
        try:
            sp = Sprint.objects.get(pk=sid)
            if sp.master != request.user: 
                return render(request, 'error', {'err':_('Permission denied')}, fmt='json')
            sp.retro = '{"good":"%s","bad":"%s","next":"%s"}' % (jsonstr(good), jsonstr(bad), jsonstr(advice))
            sp.save()
            return HttpResponse('{"status":"OK"}')
        except Exception,e:
            logger.error('submit_retro failed: %s' % e)
            return error_response(_('Error saving submits, retry later.'), fmt='json')
Exemplo n.º 46
0
def submit_daily(request):
    """ Team members submit daily questions for reminder.
        If daily record exists for today, update its content, else create a new record.
        Parameters: yesterday, today, problem, serious, spid.
    """
    spid = request.POST.get('spid', None)
    yesterday = request.POST.get('yesterday', '')
    today = request.POST.get('today', '')
    problem = request.POST.get('problem', '')
    serious = request.POST.get('serious', '3')
    try:
        sprint = Sprint.objects.get(pk=spid)
        product = sprint.product
        if not product.has_member(request.user):
            return HttpResponse('{"error":"%s"}' % _('Not a team member'))
        dailys = Daily.objects.filter(sprint=sprint, member=request.user)
        daily = None
        if len(dailys) > 0:
            daily = dailys[0]
            if daily.scrumdate.date() != datetime.now().date():
                daily = None
        if not daily:
            daily = Daily(product=product, sprint=sprint, member=request.user)
        daily.yesterday = yesterday
        daily.today = today
        daily.problem = problem
        daily.serious = int(serious)
        daily.save()
        return HttpResponseRedirect('/daily')


#        return HttpResponse('{"status":"OK"}')
    except Exception, e:
        logger.exception(e)
        return render(request, 'error',
                      {"err": _('Save failed, retry later.')})
Exemplo n.º 47
0
            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)
    elif 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')})
        form = BacklogForm(instance=item)
    else:
        form = BacklogForm()
    if not nextpg: nextpg = request.GET.get('next','/backlog/')
    params = {'form': form, 'product': product, 'cataheads': get_category_typeaheads(), 'next':nextpg, 'url':'/backlog/edit/%s' % (bid or '')}
    return render(request, 'story_edit', params, 'backlog/') 

@permission_required('backlog.add_backlog')
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')})
Exemplo n.º 48
0
            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)
    elif 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')})
        form = BacklogForm(instance=item)
    else:
        form = BacklogForm()
    if not nextpg: nextpg = request.GET.get('next', '/backlog/')
    params = {
        'form': form,
        'product': product,
        'cataheads': get_category_typeaheads(),
        'next': nextpg,
        'url': '/backlog/edit/%s' % (bid or '')
    }
    return render(request, 'story_edit', params, 'backlog/')


@permission_required('backlog.add_backlog')
Exemplo n.º 49
0
def sprint_tasks(request, sid):
    """ Show a list of all tasks grouped by backlog items for the sprint.
    """
    sprint = get_object_or_404(Sprint, pk=sid)
    tasks = Task.objects.filter(item__sprint=sprint)
    return render(request, 'sprint_tasks', {'tasks':tasks,'sprint':sprint,'product':sprint.product}, 'sprint/')