def promote(request): """ Promote a user to a product owner or scrum master. The permissions are updated for this user accordingly. """ if not request.user.is_superuser(): return error_response(_('Permission denied')) uid = request.REQUEST.get('uid') usr = get_object_or_404(User, pk=uid) staff = usr.get_profile() role = request.REQUEST.get('role', '') if role not in ['D','M','O']: return error_response(_('Invalid role')) staff.role = role staff.save() # change permission # product owner permissions: add,change,delete product, backlog, releaseplan # scrum master permissions: add,change,delete backlog, sprint, task, releaseplan if role == 'D': usr.permissions.clear() else: perms = {'O':['product','backlog','releaseplan'],'M':['sprint','backlog','releaseplan']} for p in perms[role]: for act in ['add','change','delete']: cname = '%s_%s' % (act, p) usr.permissions.add(Permission.objects.get(codename=cname)) return HttpResponseRedirect('/')
def start(request): """ Start a new voting session. @return: {"sid":"a343545d4454.."} """ usr = request.user pid = request.GET.get('pid', None) if not pid: return error_response(_('No Product ID'), fmt='json') if not is_scrum_master(request, pid): return error_response(_('Scrum Master required'), fmt='json') return json_response(Vote.objects.start(usr, pid))
def start(request): """ Start a new voting session. @return: {"sid":"a343545d4454.."} """ usr = request.user pid = request.GET.get("pid", None) if not pid: return error_response(_("No Product ID"), fmt="json") if not is_scrum_master(request, pid): return error_response(_("Scrum Master required"), fmt="json") return json_response(Vote.objects.start(usr, pid))
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'))
def status(request): """ Check status of voting and get sid if started. """ pid = request.GET.get("pid", None) if not pid: return error_response(_("No Product ID"), fmt="json") return json_response(Vote.objects.status(pid))
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')
def status(request): """ Check status of voting and get sid if started. """ pid = request.GET.get('pid', None) if not pid: return error_response(_('No Product ID'), fmt='json') return json_response(Vote.objects.status(pid))
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'))
def vote(request): """ Submit a value for the voting session. """ try: sid, pid, vote = get_params(request, ['sid', 'pid', 'vote']) except KeyError, e: logger.error('poker.vote() request.get error: %s' % e) return error_response(_('Not enough parameters'), fmt='json')
def stop(request): """ Stop the session. """ try: sid, pid = get_params(request, ['sid', 'pid']) except KeyError, e: logger.error('poker.stop() error: %s' % e) return error_response(_('Not enough parameters'), fmt='json')
def stop(request): """ Stop the session. """ try: sid, pid = get_params(request, ["sid", "pid"]) except KeyError, e: logger.error("poker.stop() error: %s" % e) return error_response(_("Not enough parameters"), fmt="json")
def vote(request): """ Submit a value for the voting session. """ try: sid, pid, vote = get_params(request, ["sid", "pid", "vote"]) except KeyError, e: logger.error("poker.vote() request.get error: %s" % e) return error_response(_("Not enough parameters"), fmt="json")
def unpick_task(request, tid=None): """ Delevepor unselect a task and put it back to the unassigned status. """ if not tid: tid = request.REQUEST.get('tid', None) task = get_object_or_404(Task, pk=tid) if not task.doer: logger.error('unpick_task(tid=%s), task.doer is None' % tid) return error_response(_('Task not assigned to anybody')) if task.doer != request.user: logger.warning('unpick_task(tid=%s), task.doer != request.user' % tid) return error_response(_('Not task doer')) try: task.unassign(request.user) return HttpResponseRedirect('/daily') except Exception, e: logger.error('unpick_task.save() failed: %s' % e) return error_response(_('Failed to save, retry later.'))
def collect(request): """ Collect all submitted votes. @return: {"status":"STARTED","votes":{"<uid>":"<vote>","1":3,..}} """ try: sid, pid = get_params(request, ["sid", "pid"]) except KeyError, e: logger.error("poker.collect() error: %s" % e) return error_response(_("Not enough parameters"), fmt="json")
def collect(request): """ Collect all submitted votes. @return: {"status":"STARTED","votes":{"<uid>":"<vote>","1":3,..}} """ try: sid, pid = get_params(request, ['sid', 'pid']) except KeyError, e: logger.error('poker.collect() error: %s' % e) return error_response(_('Not enough parameters'), fmt='json')
def unpick_task(request, tid=None): """ Delevepor unselect a task and put it back to the unassigned status. """ if not tid: tid = request.REQUEST.get("tid", None) task = get_object_or_404(Task, pk=tid) if not task.doer: logger.error("unpick_task(tid=%s), task.doer is None" % tid) return error_response(_("Task not assigned to anybody")) if task.doer != request.user: logger.warning("unpick_task(tid=%s), task.doer != request.user" % tid) return error_response(_("Not task doer")) try: task.unassign(request.user) return HttpResponseRedirect("/daily") except Exception, e: logger.error("unpick_task.save() failed: %s" % e) return error_response(_("Failed to save, retry later."))
def import_stories(request): """ Upload a CSV file as stories into the specified product backlog. A list of fields can be selected to parse the rows in the CSV to match the Backlog metadata. """ # TODO: implement importing backlog items from a CSV file from upload if request.method == 'POST': pass else: pass return error_response(_('Not implemented yet'))
def include_backlog(request, sid, bid): """ Include a backlog item into this sprint. If this item is assigned to another sprint, returns an error. User permission: change_sprint and must be scrum master of the sprint. """ try: bitem = get_object_or_404(Backlog, pk=bid) if bitem.sprint: logger.error('include_backlog(sid=%s,bid=%s), item already assigned to %s'%bitem.sprint) return error_response(_('This item is already assigned to another sprint')) sprint = get_object_or_404(Sprint, pk=sid) if sprint.master != request.user: logger.error('include_backlog() user Not sprint master') return error_response(_('Permission denied')) bitem.sprint = sprint bitem.save() except Exception, e: logger.error('include_backlog(sid=%s,bid=%s) failed:%s' % (sid,bid,e)) return error_response(_('Error saving backlog item in the sprint.'))
def pick_task(request, tid=None): """ A team member picks a task to work on. This is an Ajax call. There should be a way to notify others that somebody picked a task. A simple way is to save this operation in a database table, and others using the same page will check it up and once applied will put a mark there so as not to repeat it. This can be done using a timestamp. Operations with timestamp later than the last visit time, will be applied, or else ignored. """ if not tid: tid = request.REQUEST.get("tid", None) if tid: task = get_object_or_404(Task, pk=tid) else: logger.error("pick_task tid not given") return error_response(_("Task ID error")) if task.doer: return error_response(_("This task has been assigned to another member.")) try: task.assign(request.user) except Exception, e: logger.error("pick_task save() failed: %s" % e) return error_response(_("Failed to save, retry later."))
def save_estimate(request, tid): """ Get or Post/save Backlog(item).estimate. Only product scrum master can save. Query path: /task/<tid>/estimate[?est=4] User permission required: change_backlog """ try: tsk = Task.objects.get(pk=tid) except Task.DoesNotExist: return error_response(_('Task not found'), fmt='json') if request.method == 'GET': return HttpResponse('{"status":"OK","estimate":"%s"}' % (tsk.estimate)) else: # TODO: verify user is a product owner or scrum master tsk.estimate = float(request.POST.get('est')) try: tsk.save() # TODO: call Backlog.update_estimate to sum up all task estimates return HttpResponseRedirect('/backlog/%s/tasks'%tsk.item.pk) # return HttpResponse('{"status":"OK"}') except: return error_response(_('Failed to save task estimate.'))
def include_backlog(request, sid, bid): """ Include a backlog item into this sprint. If this item is assigned to another sprint, returns an error. User permission: change_sprint and must be scrum master of the sprint. """ try: bitem = get_object_or_404(Backlog, pk=bid) if bitem.sprint: logger.error( 'include_backlog(sid=%s,bid=%s), item already assigned to %s' % bitem.sprint) return error_response( _('This item is already assigned to another sprint')) sprint = get_object_or_404(Sprint, pk=sid) if sprint.master != request.user: logger.error('include_backlog() user Not sprint master') return error_response(_('Permission denied')) bitem.sprint = sprint bitem.save() except Exception, e: logger.error('include_backlog(sid=%s,bid=%s) failed:%s' % (sid, bid, e)) return error_response(_('Error saving backlog item in the sprint.'))
def pick_task(request, tid=None): """ A team member picks a task to work on. This is an Ajax call. There should be a way to notify others that somebody picked a task. A simple way is to save this operation in a database table, and others using the same page will check it up and once applied will put a mark there so as not to repeat it. This can be done using a timestamp. Operations with timestamp later than the last visit time, will be applied, or else ignored. """ if not tid: tid = request.REQUEST.get('tid', None) if tid: task = get_object_or_404(Task, pk=tid) else: logger.error('pick_task tid not given') return error_response(_('Task ID error')) if task.doer: return error_response( _('This task has been assigned to another member.')) try: task.assign(request.user) except Exception, e: logger.error('pick_task save() failed: %s' % e) return error_response(_('Failed to save, retry later.'))
def save_estimate(request, tid): """ Get or Post/save Backlog(item).estimate. Only product scrum master can save. Query path: /task/<tid>/estimate[?est=4] User permission required: change_backlog """ try: tsk = Task.objects.get(pk=tid) except Task.DoesNotExist: return error_response(_('Task not found'), fmt='json') if request.method == 'GET': return HttpResponse('{"status":"OK","estimate":"%s"}' % (tsk.estimate)) else: # TODO: verify user is a product owner or scrum master tsk.estimate = float(request.POST.get('est')) try: tsk.save() # TODO: call Backlog.update_estimate to sum up all task estimates return HttpResponseRedirect('/backlog/%s/tasks' % tsk.item.pk) # return HttpResponse('{"status":"OK"}') except: return error_response(_('Failed to save task estimate.'))
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/')
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'))
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'))
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/')
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')
@permission_required('sprint.change_sprint') 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') else: return error_response(_('POST only'))
def export_stories(request): """ Download backlog items as CSV file """ # TODO: implement exporting backlog items as a CSV file for download return error_response(_('Not implemented yet'))
def jsonstr(st): """ Convert double quote marks into HTML encoding " """ return st.replace('"', '"') @permission_required('sprint.change_sprint') 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') else: return error_response(_('POST only'))
return error_response(_("No Product ID"), fmt="json") return json_response(Vote.objects.status(pid)) @login_required def vote(request): """ Submit a value for the voting session. """ try: sid, pid, vote = get_params(request, ["sid", "pid", "vote"]) except KeyError, e: logger.error("poker.vote() request.get error: %s" % e) return error_response(_("Not enough parameters"), fmt="json") if not is_team_member(request, pid): return error_response(_("Team member required"), fmt="json") v, created = Vote.objects.get_or_create(session=sid, product=pid, voter=request.user) try: v.vote = vote v.save() return HttpResponse('{"status":"OK"}', content_type="application/json") except Exception, e: logger.error("poker.vote() error: %s" % e) return error_response(_("Error saving your vote"), fmt="json") @login_required def collect(request): """ Collect all submitted votes. @return: {"status":"STARTED","votes":{"<uid>":"<vote>","1":3,..}}
return error_response(_('No Product ID'), fmt='json') return json_response(Vote.objects.status(pid)) @login_required def vote(request): """ Submit a value for the voting session. """ try: sid, pid, vote = get_params(request, ['sid', 'pid', 'vote']) except KeyError, e: logger.error('poker.vote() request.get error: %s' % e) return error_response(_('Not enough parameters'), fmt='json') if not is_team_member(request, pid): return error_response(_('Team member required'), fmt='json') v, created = Vote.objects.get_or_create(session=sid, product=pid, voter=request.user) try: v.vote = vote v.save() return HttpResponse('{"status":"OK"}', content_type='application/json') except Exception, e: logger.error('poker.vote() error: %s' % e) return error_response(_('Error saving your vote'), fmt='json') @login_required def collect(request):