Index: ticketstats/ticketstats.py
===================================================================
--- ticketstats/ticketstats.py	(revision 13987)
+++ ticketstats/ticketstats.py	(working copy)
@@ -15,6 +15,7 @@
 from trac.config import IntOption, Option
 from trac.core import *
 from trac.ticket import Milestone
+from trac.ticket import Component as TicketComponent  # Not to interfere with Component class
 from trac.util.datefmt import format_date, parse_date, utc
 from trac.web.api import IRequestHandler
 from trac.web.chrome import Chrome, INavigationContributor, ITemplateProvider
@@ -65,6 +66,7 @@
         req.perm.require('REPORT_VIEW')
         req_content = req.args.get('content')
         milestone = None
+        component = None
 
         if not None in [req.args.get('end_date'), req.args.get('start_date'),
                         req.args.get('resolution')]:
@@ -73,11 +75,18 @@
             grab_from_date = req.args.get('start_date')
             grab_resolution = req.args.get('resolution')
             grab_milestone = req.args.get('milestone')
+            grab_component = req.args.get('component')
+
             if grab_milestone == "__all":
                 milestone = None
             else:
                 milestone = grab_milestone
 
+            if grab_component == "__all":
+                component = None
+            else:
+                component = grab_component
+
             # validate inputs
             if None in [grab_at_date, grab_from_date]:
                 raise TracError('Please specify a valid range.')
@@ -116,12 +125,12 @@
 
         # Calculate 0th point
         last_date = from_date - timedelta(graph_res)
-        last_num_open = get_num_open_tix(db, last_date, milestone)
+        last_num_open = get_num_open_tix(db, last_date, milestone, component)
 
         # Calculate remaining points
         for cur_date in date_range(from_date, at_date, graph_res):
-            num_open = get_num_open_tix(db, cur_date, milestone)
-            num_closed = get_num_closed_tix(db, last_date, cur_date, milestone)
+            num_open = get_num_open_tix(db, cur_date, milestone, component)
+            num_closed = get_num_closed_tix(db, last_date, cur_date, milestone, component)
             date_str = format_date(cur_date)
             if graph_res != 1:
                 date_str = "%s thru %s" % (format_date(last_date), date_str)
@@ -148,19 +157,31 @@
             show_all = req.args.get('show') == 'all'
             milestone_list = [m.name for m in
                               Milestone.select(self.env, show_all)]
+
+	    # Get list of all components
+            component_list = [c.name for c in
+                              TicketComponent.select(self.env, show_all)]
             if milestone in milestone_list:
                 milestone_num = milestone_list.index(milestone) + 1
             else:
                 milestone_num = 0
 
+            # index of selected component
+	    if component in component_list:
+                component_num = component_list.index(component) + 1
+            else:
+                component_num = 0
+
             data = {
                 'ticket_data': count,
                 'start_date': format_date(from_date),
                 'end_date': format_date(at_date),
                 'resolution': str(graph_res),
                 'baseurl': req.base_url,
+                'components': component_list,
                 'milestones': milestone_list,
                 'cmilestone': milestone_num,
+                'ccomponent': component_num,
                 'yui_base_url': self.yui_base_url,
                 'debug': 'debug' in req.args
             }
@@ -180,7 +201,7 @@
         return [resource_filename(__name__, 'templates')]
 
 
-def get_num_closed_tix(db, from_date, at_date, milestone):
+def get_num_closed_tix(db, from_date, at_date, milestone, component):
     """Returns an integer of the number of close ticket events counted
     between from_date to at_date."""
 
@@ -188,18 +209,24 @@
 
     args = [to_timestamp(from_date), to_timestamp(at_date)]
     milestone_str = ''
+    
     if milestone:
         args.append(milestone)
         milestone_str += 'AND t.milestone = %s'
 
+    component_str = ''
+    if component:
+        args.append(component)
+        component_str += 'AND t.component = %s'
+
     # Count tickets between two dates (note: does not account for tickets
     # that were closed and then reopened between the two dates)
     cursor.execute("""
         SELECT newvalue
         FROM ticket_change tc
         INNER JOIN ticket t ON t.id = tc.ticket AND tc.time > %%s
-          AND tc.time <= %%s AND tc.field = 'status' %s
-        ORDER BY tc.time""" % milestone_str, args)
+          AND tc.time <= %%s AND tc.field = 'status' %s %s
+        ORDER BY tc.time""" % (milestone_str, component_str), args)
 
     closed_count = 0
     for (status,) in cursor:
@@ -209,7 +236,7 @@
     return closed_count
 
 
-def get_num_open_tix(db, at_date, milestone):
+def get_num_open_tix(db, at_date, milestone, component):
     """Returns an integer of the number of tickets currently open on that
     date."""
 
@@ -221,11 +248,17 @@
         args.append(milestone)
         milestone_str += 'AND t.milestone = %s'
 
+    # Filter by component if component is selected
+    component_str = ''
+    if component:
+        args.append(component)
+        component_str += 'AND t.component = %s'
+
     # Count number of tickets created before specified date
     cursor.execute("""
         SELECT COUNT(*) FROM ticket t
-        WHERE t.time <= %%s %s
-        """ % milestone_str, args)
+        WHERE t.time <= %%s %s %s
+        """ % (milestone_str, component_str), args)
     open_count = cursor.fetchone()[0]
 
     # Count closed and reopened tickets
@@ -233,8 +266,8 @@
         SELECT newvalue
         FROM ticket_change tc
         INNER JOIN ticket t ON t.id = tc.ticket AND tc.time > 0
-          AND tc.time <= %%s AND tc.field = 'status' %s
-        ORDER BY tc.time""" % milestone_str, args)
+          AND tc.time <= %%s AND tc.field = 'status' %s %s
+        ORDER BY tc.time""" % (milestone_str, component_str), args)
 
     for (status,) in cursor:
         if status == 'closed':
Index: ticketstats/templates/ticketstats.html
===================================================================
--- ticketstats/templates/ticketstats.html	(revision 13987)
+++ ticketstats/templates/ticketstats.html	(working copy)
@@ -40,9 +40,8 @@
   </head>
 
   <body class="yui-skin-sam">
-    <h1 class="chart_title">Ticket Statistics</h1>
-
     <div id="content">
+      <h1 class="chart_title">Ticket Statistics</h1>
       <div id="chart"></div>
 
       <fieldset id="Settings">
@@ -71,6 +70,11 @@
             <option value="__all">All Milestones</option>
             <option py:for="m in milestones" value="$m">$m</option>
           </select>
+          <label for="component">Component: </label>
+          <select name="component" id="component">
+            <option value="__all">All Components</option>
+            <option py:for="c in components" value="$c">$c</option>
+          </select>
 
           <input type="submit" name="Update Chart" value="Update Chart"/>
         </form>
@@ -88,6 +92,7 @@
         return  "&#38;start_date=" + document.getElementById("dt_frm").start_date.value +
             "&#38;end_date=" + document.getElementById("dt_frm").end_date.value +
             "&#38;resolution=" + document.getElementById("dt_frm").resolution.value +
+            "&#38;component=" + document.getElementById("dt_frm").component.value +
             "&#38;milestone=" + document.getElementById("dt_frm").milestone.value;
       }
       function doRequestData() {
@@ -97,13 +102,16 @@
       function updateStaticURL() {
         document.getElementById("static_url").innerHTML = "${baseurl}" + "/ticketstats?content=render" + encodeURI(rqstr());
       }
-      function setProvided(res, mile) {
+      function setProvided(res, mile, comp) {
         if (!res)
           res = ${resolution};
         document.getElementById("resolution").selectedIndex = {1: 0, 7: 1, 14: 2, 30: 3, 60: 4, 90: 5, 180: 6, 360: 7}[res];
         if (!mile)
           mile = ${cmilestone};
         document.getElementById("milestone").selectedIndex = mile;
+        if (!comp)
+          comp = ${ccomponent};
+        document.getElementById("component").selectedIndex = comp;
       }
 
       setProvided();
