Uncategorized

There4Travel Graphical BurnDown Chart

[nick]…

Graphical Progress Tracker

Mark has asked us to submit a graphical progress report on a weekly basis; much like a burndown chart in Agile methodologies. Thank goodness that Python is here to make life easy 🙂

Python Burndown Chart:

#! /usr/bin/env python
"""
Reporting Tracker
Graphical tracker for progress on the 
project.
NET601 There4Travel Project
author: Nick Burns
updated: 14 May 2013
"""
import sys
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import pylab
import numpy
from collections import defaultdict
from PyQt4 import QtGui
from PyQt4 import QtCore
class TaskStopwatch(QtGui.QWidget):
    def __init__(self):
        super(TaskStopwatch, self).__init__()
 
        # initiate dictionary of tasks
        self.report = ReportData()
        self.tasklist = self.report.task_dictionary
 
        # initiate gui
        self.initUI()

    def initUI(self):
        """ Initiate GUI """

        self.stopwatch = 0
        self.is_running = False

        self.create_widgets()
        self.layout()
        self.stopwatch_timer()

    def stopwatch_timer(self):
        """
        Create a timer that executes every second
        connect to the Stopwatch Lable (display)
        """ 
        self.my_timer = QtCore.QTimer(self)
        self.my_timer.start(1000)
        QtCore.QObject.connect(self.my_timer, QtCore.SIGNAL("timeout()"), self.draw)

    def draw(self):
        self.increment_stopwatch()
        self.stopwatch_display.setText(self.format_stopwatch())

    def create_widgets(self):

        # stopwatch
        self.stopwatch_display = QtGui.QLabel(self.format_stopwatch())

        # button widgets
        self.start_btn = QtGui.QPushButton('Start', self)
        self.stop_btn = QtGui.QPushButton('Stop', self)
        self.pause_btn = QtGui.QPushButton('Pause', self)

        # drop down menu
        self.task_list = QtGui.QComboBox()
        self.task_list.addItems(list(self.tasklist.keys()))
        self.current_task = self.task_list.currentText()

        # % complete widget
        self.complete_label = QtGui.QLabel("% Complete: ")
        self.percent_complete = QtGui.QLineEdit("")
        # connect events with handlers
        self.start_btn.clicked.connect(self.start_handler)
        self.stop_btn.clicked.connect(self.stop_handler)
        self.pause_btn.clicked.connect(self.pause_handler)
        self.task_list.currentIndexChanged.connect(self.task_handler)

    def layout(self):
        """ Align widgets to a grid layout """
        layout = QtGui.QGridLayout()
        layout.addWidget(self.start_btn, 0, 0)
        layout.addWidget(self.stop_btn, 1, 0)
        layout.addWidget(self.pause_btn, 2, 0)
        layout.addWidget(self.task_list, 4, 0)
        layout.addWidget(self.complete_label, 5, 0)
        layout.addWidget(self.percent_complete, 5, 2)
        layout.addWidget(self.stopwatch_display, 0, 2)
        self.setLayout(layout)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle("There4Travel Reporting")

    # define Event Handlers
    def start_handler(self):
        """ start the Stopwatch """
        self.is_running = True 

    def stop_handler(self):
        """ Stop recording for given task """
        if self.is_running:
            self.is_running = False 
        self.update_tasklist()

   def pause_handler(self):
        """ Pause the given task, with a view to resume """
        self.is_running = False

    def task_handler(self):
        """ Get the value of the selected task in task_list drop down menu """
        if not self.is_running:
            self.current_task = self.task_list.currentText()
        else:
            print("Can not change the task at this time. Stopwatch is running.")
            print("the current task is: {}".format(self.current_task))

    def increment_stopwatch(self):
        """ Increment the stopwatch, once every second """
        if self.is_running:
            self.stopwatch += 1

    def format_stopwatch(self):
        """
        Format stopwatch: HH:MM:SS
        """ 
        hours = self.stopwatch // 3600
        minutes = (self.stopwatch - hours*3600) // 60
        seconds = self.stopwatch - hours*3600 - minutes*60
        return "<font color=blue size=6>%.02d:%.02d:%.02d</font>" % (hours, minutes, seconds)

    def update_tasklist(self):
        percent_sofar = int(self.percent_complete.text())
        self.report.task_dictionary["{}".format(self.current_task)][1] = percent_sofar
        self.report.task_dictionary["{}".format(self.current_task)][0] += self.stopwatch
        self.report.write_tasklist() 
        self.stopwatch = 0

class ReportData():
   def __init__(self):
       self.datafile = "./setup/weekly_report_data.csv"
       self.task_dictionary = defaultdict(list)
       self.header = ''

       self.read_tasklist()

   def read_tasklist(self):
       """
       Read in the task list from file
       format into a dictionary:
       {task : [time_spent, %_complete]}

       NOTE: time_sent is in seconds
       """ 
       with open(self.datafile) as f:
           inData = (i.strip().split(',') for i in f.readlines())

       for i in inData:
           # dump the HEADER
           if "HEADER" in i:
               self.header = ', '.join(i)
           else: 
               self.task_dictionary[i[0]] += [int(i[-2]), int(i[-1])]

   def write_tasklist(self):
       """
       Write out the task list to file
       Formatted:
       task,time_spent,percent_complete

       NOTE: time_spent is in seconds
       """ 
       with open(self.datafile, 'w') as f:
           f.write(self.header+'\n')
           for task, times in self.task_dictionary.items():
               f.write(','.join([task, str(times[0]), str(times[1])+'\n']))

class GraphReport():

   def __init__(self):

       report = ReportData()
       self.plot_data = report.task_dictionary
       self.x_data = [t[0] for t in self.plot_data.values()]
       self.x_coords = numpy.arange(len(self.plot_data.keys()))

       self.plot_graph()

   def plot_graph(self):

       fig = plt.figure()
       ax = fig.add_subplot(111)
       ax.bar(self.x_coords, self.x_data, width=0.34)
       ax.set_ylabel('%')

       xTickMarks = list(self.plot_data.keys())
       ax.set_xticks(self.x_coords+0.2)
       xtickNames = ax.set_xticklabels(xTickMarks)
       plt.setp(xtickNames, rotation=90, fontsize=10)

       plt.show()

def main():
   app = QtGui.QApplication(sys.argv)
   t_stopwatch = TaskStopwatch()
   t_stopwatch.show()
   app.exec_()

   print("closed GUI")
   graph = GraphReport()

if __name__ == '__main__':
   main()

Still to do:

Create a shared Dropbox folder for the weekly_report_data.csv file

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s