def adminProjectsMoveSprintsPost(handler, id, p_newproject, p_sprintid = None): handler.title('Move Sprints') requirePriv(handler, 'Admin') project = Project.load(int(id)) if not project: ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id)) p_newproject = to_int(p_newproject, 'newproject', ErrorBox.die) target = Project.load(p_newproject) if not p_sprintid: delay(handler, WarningBox("No sprints to move", close = True)) redirect("/admin/projects/%d" % project.id) sprintids = [to_int(id, 'sprintid', ErrorBox.die) for id in p_sprintid] sprints = [Sprint.load(id) for id in sprintids] if not all(sprints): ErrorBox.die("Invalid sprint ID(s)") for sprint in sprints: sprint.project = target sprint.save() delay(handler, SuccessBox("%s moved" % pluralize(len(sprints), 'sprint', 'sprints'), close = True)) redirect("/admin/projects/%d" % project.id)
def findActiveSprint(handler, project = None, search = None): handler.title('Active Sprint') requirePriv(handler, 'User') if project: projectid = int(project) project = Project.load(projectid) if not project: ErrorBox.die('Load project', "No project with ID <b>%d</b>" % projectid) url = "/sprints/%d" if search: url += "?search=%s" % search sprints = Sprint.loadAllActive(handler.session['user'], project) sprints = filter(lambda sprint: sprint.canView(handler.session['user']), sprints) for case in switch(len(sprints)): if case(0): ErrorBox.die('Active sprint', 'No active sprints found') break if case(1): redirect(url % sprints[0].id) break if case(): print "You are active in multiple sprints%s:<br><br>" % (" in the %s project" % project.safe.name if project else '') for sprint in sprints: print "<a href=\"%s\">%s</a><br>" % (url % sprint.id, sprint.safe.name) break
def exportSprints(handler, project): id = int(project) handler.title('Export Sprint') requirePriv(handler, 'User') project = Project.load(id) if not project: ErrorBox.die('Invalid project', "No project with ID <b>%d</b>" % id) print "<link href=\"/static/jquery.multiselect.css\" rel=\"stylesheet\" type=\"text/css\" />" print "<script src=\"/static/jquery.multiselect.js\" type=\"text/javascript\"></script>" print "<script src=\"/static/sprints-export.js\" type=\"text/javascript\"></script>" print "<style type=\"text/css\">" print "select {width: 50%;}" print "img.format {" print " width: 64px;" print " cursor: pointer;" print " padding: 5px;" print " border: 3px solid #fff;" print "}" print "img.format.selected, .ui-effects-transfer {border: 3px solid #f00;}" print "</style>" print "<h2>Sprints</h2>" print "<select name=\"sprints\" multiple>" for sprint in project.getSprints(): if sprint.canView(handler.session['user']): print "<option value=\"%d\"%s>%s</option>" % (sprint.id, ' selected' if sprint.isActive() else '', sprint.safe.name) print "</select>" print "<h2>Format</h2>" for export in exports.values(): print "<img class=\"format\" src=\"/static/images/%s.png\" title=\"%s\" export-name=\"%s\">" % (export.getIcon(), export.getFriendlyName(), export.getName()) print "<br><br>" print Button('Export', id = 'export-button').positive()
def newSprintPost(handler, p_project, p_name, p_start, p_end, p_members = None, p_private = False, p_hidden = False): def die(msg): print msg done() handler.wrappers = False p_project = int(p_project) if not handler.session['user']: die("You must be logged in to create a new sprint") project = Project.load(p_project) if not project: die("Unknown project ID: %d" % p_project) if p_name == '': die("The sprint name cannot be empty") 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) 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) if msg: die(msg) members = set(User.load(int(memberid)) for memberid in p_members) if None in members: die("Unknown username") if handler.session['user'] not in members: die("The scrummaster (%s) must be a sprint member" % handler.session['user']) sprint = Sprint(project.id, p_name, handler.session['user'].id, dateToTs(start), dateToTs(end)) sprint.members |= members if p_private or p_hidden: sprint.flags.add('private') if p_hidden: sprint.flags.add('hidden') sprint.save() # Make a default 'Miscellaneous' group so there's something to add tasks to Group(sprint.id, 'Miscellaneous', 1, False).save() # Make the standard set of sprint goals Goal.newSet(sprint) handler.responseCode = 299 print "/sprints/%d" % sprint.id Event.newSprint(handler, sprint)
def adminProjectsManage(handler, id): handler.title('Manage Project') requirePriv(handler, 'Admin') project = Project.load(int(id)) if not project: ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id)) undelay(handler) sprints = project.getSprints() otherProjects = sorted((p for p in Project.loadAll() if p != project), key = lambda p: p.name) for sprint in sprints: if 'deleted' in sprint.flags: print "<form method=\"post\" action=\"/admin/projects/%d/cancel-deletion/%d\">" % (project.id, sprint.id) print WarningBox("%s is flagged for deletion during nightly cleanup. %s" % (sprint.link(handler.session['user']), Button('Cancel').mini().post())) print "</form>" print "<a name=\"sprints\"></a>" print "<h3>Sprints</h3>" if len(sprints) > 0: print "<form method=\"post\" action=\"/admin/projects/%d/sprints\">" % project.id for sprint in sprints: print "<input type=\"checkbox\" name=\"sprintid[]\" value=\"%d\"> %s<br>" % (sprint.id, sprint.link(handler.session['user'])) print "<br>" print "Move to project: <select name=\"newproject\">" for p in otherProjects: print "<option value=\"%d\">%s</option>" % (p.id, p.safe.name) print "</select>" print Button('Move').post('move').positive() print "<br><br>" print "Delete sprints (irreversible once finalized):" print Button('Delete').post('delete').negative() print "</form>" else: print "No sprints" print "<a name=\"rename\"></a>" print "<h3>Rename</h3>" print "<form method=\"post\" action=\"/admin/projects/%d/edit\">" % project.id print "Name: <input type=\"text\" name=\"name\" value=\"%s\">" % project.safe.name print Button('Rename', type = 'submit').positive() print "</form>" print "<a name=\"delete\"></a>" print "<h3>Delete</h3>" print "<form method=\"post\" action=\"/admin/projects/%d/delete\">" % project.id if len(project.getSprints()) > 0: if len(otherProjects) > 0: print "Delete <b>%s</b> and move all sprints to <select name=\"newproject\">" % project.safe.name for p in otherProjects: print "<option value=\"%d\">%s</option>" % (p.id, p.safe.name) print "</select>" print Button('Delete', type = 'submit').negative() else: print "Unable to remove the only project if it has sprints" else: print Button("Delete %s" % project.safe.name, type = 'submit').negative() print "</form><br>"
def open(self, event=None, path=None): if self.askForSavingIfNeed(): if path == None: dia = wx.FileDialog(self, "Select a file to open", wildcard="PySaved-files (*.pysa)|*.pysa", style=wx.FD_OPEN) if dia.ShowModal() == wx.ID_OK: path = dia.GetPath() else: return try: print(u"Loading project from: '{0}'".format(path)) p = Project() p.load(self, path) self.project = p self.project.loadAllhiddenmodules(self) self.path = path except: wx.MessageDialog(self, u"Cannot open file: '{0}'".format(path), "Cannot open file", wx.ICON_ERROR).ShowModal() raise
def adminProjectsEditPost(handler, id, p_name): handler.title('Edit Project') requirePriv(handler, 'Admin') project = Project.load(int(id)) if not project: ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id)) project.name = p_name project.save() delay(handler, SuccessBox("Saved project changes", close = True)) redirect("/admin/projects/%d" % project.id)
def adminProjectsPost(handler, p_name): handler.title('Project Management') requirePriv(handler, 'Admin') if Project.load(name = p_name): ErrorBox.die('Add Project', "There is already a project named <b>%s</b>" % stripTags(p_name)) project = Project(p_name) project.save() delay(handler, SuccessBox("Added project <b>%s</b>" % stripTags(p_name), close = True)) Event.newProject(handler, project) redirect('/')
def adminProjectsDeletePost(handler, id, p_newproject = None): handler.title('Delete Project') requirePriv(handler, 'Admin') project = Project.load(int(id)) if not project: ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id)) sprints = project.getSprints() if len(sprints) > 0: if p_newproject is None: ErrorBox.die('Missing Parameter', "No new project specified") p_newproject = to_int(p_newproject, 'newproject', ErrorBox.die) target = Project.load(p_newproject) if not target: ErrorBox.die('Invalid Project', "No target project with ID <b>%d</b>" % p_newproject) for sprint in sprints: sprint.project = target sprint.save() project.delete() delay(handler, SuccessBox("Project deleted", close = True)) redirect("/admin/projects")
def newSprint(handler, project): id = int(project) handler.title('New Sprint') requirePriv(handler, 'User') project = Project.load(id) if not project: ErrorBox.die('Invalid project', "No project with ID <b>%d</b>" % id) sprints = project.getSprints() print "<style type=\"text/css\">" print "table.list td.right > * {width: 400px;}" print "table.list td.right button {width: 200px;}" # Half of the above value print "#select-members {padding-right: 5px;}" print "</style>" print "<script src=\"/static/sprints-new.js\" type=\"text/javascript\"></script>" print InfoBox('', id = 'post-status') print "<form method=\"post\" action=\"/sprints/new\">" print "<table class=\"list\">" print "<tr><td class=\"left\">Project:</td><td class=\"right\">" print "<select id=\"select-project\" name=\"project\">" for thisProject in Project.loadAll(): print "<option value=\"%d\"%s>%s</option>" % (thisProject.id, ' selected' if thisProject == project else '', thisProject.safe.name) print "</select>" print "</td></tr>" print "<tr><td class=\"left\">Name:</td><td class=\"right\"><input type=\"text\" name=\"name\" class=\"defaultfocus\"></td></tr>" print "<tr><td class=\"left\">Planning:</td><td class=\"right\"><input type=\"text\" name=\"start\" class=\"date\" value=\"%s\"></td></tr>" % Weekday.today().strftime('%m/%d/%Y') print "<tr><td class=\"left\">Wrapup:</td><td class=\"right\"><input type=\"text\" name=\"end\" class=\"date\"></td></tr>" print "<tr><td class=\"left no-bump\">Members:</td><td class=\"right\">" print "<select name=\"members[]\" id=\"select-members\" multiple>" # Default to last sprint's members members = {handler.session['user']} if sprints: members |= sprints[-1].members for user in sorted(User.loadAll()): if user.hasPrivilege('User'): print "<option value=\"%d\"%s>%s</option>" % (user.id, ' selected' if user in members else '', user.safe.username) print "</select>" print "</td></tr>" print "<tr><td class=\"left no-bump\">Options:</td><td class=\"right\">" print "<div><input type=\"checkbox\" name=\"private\" id=\"flag-private\"><label for=\"flag-private\">Private – Only sprint members can view tasks</label></div>" print "<div><input type=\"checkbox\" name=\"hidden\" id=\"flag-hidden\"><label for=\"flag-hidden\">Hidden – Only sprint members can see the sprint</label></div>" print "</td></tr>" print "<tr><td class=\"left\"> </td><td class=\"right\">" print Button('Save', id = 'save-button', type = 'button').positive() print Button('Cancel', id = 'cancel-button', type = 'button').negative() print "</td></tr>" print "</table>" print "</form>"
def apiProjectActive(handler, id, action): def die(msg): print toJS({'error': msg}) done() id = int(id) handler.wrappers = False project = Project.load(id) if not project: die("No project with ID %d" % id) sprints = sorted(filter(lambda sprint: sprint.isActive() and not sprint.isHidden(handler.session['user']), project.getSprints()), lambda x, y: cmp(x.start, y.start)) if len(sprints): redirect("/api/sprints/%d/%s" % (sprints[-1].id, action)) else: die('No active sprints found')
def adminProjectsCancelSprintDeletionPost(handler, projectid, id): handler.title('Undelete Sprint') requirePriv(handler, 'Admin') project = Project.load(int(projectid)) if not project: ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(projectid)) sprint = Sprint.load(int(id)) if not sprint: ErrorBox.die('Invalid Sprint', "No sprint with ID <b>%d</b>" % int(id)) if sprint.project != project: # We really don't use the project at all, but it's the principle of the thing ErrorBox.die('Invalid Sprint', "Project/sprint mismatch") if 'deleted' not in sprint.flags: ErrorBox.die('Invalid Sprint', "Sprint is not deleted") sprint.flags.remove('deleted') sprint.save() Event.undeleteSprint(handler, sprint) delay(handler, SuccessBox("Deletion canceled for %s" % sprint.link(handler.session['user']), close = True)) redirect("/admin/projects/%d" % project.id)
def adminProjectsDeleteSprintsPost(handler, id, p_newproject = None, p_sprintid = None): # p_newproject is part of the form because of adminProjectsMoveSprintsPost; it's unused here handler.title('Delete Sprints') requirePriv(handler, 'Admin') project = Project.load(int(id)) if not project: ErrorBox.die('Invalid Project', "No project with ID <b>%d</b>" % int(id)) if not p_sprintid: delay(handler, WarningBox("No sprints to delete", close = True)) redirect("/admin/projects/%d" % project.id) sprintids = [to_int(id, 'sprintid', ErrorBox.die) for id in p_sprintid] sprints = [Sprint.load(id) for id in sprintids] if not all(sprints): ErrorBox.die("Invalid sprint ID(s)") for sprint in sprints: sprint.flags.add('deleted') sprint.save() Event.deleteSprint(handler, sprint) delay(handler, SuccessBox("%s queued for deletion. To delete now, run the <a href=\"/admin/cron\">Sprint Cleanup cron job</a>" % pluralize(len(sprints), 'sprint', 'sprints'), close = True)) redirect("/admin/projects/%d" % project.id)