# Kanbanara Metrics Component
# Written by Rebecca Shalfield between 2013 and 2017
# Copyright (c) Rebecca Shalfield 2013-2017
# Released under the GNU AGPL v3

'''Kanbanara's Metrics Component'''

import datetime
import logging
import os
import re
import urllib.parse

import cherrypy
from kanbanara import Kanbanara
import pygal
from pygal.style import LightStyle
from pymongo import MongoClient


class Metrics(Kanbanara):
    '''Kanbanara's Metrics Component'''

    @cherrypy.expose
    def duedateperformance(self):
        """Displays the Due Date Performance chart"""
        username = Kanbanara.check_authentication(f'/{self.component}/duedateperformance')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        epoch = datetime.datetime.utcnow()
        content.append(Kanbanara.header(self, 'duedateperformance', "Due Date Performance"))
        content.append(Kanbanara.filter_bar(self, 'duedateperformance'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'duedateperformance',
                                                              'Due Date Performance'))
        states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted', 'completed', 'closed'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        acceptancetestingaccepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        completed_states = self.get_custom_states_mapped_onto_metastates(['completed'])
        closed_states = self.get_custom_states_mapped_onto_metastates(['closed'])
        owner_reviewer_search_criteria['deadline'] = {'$exists': True, '$nin': [[], '', None]}
        number_of_early = 0
        number_of_late = 0
        number_of_ontime = 0
        for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
            day_epoch = card_document['deadline']
            state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
            for acceptancetestingaccepted_state in acceptancetestingaccepted_states:
                acceptancetestingaccepted = state_metrics.get(acceptancetestingaccepted_state, 0)
                if acceptancetestingaccepted:
                    break
            
            for completed_state in completed_states:
                completed = state_metrics.get(completed_state, 0)
                if completed:
                    break
            
            for closed_state in closed_states:
                closed = state_metrics.get(closed_state, 0)
                if closed:
                    break

            if ((acceptancetestingaccepted and acceptancetestingaccepted.date() == day_epoch.date()) or
                (completed and completed.date() == day_epoch.date()) or
                (closed and closed.date() == day_epoch.date())):
                number_of_ontime += 1
            elif ((acceptancetestingaccepted and acceptancetestingaccepted.date() > day_epoch.date()) or
                  (completed and completed.date() > day_epoch.date()) or
                  (closed and closed.date() > day_epoch.date())):
                number_of_late += 1
            elif ((acceptancetestingaccepted and acceptancetestingaccepted.date() < day_epoch.date()) or
                  (completed and completed.date() < day_epoch.date()) or
                  (closed and closed.date() < day_epoch.date())):
                number_of_early += 1

        number_of_documents = sum([number_of_late, number_of_early, number_of_ontime])
        if number_of_late:
            late_percentage = number_of_late/number_of_documents
        else:
            late_percentage = 0

        if number_of_early:
            early_percentage = number_of_early/number_of_documents
        else:
            early_percentage = 0

        if number_of_ontime:
            ontime_percentage = number_of_ontime/number_of_documents
        else:
            ontime_percentage = 0

        pie_chart = pygal.Pie(style=LightStyle)
        pie_chart.title = self.assemble_chart_title('Due-Date Performance', project, release, iteration, [])
        pie_chart.add('Early', early_percentage)
        pie_chart.add('Late', late_percentage)
        pie_chart.add('On time', ontime_percentage)
        pie_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def emotions_chart(self):
        """Displays the Emotions chart"""
        username = Kanbanara.check_authentication(f'/{self.component}/emotions_chart')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        epoch = datetime.datetime.utcnow()
        content = []
        content.append(Kanbanara.header(self, 'emotions_chart', "Emotions Chart"))
        content.append(Kanbanara.filter_bar(self, 'emotions_chart'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'emotions_chart', 'Emotions Chart'))
        horizontalbar_chart = pygal.HorizontalBar(style=LightStyle)
        pie_chart = pygal.Pie(style=LightStyle)
        horizontalbar_chart.title = self.assemble_chart_title('Emotions Chart', project, release, iteration, [])
        pie_chart.title = self.assemble_chart_title('Emotions Chart', project, release, iteration, [])
        required_columns, required_states = self.get_displayable_columns()
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, required_states)
        emotion_distincts = self.cards_collection.find({'emotion': {'$nin': [[], '', None]}}).distinct('emotion')
        emotion_counts = []
        for emotion in emotion_distincts:
            owner_reviewer_search_criteria['emotion'] = emotion
            emotion_count = self.cards_collection.find(owner_reviewer_search_criteria).count()
            emotion_counts.append(emotion_count)

        number_of_documents = sum(emotion_counts)
        for emotion, emotion_count in zip(emotion_distincts, emotion_counts):
            if emotion_count:
                percentage = round((emotion_count / number_of_documents) * 100)
                horizontalbar_chart.add(emotion.capitalize(), [{'value': emotion_count,
                                                                'tooltip': emotion+' : '+str(emotion_count)+' : '+str(percentage)+'%'}])
                pie_chart.add(emotion.capitalize(), [{'value': emotion_count,
                                                      'tooltip': emotion+' : '+str(emotion_count)+' : '+str(percentage)+'%'}])

        horizontalbar_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append('<table class="sidebyside"><tr><td><figure>')
        content.append(f'<embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())}.svg" />')
        content.append('</figure></td><td>')
        pie_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp())+1)+'.svg'))
        content.append('<figure>')
        content.append(f'<embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())+1}.svg" />')
        content.append('</figure></td></tr></table></div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    def __init__(self):
        """Initialisation"""
        self.component = 'metrics'

        Kanbanara.__init__(self)

        self.current_dir = os.path.dirname(os.path.abspath(__file__))

        logging.basicConfig(level=logging.DEBUG,
                            format='%(asctime)s - %(levelname)s - %(message)s',
                            filename=os.path.join(self.current_dir, '..', 'logs', 'kanbanara.log'),
                            filemode='w'
                           )

        # Initialisation Settings
        self.mongodb_host, self.mongodb_port, self.mongodb_username, self.mongodb_password, self.mongodb_bindir = Kanbanara.read_mongodb_ini_file(self)

        # Connect to MongoDB on given host and port
        if self.mongodb_username and self.mongodb_password:
            modified_username = urllib.parse.quote_plus(self.mongodb_username)
            modified_password = urllib.parse.quote_plus(self.mongodb_password)
            connection = MongoClient('mongodb://' + modified_username + ':' + modified_password + '@' +
                                     self.mongodb_host + ':' + str(self.mongodb_port))
        else:
            connection = MongoClient(self.mongodb_host, self.mongodb_port)

        # Connect to 'kanbanara' database, creating if not already exists
        kanbanara_db = connection['kanbanara']

        # Connect to 'projects' collection
        self.projects_collection = kanbanara_db['projects']
        for attribute in ['project']:
            self.projects_collection.create_index(attribute, unique=True, background=True)

        # Connect to 'sessions' collection
        self.sessions_collection = kanbanara_db['sessions']
        for attribute in ['session_id']:
            self.sessions_collection.create_index(attribute, unique=True, background=True)

        for attribute in ['lastaccess']:
            self.sessions_collection.create_index(attribute, unique=False, background=True)

        # Connect to 'members' collection
        self.members_collection = kanbanara_db['members']
        for attribute in ['username']:
            # TODO - username attribute should be unique but get error when unique=true set
            self.members_collection.create_index(attribute, unique=False, background=True)

        # Connect to 'cards' collection
        self.cards_collection = kanbanara_db['cards']

    @cherrypy.expose
    def cfd(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """ Generates a cumulative flow diagram (CFD) """
        Kanbanara.check_authentication(f'/{self.component}/cfd')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        content = []
        content.append(Kanbanara.header(self, 'cfd', "Cumulative Flow Diagram"))
        content.append(Kanbanara.filter_bar(self, 'cfd'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.cumulative_flow_diagram_chart(number_of_days, division, rawdatarequired, csvrequired))
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def divisionoflabour(self):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/divisionoflabour')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        epoch = datetime.datetime.utcnow()
        content = []
        content.append(Kanbanara.header(self, 'divisionoflabour', "Division Of Labour"))
        content.append(Kanbanara.filter_bar(self, 'divisionoflabour'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'divisionoflabour', 'Division Of Labour'))
        fullnames_and_usernames = self.get_project_members([project])
        busy_order_user_distincts = self.get_busy_order_user_distincts(fullnames_and_usernames, project, release, iteration)
        horizontalbar_chart = pygal.HorizontalBar(style=LightStyle)
        bar_chart = pygal.Bar(x_label_rotation=90, style=LightStyle)
        chart_title = self.assemble_chart_title('Division Of Labour', project, release, iteration, [])
        horizontalbar_chart.title = chart_title
        bar_chart.title = chart_title
        x_labels = []
        epic_values = []
        feature_values = []
        story_values = []
        enhancement_values = []
        defect_values = []
        task_values = []
        test_values = []
        bug_values = []
        transient_values = []
        for (user_count, epicCount, featureCount, storyCount, enhancementCount, defectCount, taskCount, test_count,
             bugCount, transientCount, fullname_distinct, userDistinct) in busy_order_user_distincts:
            horizontalbar_chart.add(fullname_distinct, [{'value':user_count,'tooltip':fullname_distinct+' : '+str(user_count)}])
            x_labels.append(fullname_distinct)
            epic_values.append({'value': epicCount, 'tooltip': fullname_distinct+' : Epic : '+str(epicCount)})
            feature_values.append({'value':featureCount,'tooltip':fullname_distinct+' : Feature : '+str(featureCount)})
            story_values.append({'value': storyCount, 'tooltip': fullname_distinct+' : Story : '+str(storyCount)})
            enhancement_values.append({'value': enhancementCount, 'tooltip': fullname_distinct+' : Enhancement : '+str(enhancementCount)})
            defect_values.append({'value': defectCount, 'tooltip': fullname_distinct+' : Defect : '+str(defectCount)})
            task_values.append({'value': taskCount, 'tooltip': fullname_distinct+' : Task : '+str(taskCount)})
            test_values.append({'value': test_count, 'tooltip': fullname_distinct+' : Test : '+str(test_count)})
            bug_values.append({'value': bugCount, 'tooltip': fullname_distinct+' : Bug : '+str(bugCount)})
            transient_values.append({'value': transientCount, 'tooltip': fullname_distinct+' : Bug : '+str(transientCount)})

        horizontalbar_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append('<table class="sidebyside"><tr><td>')
        content.append('<figure>')
        content.append(f'<embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())}.svg" />')
        content.append('</figure></td><td>')
        bar_chart.x_labels = x_labels
        bar_chart.add('Epic',        epic_values)
        bar_chart.add('Feature',     feature_values)
        bar_chart.add('Story',       story_values)
        bar_chart.add('Enhancement', enhancement_values)
        bar_chart.add('Defect',      defect_values)
        bar_chart.add('Task',        task_values)
        bar_chart.add('Test',        test_values)
        bar_chart.add('Bug',         bug_values)
        bar_chart.add('Untracked',   transient_values)
        bar_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp())+1)+'.svg'))
        content.append('<figure>')
        content.append(f'<embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())+1}.svg" />')
        content.append('</figure></td></tr></table>')
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def tagschart(self):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/tagschart')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        epoch = datetime.datetime.utcnow()
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        content = []
        content.append(Kanbanara.header(self, 'tagschart', "Tags Chart"))
        content.append(Kanbanara.filter_bar(self, 'tagschart'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'tagschart', 'Tags Chart'))
        required_columns, required_states = self.get_displayable_columns()
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, required_states)
        horizontalbar_chart = pygal.HorizontalBar(style=LightStyle)
        pie_chart = pygal.Pie(style=LightStyle)
        horizontalbar_chart.title = self.assemble_chart_title('Tags Chart', project, release, iteration, [])
        pie_chart.title = self.assemble_chart_title('Tags Chart', project, release, iteration, [])
        tags = {}
        for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
            if 'tags' in card_document:
                card_tags = card_document['tags'].split(' ')
                for card_tag in card_tags:
                    if card_tag in tags:
                        tags[card_tag] += 1
                    else:
                        tags[card_tag] = 1

        for tag in tags:
            horizontalbar_chart.add(tag, [{'value':tags[tag],'tooltip':tag+' : '+str(tags[tag])}])
            pie_chart.add(tag, [{'value':tags[tag],'tooltip':tag+' : '+str(tags[tag])}])

        horizontalbar_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append('<table class="sidebyside"><tr><td><figure>')
        content.append(f'<embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())}.svg" />')
        content.append('</figure></td><td>')
        pie_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp())+1)+'.svg'))
        content.append('<figure>')
        content.append(f'<embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())+1}.svg" />')
        content.append('</figure></td></tr></table></div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def recidivism_rate(self):
        """comment"""
        Kanbanara.check_authentication(f'/{self.component}/recidivism_rate')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        content = []
        content.append(Kanbanara.header(self, 'recidivism_rate', "Recidivism Rate"))
        content.append(Kanbanara.filter_bar(self, 'recidivism_rate'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'recidivism_rate', 'Recidivism Rate'))
        content.append('<table class="sidebyside"><tr><td width="20%"></td><td width="60%">')
        content.append(Kanbanara.component_recidivism_rate(self))
        content.append('</td><td width="20%"></td></tr></table>')
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def teamautonomy(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/teamautonomy')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])        
        content = []
        content.append(Kanbanara.header(self, 'teamautonomy', "Team Autonomy"))
        content.append(Kanbanara.filter_bar(self, 'teamautonomy'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'teamautonomy',
                                                          'Team Autonomy [CURRENTLY UNDER CONSTRUCTION!]'))
        content.append(self.assemble_chart_buttons('teamautonomy', number_of_days, division,
                                                   project, release, iteration))
        all_states = self.get_custom_states_mapped_onto_metastates(['development', 'developed',
                                                                    'unittesting', 'unittestingaccepted',
                                                                    'integrationtesting', 'integrationtestingaccepted',
                                                                    'systemtesting', 'systemtestingaccepted',
                                                                    'acceptancetesting', 'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, all_states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        owner_reviewer_search_criteria['development'] = {"$exists": True}
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         

                if accepted and accepted.date() == day_epoch.date():
                    total_cycle_times += int((accepted - development)/self.timedelta_day)
                    number_of_documents += 1
                elif testing and testing.date() == day_epoch.date():
                    total_cycle_times += int((testing - development)/self.timedelta_day)
                    number_of_documents += 1
                elif developed and developed.date() == day_epoch.date():
                    total_cycle_times += int((developed - development)/self.timedelta_day)
                    number_of_documents += 1
                elif development and development.date() == day_epoch.date():
                    number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Team Autonomy', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def valuefailuredemand(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/valuefailuredemand')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'valuefailuredemand', "Value/Failure Demand"))
        content.append(Kanbanara.filter_bar(self, 'valuefailuredemand'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'valuefailuredemand',
                                                          'Value/Failure Demand [CURRENTLY UNDER CONSTRUCTION!]'))
        content.append(self.assemble_chart_buttons('valuefailuredemand', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break
                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Value/Failure Demand', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def abandoned(self, number_of_days="", division=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/abandoned')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        content = []
        content.append(Kanbanara.header(self, 'abandoned', "Abandoned"))
        content.append(Kanbanara.filter_bar(self, 'abandoned'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.abandoned_chart(number_of_days, division))
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def blockages(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/blockages')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        content = []
        content.append(Kanbanara.header(self, 'blockages', "Blockages"))
        content.append(Kanbanara.filter_bar(self, 'blockages'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'blockages', 'Blockages'))
        content.append(self.assemble_chart_buttons('blockages', number_of_days, division, project, release, iteration))
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        uncondensed_column_states = workflow_index.get('uncondensed_column_states', [])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, uncondensed_column_states)
        owner_reviewer_search_criteria['blockedhistory'] = {"$exists": True}
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_labels = self.assemble_chart_day_labels(number_of_days, division)
        chart.title = self.assemble_chart_title('Blockages', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                blockedhistory = card_document.get('blockedhistory', [])
                for bhd, blockedhistory_entry in enumerate(blockedhistory):
                    blocked_start = 0
                    blocked_end = 0
                    if blockedhistory_entry['action'] == 'blocked':
                        blocked_start = blockedhistory_entry['datetime']
                        if bhd < len(blockedhistory)-1:
                            if blockedhistory[bhd+1]['action'] == 'unblocked':
                                blocked_end = blockedhistory[bhd+1]['datetime']

                    if blocked_start and blocked_end:
                        if blocked_start.date() <= past_epoch.date() and blocked_end.date() >= past_epoch.date():
                            number_of_documents += 1

                    elif blocked_start:
                        if blocked_start.date() <= past_epoch.date():
                            number_of_documents += 1

            day_values.append(number_of_documents)

        chart.add(project, day_values)

        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def bugs_and_defects(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        # TODO - These two card types should each have their own chart!
        username = Kanbanara.check_authentication(f'/{self.component}/bugs_and_defects')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        uncondensed_column_states = workflow_index.get('uncondensed_column_states', [])
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'bugs_and_defects', "Bugs and Defects"))
        content.append(Kanbanara.filter_bar(self, 'bugs_and_defects'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'bugs_and_defects', 'Bugs and Defects'))
        content.append(self.assemble_chart_buttons('bugs_and_defects', number_of_days, division, project, release, iteration))
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, uncondensed_column_states)
        owner_reviewer_search_criteria['type'] = {'$in': ['bug', 'defect']}
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_labels = self.assemble_chart_day_labels(number_of_days, division)
        chart.title = self.assemble_chart_title('Bugs and Defects', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for condensed_column_state in reversed(condensed_column_states):
                    state_epoch = state_metrics.get(condensed_column_state, 0)
                    if state_epoch and state_epoch.date() == past_epoch.date():
                        number_of_documents += 1
                        break

            day_values.append(number_of_documents)

        chart.add(project, day_values)

        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def bottlenecks(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comments"""
        username = Kanbanara.check_authentication(f'/{self.component}/bottlenecks')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', []) 
        content = []
        content.append(Kanbanara.header(self, 'bottlenecks', "Bottlenecks"))
        content.append(Kanbanara.filter_bar(self, 'bottlenecks'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'bottlenecks',
                                                          'Bottlenecks [CURRENTLY UNDER CONSTRUCTION!]'))
        content.append(self.assemble_chart_buttons('bottlenecks', number_of_days, division, project, release, iteration))
        buffer_column_states = workflow_index.get('buffer_column_states', [])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, buffer_column_states)
        defined_states = self.get_custom_states_mapped_onto_metastates(['defined'])
        analysis_states = self.get_custom_states_mapped_onto_metastates(['analysis'])
        analysed_states = self.get_custom_states_mapped_onto_metastates(['analysed'])
        design_states = self.get_custom_states_mapped_onto_metastates(['design'])
        designed_states = self.get_custom_states_mapped_onto_metastates(['designed'])
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        bar_chart = pygal.Bar(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        defined_values = []
        analysed_values = []
        developed_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            defined_count = 0
            analysed_count = 0
            developed_count = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for defined_state in defined_states:
                    defined = state_metrics.get(defined_state, 0)
                    if defined:
                        break

                for analysis_state in analysis_states:
                    analysis = state_metrics.get(analysis_state, 0)
                    if analysis:
                        break

                for analysed_state in analysed_states:
                    analysed = state_metrics.get(analysed_state, 0)
                    if analysed:
                        break

                for design_state in design_states:
                    design = state_metrics.get(design_state, 0)
                    if design:
                        break                         

                for designed_state in designed_states:
                    designed = state_metrics.get(designed_state, 0)
                    if designed:
                        break                         
                
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                if defined and defined.date() == past_epoch.date():
                    if not analysis or (analysis and defined < analysis):
                        defined_count += 1

                if analysed and analysed.date() == past_epoch.date():
                    if not development or (development and analysed < development):
                        analysed_count += 1

                if developed and developed.date() == past_epoch.date():
                    if not testing or (testing and developed < testing):
                        developed_count += 1

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            defined_values.append(defined_count)
            analysed_values.append(analysed_count)
            developed_values.append(developed_count)

        bar_chart.title = self.assemble_chart_title('Bottlenecks', project, release, iteration, day_labels)
        bar_chart.x_labels = day_labels
        bar_chart.add('Defined',  defined_values)
        bar_chart.add('Analysed', analysed_values)
        bar_chart.add('Developed',developed_values)
        bar_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def burndownchart(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """ Compares story points against time """
        username = Kanbanara.check_authentication(f'/{self.component}/burndownchart')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        content = []
        content.append(Kanbanara.header(self, 'burndownchart', "Burndown Chart"))
        content.append(Kanbanara.filter_bar(self, 'burndownchart'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'burndownchart', "Burndown Chart"))
        content.append(self.assemble_chart_buttons('burndownchart', number_of_days, division, project, release, iteration))
        required_columns, required_states = self.get_displayable_columns()
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, required_states)
        owner_reviewer_search_criteria["estimatedtime"]     = {"$exists": True, '$nin': [[], '', None]}
        owner_reviewer_search_criteria["actualtimehistory"] = {"$exists": True, '$nin': [[], '', None]}
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        _, chart_end_epoch, _ = self.get_project_release_iteration_dates(project, release, iteration)
        if not chart_end_epoch:
            chart_end_epoch = epoch + (self.timedelta_day * 7)

        chartstart_epoch = chart_end_epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        total_estimated_times = 0
        all_total_actual_times = []
        for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
            total_estimated_times += float(card_document['estimatedtime'])

        while day_count < number_of_days:
            day_count += division
            currentday_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            if currentday_epoch < epoch:
                total_actual_times = 0
                for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                    actual_time_for_today = 0
                    if 'actualtimehistory' in card_document:
                        for actualTimehistory_document in card_document['actualtimehistory']:
                            if actualTimehistory_document['datetime'].date() <= currentday_epoch.date():
                                actual_time_for_today = actualTimehistory_document['actualtime']

                    if actual_time_for_today:
                        total_actual_times += float(actual_time_for_today)

            else:
                previous_total_actual_times = 0
                if all_total_actual_times:
                    previous_total_actual_times = all_total_actual_times[-1]

                try:
                    totaltotal_actual_times = sum(all_total_actual_times)
                    total_actual_times = previous_total_actual_times + (totaltotal_actual_times / len(all_total_actual_times))
                except:
                    total_actual_times = previous_total_actual_times

            all_total_actual_times.append(total_actual_times)
            date_format = self.convert_time_to_chart_display_format(currentday_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_value = total_estimated_times-total_actual_times
            if day_value < 0:
                day_value = 0

            day_values.append({'value':day_value,'tooltip':str(day_value)})

        chart.title = self.assemble_chart_title('Burndown Chart', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def controlchart(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/controlchart')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'controlchart', "Control Chart"))
        content.append(Kanbanara.filter_bar(self, 'controlchart'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'controlchart',
                                                          'Control Chart [CURRENTLY UNDER CONSTRUCTION!]'))
        content.append(self.assemble_chart_buttons('controlchart', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Control Chart', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def completedcards(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/completedcards')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'completedcards', "Completed Cards"))
        content.append(Kanbanara.filter_bar(self, 'completedcards'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'completedcards', "Completed Cards"))
        content.append(self.assemble_chart_buttons('completedcards', number_of_days, division,
                                                   project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['closed'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        closed_states = self.get_custom_states_mapped_onto_metastates(['closed'])
        owner_reviewer_search_criteria['resolution'] = 'Released'
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        day_labels = self.assemble_chart_day_labels(number_of_days, division)
        chart.title = self.assemble_chart_title('Completed Cards', project, release, iteration, day_labels)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        chart.x_labels = day_labels
        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for closed_state in closed_states:
                    closed = state_metrics.get(closed_state, 0)
                    if closed:
                        break

                if closed and closed <= past_epoch:
                    number_of_documents += 1

            day_values.append({'value':number_of_documents,'tooltip':str(number_of_documents)})

        chart.add(project, day_values)

        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def velocitychart(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """ Tracks the amount of work completed from release to release """
        username = Kanbanara.check_authentication(f'/{self.component}/velocitychart')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'velocitychart', "Velocity Chart"))
        content.append(Kanbanara.filter_bar(self, 'velocitychart'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'velocitychart',
                                                          'Velocity Chart [CURRENTLY UNDER CONSTRUCTION!]'))
        content.append(self.assemble_chart_buttons('velocitychart', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Velocity Chart', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def earned_business_value(self, number_of_days="", division="", rawdatarequired="",
                              csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/earned_business_value')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'earned_business_value', "Earned Business Value"))
        content.append(Kanbanara.filter_bar(self, 'earned_business_value'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'earned_business_value',
                                                          "Earned Business Value [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('earned_business_value', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Earned Business Value', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def running_tested_features(self, number_of_days="", division="", rawdatarequired="",
                                csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/running_tested_features')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'running_tested_features', "Running Tested Features"))
        content.append(Kanbanara.filter_bar(self, 'running_tested_features'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'running_tested_features',
                                                          "Running Tested Features [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('running_tested_features', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        closed_states = self.get_custom_states_mapped_onto_metastates(['closed'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         
            
                for closed_state in closed_states:
                    closed = state_metrics.get(closed_state, 0)
                    if closed:
                        break                         

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Running Tested Features', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def burnup_chart(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/burnup_chart')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'burnup_chart', "Burnup Chart"))
        content.append(Kanbanara.filter_bar(self, 'burnup_chart'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'burnup_chart',
                                                              "Burnup Chart [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('burnup_chart', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Burnup Chart', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def percentage_of_scope_complete(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/percentage_of_scope_complete')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'percentage_of_scope_complete', "Percentage Of Scope Complete"))
        content.append(Kanbanara.filter_bar(self, 'percentage_of_scope_complete'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'percentage_of_scope_complete',
                                                          "Percentage Of Scope Complete [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('percentage_of_scope_complete', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Percentage Of Scope Complete', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def buffer_burn_rate(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/buffer_burn_rate')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'buffer_burn_rate', "Buffer Burn Rate"))
        content.append(Kanbanara.filter_bar(self, 'buffer_burn_rate'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'buffer_burn_rate',
                                                          "Buffer Burn Rate [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('buffer_burn_rate', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         
                
                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Buffer Burn Rate', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def budget_burn(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/budget_burn')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'budget_burn', "Budget Burn"))
        content.append(Kanbanara.filter_bar(self, 'budget_burn'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'budget_burn',
                                                              "Budget Burn [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('budget_burn', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Budget Burn', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def velocitybyeffort(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """"""
        username = Kanbanara.check_authentication(f'/{self.component}/velocitybyeffort')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'velocitybyeffort', "Velocity by Effort"))
        content.append(Kanbanara.filter_bar(self, 'velocitybyeffort'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'velocitybyeffort',
                                                          "Velocity by Effort [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('velocitybyeffort', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Velocity by Effort', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def quality(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/quality')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'quality', "Quality"))
        content.append(Kanbanara.filter_bar(self, 'quality'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'quality',
                                                              "Quality [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('quality', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                         

                if development:
                    if accepted and accepted.date() == past_epoch.date():
                        total_cycle_times += int((accepted - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif testing and testing.date() == past_epoch.date():
                        total_cycle_times += int((testing - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif developed and developed.date() == past_epoch.date():
                        total_cycle_times += int((developed - development)/self.timedelta_day)
                        number_of_documents += 1
                    elif development and development.date() == past_epoch.date():
                        number_of_documents += 1

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents
            else:
                average_cycle_time = 0

            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)
            day_values.append(average_cycle_time)

        chart.title = self.assemble_chart_title('Quality', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        chart.add(project, day_values)
        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def spc(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/spc')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        content = []
        content.append(Kanbanara.header(self, 'spc', "Statistical Process Control"))
        content.append(Kanbanara.filter_bar(self, 'spc'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'spc',
                                                          "Statistical Process Control [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('spc', number_of_days, division, project, release, iteration))
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, condensed_column_states)
        backlog_states = self.get_custom_states_mapped_onto_metastates(['backlog'])
        defined_states = self.get_custom_states_mapped_onto_metastates(['defined'])
        analysis_states = self.get_custom_states_mapped_onto_metastates(['analysis'])
        analysed_states = self.get_custom_states_mapped_onto_metastates(['analysed'])
        design_states = self.get_custom_states_mapped_onto_metastates(['design'])
        designed_states = self.get_custom_states_mapped_onto_metastates(['designed'])
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        closed_states = self.get_custom_states_mapped_onto_metastates(['closed'])
        owner_reviewer_search_criteria[condensed_column_states[0]] = {"$exists": True}
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)

        chart.title = self.assemble_chart_title('Statistical Process Control', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_lead_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                card_lead_time = 0
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for backlog_state in backlog_states:
                    backlog = state_metrics.get(backlog_state, 0)
                    if backlog:
                        break

                for defined_state in defined_states:
                    defined = state_metrics.get(defined_state, 0)
                    if defined:
                        break

                for analysis_state in analysis_states:
                    analysis = state_metrics.get(analysis_state, 0)
                    if analysis:
                        break

                for analysed_state in analysed_states:
                    analysed = state_metrics.get(analysed_state, 0)
                    if analysed:
                        break

                for design_state in design_states:
                    design = state_metrics.get(design_state, 0)
                    if design:
                        break                         

                for designed_state in designed_states:
                    designed = state_metrics.get(designed_state, 0)
                    if designed:
                        break                         
                
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                        
                
                for closed_state in closed_states:
                    closed = state_metrics.get(closed_state, 0)
                    if closed:
                        break                        

                for state_epoch in [closed, accepted, testing, developed, development, analysed, analysis, defined, backlog]:
                    if state_epoch and past_epoch-self.timedelta_day <= state_epoch <= past_epoch:
                        card_lead_time = int((past_epoch - backlog)/self.timedelta_day)
                        total_lead_times += card_lead_time
                        number_of_documents += 1
                        break

            try:
                average_lead_time = total_lead_times / number_of_documents
            except:
                average_lead_time = 0

            day_values.append(average_lead_time)

        chart.add(project, day_values)

        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def cycletime(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """Cycle time allows you to track the time for work items during development and testing"""
        username = Kanbanara.check_authentication(f'/{self.component}/cycletime')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'cycletime', "Cycle Time"))
        content.append(Kanbanara.filter_bar(self, 'cycletime'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'cycletime',
                                                          "Cycle Time [CURRENTLY UNDER CONSTRUCTION!]"))
        content.append(self.assemble_chart_buttons('cycletime', number_of_days, division, project, release, iteration))
        states = self.get_custom_states_mapped_onto_metastates(['development', 'developed', 'unittesting',
                                                                'unittestingaccepted', 'integrationtesting',
                                                                'integrationtestingaccepted', 'systemtesting',
                                                                'systemtestingaccepted', 'acceptancetesting',
                                                                'acceptancetestingaccepted', 'completed', 'closed'])
        _, _, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, states)
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)

        chart.title = self.assemble_chart_title('Cycle Time', project, release, iteration, day_labels)
        chart.x_labels = day_labels
        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            total_cycle_times = 0
            number_of_documents = 0
            average_cycle_time = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                
                
                if development:
                    for state_epoch in [accepted, testing, developed, development]:
                        if state_epoch and past_epoch-self.timedelta_day <= state_epoch <= past_epoch:
                            if state_epoch > development:
                                total_cycle_times += int((state_epoch - development)/self.timedelta_day)

                            number_of_documents += 1
                            break

            if total_cycle_times:
                average_cycle_time = total_cycle_times / number_of_documents

            day_values.append(average_cycle_time)

        chart.add(project, day_values)

        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def leadtime(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """comment"""
        username = Kanbanara.check_authentication(f'/{self.component}/leadtime')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        content = []
        content.append(Kanbanara.header(self, 'leadtime', "Lead Time"))
        content.append(Kanbanara.filter_bar(self, 'leadtime'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'leadtime', "Lead Time"))
        content.append(self.assemble_chart_buttons('leadtime', number_of_days, division, project, release, iteration))
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        owner_search_criteria, reviewer_search_criteria, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, condensed_column_states)
        backlog_states = self.get_custom_states_mapped_onto_metastates(['backlog'])
        defined_states = self.get_custom_states_mapped_onto_metastates(['defined'])
        analysis_states = self.get_custom_states_mapped_onto_metastates(['analysis'])
        analysed_states = self.get_custom_states_mapped_onto_metastates(['analysed'])
        design_states = self.get_custom_states_mapped_onto_metastates(['design'])
        designed_states = self.get_custom_states_mapped_onto_metastates(['designed'])
        development_states = self.get_custom_states_mapped_onto_metastates(['development'])
        developed_states = self.get_custom_states_mapped_onto_metastates(['developed'])
        testing_states = self.get_custom_states_mapped_onto_metastates(['acceptancetesting'])
        accepted_states = self.get_custom_states_mapped_onto_metastates(['acceptancetestingaccepted'])
        closed_states = self.get_custom_states_mapped_onto_metastates(['closed'])
        owner_reviewer_search_criteria[condensed_column_states[0]] = {"$exists": True}
        chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        epoch = datetime.datetime.utcnow()
        start_epoch = epoch - self.timedelta_month
        day_count = 0
        day_labels = []
        project_document = self.projects_collection.find_one({'project': project})
        if 'start_date' in project_document and project_document['start_date']:
            project_start_date = int(project_document['start_date'])
            project_day_range = int((epoch - project_start_date)/self.timedelta_day)
            if project_day_range > number_of_days:
                start_epoch = project_start_date
                number_of_days = project_day_range

        while day_count < number_of_days:
            day_count += division
            past_epoch = start_epoch + (self.timedelta_day * day_count)
            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)

        chart.title = self.assemble_chart_title('Lead Time', project, release, iteration, day_labels)
        chart.x_labels = day_labels

        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = start_epoch + (self.timedelta_day * day_count)
            total_lead_times = 0
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states, card_document['statehistory'])
                for backlog_state in backlog_states:
                    backlog = state_metrics.get(backlog_state, 0)
                    if backlog:
                        break

                for defined_state in defined_states:
                    defined = state_metrics.get(defined_state, 0)
                    if defined:
                        break

                for analysis_state in analysis_states:
                    analysis = state_metrics.get(analysis_state, 0)
                    if analysis:
                        break

                for analysed_state in analysed_states:
                    analysed = state_metrics.get(analysed_state, 0)
                    if analysed:
                        break

                for design_state in design_states:
                    design = state_metrics.get(design_state, 0)
                    if design:
                        break                         

                for designed_state in designed_states:
                    designed = state_metrics.get(designed_state, 0)
                    if designed:
                        break                         
                
                for development_state in development_states:
                    development = state_metrics.get(development_state, 0)
                    if development:
                        break

                for developed_state in developed_states:
                    developed = state_metrics.get(developed_state, 0)
                    if developed:
                        break

                for testing_state in testing_states:
                    testing = state_metrics.get(testing_state, 0)
                    if testing:
                        break                        

                for accepted_state in accepted_states:
                    accepted = state_metrics.get(accepted_state, 0)
                    if accepted:
                        break                        
                
                for closed_state in closed_states:
                    closed = state_metrics.get(closed_state, 0)
                    if closed:
                        break                        

                card_lead_time = 0
                for state_epoch in [closed, accepted, testing, developed, development, analysed, analysis, defined, backlog]:
                    if state_epoch and past_epoch-self.timedelta_day <= state_epoch <= past_epoch:
                        if past_epoch > backlog:
                            card_lead_time = int((past_epoch - backlog)/self.timedelta_day)

                        total_lead_times += card_lead_time
                        number_of_documents += 1
                        break

            try:
                average_lead_time = total_lead_times / number_of_documents
            except:
                average_lead_time = 0

            day_values.append(average_lead_time)

        chart.add(project, day_values)

        chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        content.append(self.display_chart(username, epoch))
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def index(self):
        """Redirects you to the kanban board"""
        raise cherrypy.HTTPRedirect("/kanban", 302)

    @cherrypy.expose
    def throughput(self, number_of_days="", division="", rawdatarequired="", csvrequired=""):
        """Displays a chart showing the throughput of cards across the kanban board"""
        username = Kanbanara.check_authentication(f'/{self.component}/throughput')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        number_of_days, division = self.metrics_settings(session_document, number_of_days, division)
        member_document = Kanbanara.get_member_document(self, session_document)
        project, release, iteration = self.get_member_project_release_iteration(member_document)
        project_document = self.projects_collection.find_one({'project': project})
        workflow_index = project_document.get('workflow_index', {})
        condensed_column_states = workflow_index.get('condensed_column_states', [])
        content = []
        content.append(Kanbanara.header(self, 'throughput', "Throughput"))
        content.append(Kanbanara.filter_bar(self, 'throughput'))
        content.append(Kanbanara.menubar(self))
        content.append('<div align="center">')
        content.append(self.insert_page_title_and_online_help(session_document, 'throughput', 'Throughput'))
        content.append(self.assemble_chart_buttons('throughput', number_of_days, division, project, release, iteration))
        closed_states = self.get_custom_states_mapped_onto_metastates(['closed'])
        owner_search_criteria, reviewer_search_criteria, owner_reviewer_search_criteria = self.get_filtered_search_criteria(session_document, closed_states)
        owner_reviewer_search_criteria['resolution'] = 'Released'
        line_chart = pygal.Line(x_label_rotation=90, style=LightStyle)
        stackedline_chart = pygal.StackedLine(x_label_rotation=90, style=LightStyle, fill=True)
        epoch = datetime.datetime.utcnow()
        chartstart_epoch = epoch - (self.timedelta_day * number_of_days)
        day_count = 0
        day_labels = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            date_format = self.convert_time_to_chart_display_format(past_epoch, number_of_days, division)
            day_labels.append(date_format)

        chart_title = self.assemble_chart_title('Throughput', project, release, iteration, day_labels)
        line_chart.title = chart_title
        stackedline_chart.title = chart_title
        line_chart.x_labels = day_labels
        stackedline_chart.x_labels = day_labels
        owner_reviewer_search_criteria['project'] = project
        day_count = 0
        day_values = []
        while day_count < number_of_days:
            day_count += division
            past_epoch = chartstart_epoch + (self.timedelta_day * day_count)
            number_of_documents = 0
            for card_document in self.cards_collection.find(owner_reviewer_search_criteria):
                state_metrics = self.get_state_metrics(condensed_column_states,
                                                       card_document['statehistory'])
                for closed_state in closed_states:
                    closed = state_metrics.get(closed_state, 0)
                    if closed:
                        break
                    
                if closed and closed <= past_epoch:
                    number_of_documents += 1

            day_values.append({'value':number_of_documents,'tooltip':str(number_of_documents)})

        line_chart.add(project, day_values)
        stackedline_chart.add(project, day_values)
        line_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp()))+'.svg'))
        stackedline_chart.render_to_file(os.path.join(self.current_dir, '..', 'svgcharts', username+'_'+str(int(epoch.timestamp())+1)+'.svg'))
        content.append('<table class="sidebyside"><tr><td>')
        content.append(f'<figure><embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())}.svg" /></figure>')
        content.append('</td><td>')
        content.append(f'<figure><embed type="image/svg+xml" src="/svgcharts/{username}_{int(epoch.timestamp())+1}.svg" /></figure>')
        content.append('</td></tr></table>')
        if rawdatarequired:
            content.append('<p>Raw Data Output to be completed!</p>')

        if csvrequired:
            content.append('<p>CSV Output to be completed!</p>')

        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

    @cherrypy.expose
    def which_metric(self):
        """ Allows a user to select the most appropriate metric """
        username = Kanbanara.check_authentication(f'/{self.component}/which_metric')
        session_id = Kanbanara.cookie_handling(self)
        session_document = self.sessions_collection.find_one({"session_id": session_id})
        member_document = Kanbanara.get_member_document(self, session_document)
        content = []
        content.append(Kanbanara.header(self, 'which_metric', 'Which Metric'))
        content.append(Kanbanara.filter_bar(self, 'which_metric'))
        content.append(Kanbanara.menubar(self))
        content.append(self.insert_page_title_and_online_help(session_document, 'which_metric', 'Which Metric'))
        content.append('<div align="center">')
        content.append('<table><tr><th>Informational</th><th>Diagnostic</th><th>Motivational</th></tr></table>')
        content.append('</div>')
        content.append(Kanbanara.footer(self))
        return "".join(content)

currentDir = os.path.dirname(os.path.abspath(__file__))
conf = {'/': {'tools.staticdir.root':   currentDir,
              'tools.sessions.on':      True,
              'tools.sessions.locking': 'explicit'
              }}
for directory in ['css', 'images']:
    if os.path.exists(currentDir+os.sep+directory):
        conf['/'+directory] = {'tools.staticdir.on':  True,
                               'tools.staticdir.dir': directory}

cherrypy.tree.mount(Metrics(), '/metrics', config=conf)
