Esempio n. 1
0
	def __init__(self, placeholder, sprint, allTasks, revisions, **_):
		Chart.__init__(self, placeholder)
		days = [day for day in sprint.getDays()]
		now = Weekday.today()
		futureStarts = minOr(filter(lambda day: day > now, days), None)
		futureIndex = days.index(futureStarts) if futureStarts else None

		self.chart.defaultSeriesType = 'line'
		self.chart.zoomType = 'x'
		self.title.text = ''
		self.plotOptions.line.dataLabels.enabled = True
		self.tooltip.shared = True
		self.credits.enabled = False
		with self.xAxis as xAxis:
			xAxis.tickmarkPlacement = 'on'
			xAxis.maxZoom = 1
			xAxis.title.text = 'Day'
			# Future bar
			if futureIndex is not None:
				xAxis.plotBands = [{
					'color': '#DDD',
					'from': futureIndex - 0.75,
					'to': len(days) - 0.5
				}]
		self.yAxis.min = 0
		self.yAxis.title.text = 'Hours'
		self.series = seriesList = []

		taskSeries = {
			'id': 'taskSeries',
			'name': 'Tasking',
			'data': [],
			'color': '#4572a7',
			'zIndex': 2
		}
		seriesList.append(taskSeries)

		availSeries = {
			'name': 'Availability',
			'data': [],
			'color': '#aa4643',
			'zIndex': 2
		}
		seriesList.append(availSeries)

		flagSeries = {
			'type': 'flags',
			'data': [],
			'color': '#4572a7',
			'shape': 'flag',
			'onSeries': 'taskSeries',
			'showInLegend': False,
			'y': 16
		}
		seriesList.append(flagSeries)

		if futureIndex == 0:
			futureIndex = 1

		statusToday, hoursToday = None, None
		for day in days[:futureIndex]:
			tasksToday = [revisions[t.id, day] for t in allTasks]
			statusYesterday, hoursYesterday = statusToday, hoursToday
			statusToday = {t: t.status for t in tasksToday if t and not t.deleted}
			hoursToday = {t: t.manHours() for t in tasksToday if t and not t.deleted}
			taskSeries['data'].append(sum(hoursToday.values()))

			if hoursYesterday:
				hoursDiff = {t: hoursToday.get(t, 0) - hoursYesterday.get(t, 0) for t in hoursToday}
				largeChanges = [t for t, h in hoursDiff.iteritems() if abs(h) >= 16]
				if largeChanges:
					texts = []
					for t in largeChanges:
						if t not in hoursYesterday:
							texts.append("<span style=\"color: #f00\">(New +%d)</span> %s" % (t.effectiveHours(), t.name))
						elif hoursDiff[t] > 0:
							texts.append("<span style=\"color: #f00\">(+%d)</span> %s" % (hoursDiff[t], t.name))
						else:
							if t.status in ('in progress', 'not started'):
								texts.append("<span style=\"color: #0a0\">(%d)</span> %s" % (hoursDiff[t], t.name))
							elif t.status == 'complete':
								texts.append("<span style=\"color: #0a0\">(Complete %d)</span> %s" % (hoursDiff[t], t.name))
							else:
								texts.append("<span style=\"color: #999\">(%s %d)</span> %s" % (statuses[t.status].getRevisionVerb(statusYesterday.get(t, 'not started')), hoursDiff[t], t.name))
					flagSeries['data'].append({'x': days.index(day), 'title': alphabet[len(flagSeries['data']) % len(alphabet)], 'text': '<br>'.join(texts)})

		avail = Availability(sprint)
		for day in days:
			availSeries['data'].append(avail.getAllForward(day))

		setupTimeline(self, sprint, ['Projected tasking'])

		# Add commitment percentage to the axis label
		labels = self.xAxis.categories.get()
		for i in range(len(labels)):
			# For future percentages, use today's hours (i.e. don't use the projected hours)
			needed = taskSeries['data'][min(i, futureIndex - 1) if futureIndex else i][1]
			thisAvail = availSeries['data'][i][1]
			pcnt = "%d" % (needed * 100 / thisAvail) if thisAvail > 0 else "inf"
			labels[i] += "<br>%s%%" % pcnt
		self.xAxis.categories = labels
		self.xAxis.labels.formatter = "function() {return this.value.replace('inf', '\u221e');}"

		# Trendline
		data = self.series[0].data.get()
		dailyAvail = dict((day, avail.getAll(day)) for day in days)
		totalAvail = 0
		for daysBack in range(1, (futureIndex or 0) + 1):
			midPoint = [futureIndex - daysBack, data[futureIndex - daysBack][1]]
			if dailyAvail[days[midPoint[0]]] > 0:
				daysBack = min(daysBack + 2, futureIndex)
				startPoint = [futureIndex - daysBack, data[futureIndex - daysBack][1]]
				totalAvail = sum(dailyAvail[day] for day in days[startPoint[0] : midPoint[0]])
				break

		if totalAvail > 0 and startPoint[0] != midPoint[0]:
			slope = (midPoint[1] - startPoint[1]) / (midPoint[0] - startPoint[0])
			slopePerAvail = slope * (midPoint[0] - startPoint[0]) / totalAvail
			points, total = [], midPoint[1]
			total = taskSeries['data'][futureIndex - 1][1]
			points.append([futureIndex - 1, total])
			for i in range(futureIndex, len(days)):
				total += slopePerAvail * dailyAvail[days[i]]
				points.append([i, total])
			seriesList.append({
				'name': 'Projected tasking',
				'data': points,
				'color': '#666',
				'dataLabels': {'formatter': "function() {return (this.point.x == %d) ? parseInt(this.y, 10) : null;}" % (len(days) - 1)},
				'marker': {'symbol': 'circle'},
				'zIndex': 1
			})