Beispiel #1
0
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)
Beispiel #2
0
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
Beispiel #3
0
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()
Beispiel #4
0
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)
Beispiel #5
0
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\">&nbsp;%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>"
Beispiel #6
0
    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
Beispiel #7
0
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)
Beispiel #8
0
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('/')
Beispiel #9
0
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")
Beispiel #10
0
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 &ndash; Only sprint members can view tasks</label></div>"
	print "<div><input type=\"checkbox\" name=\"hidden\" id=\"flag-hidden\"><label for=\"flag-hidden\">Hidden &ndash; Only sprint members can see the sprint</label></div>"
	print "</td></tr>"
	print "<tr><td class=\"left\">&nbsp;</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>"
Beispiel #11
0
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')
Beispiel #12
0
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)
Beispiel #13
0
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)