def taskEditPost(handler, ids, p_hours, p_status, p_goal, p_assigned=[], p_include={}): handler.title("Edit tasks") requirePriv(handler, "Write") allIDs = map(int, uniq(ids.split(","))) ids = map(lambda i: to_int(i, "include", ErrorBox.die), p_include.keys()) if not set(ids) <= set(allIDs): ErrorBox.die("Included tasks don't match query arguments") tasks = dict((id, Task.load(id)) for id in ids) if not all(tasks.values()): ids = [str(id) for (id, task) in tasks.iteritems() if not task] ErrorBox.die( "No %s with %s %s" % ("task" if len(ids) == 1 else "tasks", "ID" if len(ids) == 1 else "IDs", ", ".join(ids)) ) tasks = [tasks[id] for id in ids] if len(set(task.sprint for task in tasks)) > 1: ErrorBox.die("All tasks must be in the same sprint") sprint = (tasks[0] if len(tasks) > 0 else Task.load(allIDs[0])).sprint if sprint.isHidden(handler.session["user"]): ErrorBox.die( "No %s with %s %s" % ("task" if len(ids) == 1 else "tasks", "ID" if len(ids) == 1 else "IDs", ", ".join(ids)) ) if not sprint.canEdit(handler.session["user"]): ErrorBox.die("You don't have permission to modify this sprint") assignedids = set(to_int(i, "assigned", ErrorBox.die) for i in p_assigned) changes = { "assigned": False if assignedids == set() else {User.load(assignedid) for assignedid in assignedids}, "hours": False if p_hours == "" else int(p_hours), "status": False if p_status == "" else p_status, "goal": False if p_goal == "" else Goal.load(int(p_goal)), } if changes["assigned"] and not all(changes["assigned"]): ErrorBox.die("Invalid assignee") if changes["assigned"] and not set(changes["assigned"]).issubset(sprint.members): ErrorBox.die("Unable to assign tasks to non-sprint members") if changes["goal"] and changes["goal"].sprint != sprint: ErrorBox.die("Unable to set goal to a goal outside the sprint") changed = set() for task in tasks: for field, value in changes.iteritems(): if value is not False and getattr(task, field) != value: setattr(task, field, value) changed.add(task) Event.taskUpdate(handler, task, field, value) if len(changed) == 0: delay(handler, WarningBox("No changes necessary", close=3, fixed=True)) else: for task in changed: task.saveRevision(handler.session["user"]) delay(handler, SuccessBox("Updated %d %s" % (len(changed), "task" if len(changed) == 1 else "tasks"))) redirect("/sprints/%d" % sprint.id)
def newTaskPost(handler, p_group, p_name, p_goal, p_status, p_hours, p_assigned=[]): def die(msg): print msg done() requirePriv(handler, "User") handler.wrappers = False groupid = to_int(p_group, "group", die) group = Group.load(groupid) if not group or group.sprint.isHidden(handler.session["user"]): die("No group with ID <b>%d</b>" % groupid) sprint = group.sprint if not (sprint.isActive() or sprint.isPlanning()): die("Unable to modify inactive sprint") elif not sprint.canEdit(handler.session["user"]): die("You don't have permission to modify this sprint") if p_name.strip() == "": die("Task must have a non-empty name") assignedids = set(to_int(i, "assigned", die) for i in p_assigned) assigned = set(User.load(assignedid) for assignedid in assignedids) if assigned == set(): assigned.add(handler.session["user"] if handler.session["user"] in sprint.members else sprint.owner) if not all(assigned): die("Invalid assignee") goalid = to_int(p_goal, "goal", die) if goalid != 0: goal = Goal.load(goalid) if not goal: die("No goal with ID <b>%d</b>" % goalid) if goal.sprint != group.sprint: die("Goal does not belong to the correct sprint") hours = to_int(p_hours, "hours", die) task = Task(groupid, group.sprintid, handler.session["user"].id, goalid, p_name, p_status, hours) task.assigned |= assigned task.save() handler.responseCode = 299 delay( handler, """ <script type=\"text/javascript\"> $(document).ready(function() { $('#task%d').effect('highlight', {}, 3000); }); </script>""" % task.id, ) delay(handler, SuccessBox("Added task <b>%s</b>" % task.safe.name, close=3, fixed=True)) Event.newTask(handler, task)
def assignGroupGoalPost(handler, id, p_goal): def die(msg): print msg done() handler.title('Manage Group') requirePriv(handler, 'User') handler.wrappers = False id = int(id) group = Group.load(id) if not group: ErrorBox.die('Invalid Group', "No group with ID <b>%d</b>" % id) if p_goal == '0': goal = None else: goal = Goal.load(int(p_goal)) if not goal: ErrorBox.die('Invalid Goal', "No goal with ID <b>%d</b>" % int(p_goal)) elif not goal.sprint == group.sprint: ErrorBox.die('Invalid Goal', "Selected goal is not part of the correct sprint") for task in group.getTasks(): if task.goal != goal: task.goal = goal if task.creator == handler.session['user'] and (dateToTs(getNow()) - task.timestamp) < 5*60: task.save() else: task.saveRevision(handler.session['user']) #TODO Event # NO redirect("/sprints/%d#group%d" % (group.sprintid, group.id))
def sprintInfoPost(handler, id, p_name, p_start, p_end, p_goals, p_members = None, p_clear = [], p_private = False, p_hidden = False): def die(msg): print msg done() handler.wrappers = False if not handler.session['user']: die("You must be logged in to modify sprint info") id = to_int(id, 'id', die) p_members = to_int(p_members, 'members', die) sprint = Sprint.load(id) if not sprint or sprint.isHidden(handler.session['user']): die("There is no sprint with ID %d" % id) if sprint.owner != handler.session['user']: die("You must be the scrummaster to modify sprint information") try: start = re.match("^(\d{1,2})/(\d{1,2})/(\d{4})$", p_start) if not start: raise ValueError month, day, year = map(int, start.groups()) start = datetime(year, month, day, 0, 0, 0) except ValueError: die("Malformed start date: %s" % stripTags(p_start)) try: end = re.match("^(\d{1,2})/(\d{1,2})/(\d{4})$", p_end) if not end: raise ValueError month, day, year = map(int, end.groups()) end = datetime(year, month, day, 23, 59, 59) except ValueError: die("Malformed end date: %s" % stripTags(p_end)) msg = Sprint.validateDates(start, end, tsToDate(sprint.start), tsToDate(sprint.end)) if msg: die(msg) goals = map(Goal.load, to_int(p_goals.keys(), 'goals', die)) if not all(goals): die("One or more goals do not exist") members = set(map(User.load, p_members)) if p_members else set() if not all(members): die("One or more members do not exist") if sprint.owner not in members: die("The scrummaster (%s) must be a sprint member" % sprint.owner) tasks = sprint.getTasks() changedTasks = set() avail = Availability(sprint) addMembers = set(members) - set(sprint.members) delMembers = set(sprint.members) - set(members) for user in delMembers: for task in filter(lambda task: user in task.assigned, tasks): print "Removing %s from %d<br>" % (user, task.id) task.assigned -= {user} if len(task.assigned) == 0: print "Adding %s to %d<br>" % (sprint.owner, task.id) task.assigned = {sprint.owner} changedTasks.add(task) avail.delete(user) sprint.members -= {user} # For event dispatching changes = OrderedDict([ ('name', None if sprint.name == p_name else p_name), ('start', None if tsToDate(sprint.start) == start else start), ('end', None if tsToDate(sprint.end) == end else end), ('addMembers', addMembers), ('delMembers', delMembers), # Updated later ('addGoals', []), ('removeGoals', []), ('addFlags', []), ('removeFlags', []), ]) sprint.members |= addMembers sprint.name = p_name p_private = (p_private or p_hidden) # Hidden implies Private for flagName, flagValue in (('private', p_private), ('hidden', p_hidden)): if flagValue and flagName not in sprint.flags: sprint.flags.add(flagName) changes['addFlags'].append(flagName) elif not flagValue and flagName in sprint.flags: sprint.flags.remove(flagName) changes['removeFlags'].append(flagName) if dateToTs(start) != sprint.start or dateToTs(end) != sprint.end: sprint.start = dateToTs(start) sprint.end = dateToTs(end) avail.trim() sprint.save() for id in p_goals: goal = Goal.load(int(id)) if goal.name != p_goals[id]: if goal.name: changes['removeGoals'].append(goal.name) if p_goals[id]: changes['addGoals'].append(p_goals[id]) goal.name = p_goals[id] goal.save() if start: for task in sprint.getTasks(includeDeleted = True): for rev in task.getRevisions(): if rev.timestamp < sprint.start: rev.timestamp = sprint.start rev.save() else: break for task in changedTasks: if task.timestamp < sprint.start: task.timestamp = sprint.start if p_clear: ids = [to_int(goalid, 'p_clear', die) for goalid in p_clear] for task in tasks: if task.goal and task.goal.id in ids: task.goal = None changedTasks.add(task) for task in changedTasks: print "Saving new revision for %d<br>" % task.id task.saveRevision(handler.session['user']) handler.responseCode = 299 delay(handler, SuccessBox("Updated info", close = 3, fixed = True)) Event.sprintInfoUpdate(handler, sprint, changes)
def sprintPost(handler, sprintid, p_id, p_rev_id, p_field, p_value): def die(msg): print msg done() handler.wrappers = False sprintid = to_int(sprintid, 'sprintid', die) p_id = to_int(p_id, 'id', die) p_rev_id = to_int(p_rev_id, 'rev_id', die) if not handler.session['user']: die("You must be logged in to modify tasks") sprint = Sprint.load(sprintid) if not sprint or sprint.isHidden(handler.session['user']): die("There is no sprint with ID %d" % sprintid) elif not (sprint.isActive() or sprint.isPlanning()): die("Unable to modify inactive sprint") elif not sprint.canEdit(handler.session['user']): die("You don't have permission to modify this sprint") # Special case group moves; p_id is the group ID, not task task = None if p_field != 'groupmove': task = Task.load(p_id) if not task: die("Task %d does not exist" % p_id) if task.sprint != sprint: die("Attempting to modify task outside the specified sprint") if task.revision != p_rev_id: #TODO Implement collision support die("Collision with %s detected. Changes not saved" % task.creator) if p_value.strip() == '': die("Value cannot be empty") if p_field in ['status', 'name', 'goal', 'assigned', 'hours', 'deleted']: for case in switch(p_field): if case('status') or case('name'): parsedValue = p_value break elif case('goal'): parsedValue = None if p_value != '0': parsedValue = Goal.load(to_int(p_value, 'goal', die)) if not parsedValue: die("Unknown goal: <b>%s</b>" % stripTags(p_value)) if parsedValue.sprint != sprint: die("Attempting to use goal outside the specified sprint") break elif case('assigned'): parsedValue = set(User.load(username = username) for username in p_value.split(' ')) if not all(parsedValue): die("Unknown user(s): <b>%s</b>" % stripTags(p_value)) break elif case('hours'): parsedValue = int(p_value) break elif case('deleted'): parsedValue = True if p_value == 'true' else False if p_value == 'false' else die("Bad value for field 'deleted'") break if task.__getattribute__(p_field) != parsedValue: # Only save if the field has changed task.__setattr__(p_field, parsedValue) # Is this within the 5-minute window, by the same user? # If we're in pre-planning, the task's timestamp will be in the future, so (ts - task.timestamp) will be negative, which satisfies the check if task.creator == handler.session['user'] and (dateToTs(getNow()) - task.timestamp) < 5*60: task.save() else: task.saveRevision(handler.session['user']) Event.taskUpdate(handler, task, p_field, parsedValue) elif p_field == 'taskmove': if ':' not in p_value: die("Malformed value") newGroupID, newSeq = map(lambda i: to_int(i, 'value', die), p_value.split(':', 1)) newGroup = Group.load(newGroupID) if not newGroup: die("No group with ID %d" % newGroupID) maxSeq = len(Task.loadAll(groupid = newGroup.id)) if task.group != newGroup: maxSeq += 1 if not 1 <= newSeq <= maxSeq: die("Bad sequence number") task.move(newSeq, newGroup) elif p_field == 'groupmove': group = Group.load(p_id) if not group: die("Group %d does not exist" % p_id) if group.sprint != sprint: die("Attempting to modify group outside the specified sprint") newSeq = to_int(p_value, 'value', die) if not 1 <= newSeq <= len(sprint.getGroups()): die("Bad sequence number") group.move(newSeq) else: die("Unexpected field name: %s" % stripTags(p_field)) handler.responseCode = 299 if task is not None: print task.revision