Beispiel #1
0
 def __init__(self, username, password, base_url, 
              start_date=None, end_date=None, prefetch=False):
     self.fbapi = FogBugzAPI(base_url, username, password)
     self.bugs = DefaultDictForKey(self.get_buginfo)
     self.hours_perdev = DefaultDictForKey(self.get_hours_for_dev)
     self.start_date, self.end_date = start_date, end_date
     self.all_tags = set()
     self.bad_num_tags = set()
     self.base_url = base_url
     if prefetch:
         self.get_buginfo('all')
     self.devs = {}
     self.get_devinfo(9)
Beispiel #2
0
class TimeReporting(object):
    
    def __init__(self, username, password, base_url, 
                 start_date=None, end_date=None, prefetch=False):
        self.fbapi = FogBugzAPI(base_url, username, password)
        self.bugs = DefaultDictForKey(self.get_buginfo)
        self.hours_perdev = DefaultDictForKey(self.get_hours_for_dev)
        self.start_date, self.end_date = start_date, end_date
        self.all_tags = set()
        self.bad_num_tags = set()
        self.base_url = base_url
        if prefetch:
            self.get_buginfo('all')
        self.devs = {}
        self.get_devinfo(9)

    def logout(self):
        self.fbapi.logout()
    
    def url_for_bug(self, bug_id):
        url_elements = list(urllib2.urlparse.urlsplit(self.base_url))
        url_elements[2] = '/?%d' % bug_id
        url_elements[3] = ''
        url_elements[4] = ''
        return urllib2.urlparse.urlunsplit(url_elements)

    def fb_filter_for_bugs(self, bug_id_list):
        return ('OrderBy:Project ' + ' OR '.join('ixbug:%s'%b 
                                                 for b in bug_id_list))

    def get_devinfo(self, dev_id):
        """Actually just gets info for all devs at once"""
        resp = self.fbapi.call('listPeople', fIncludeNormal=1, 
                               fIncludeVirtual=1)
        for p in resp.find('people').iterfind('person'):
            dev_id = int(p.find('ixPerson').text)
            self.devs[dev_id] = {
                'name': p.find('sFullName').text,
                'email': p.find('sEmail').text
            }
        self.devs[0] = {
            'name':     'nobody',
            'email':    '*****@*****.**'
        }
        return self.devs[dev_id]

    def get_buginfo(self, bug_list):
        """Fill in info for one or more bugs, or 'all' bugs"""
        if bug_list == 'all':
            query = '""'
        elif isinstance(bug_list, (int, long)):
            query = 'ixBug:%d' % bug_list
        else:
            query = ' or '.join('ixBug:%d' % bug_id for bug_id in bug_list)
        resp = self.fbapi.call('search', q=query, 
                               cols='tags,sTitle,ixBug,sProject,dtResolved')
        for c in resp.find('cases').findall('case'):
            bug_id = int(c.find('ixBug').text)
            project = c.find('sProject').text
            self.bugs[bug_id] = {
                'title': c.find('sTitle').text,
                'tags': ['%s-%s'%(project, t.text) 
                         for t in c.find('tags').findall('tag')],
                'project': project,
                'resolved': c.find('dtResolved').text
            }
        return self.bugs[bug_id]

    def get_hours_for_dev(self, dev_name):
        self.get_all_hours_per_tag_per_dev(self.start_date, self.end_date)
        return self.hours_perdev[dev_name]

    def get_all_hours_per_tag_per_dev(self, start=None, end=None):
        if start is None: start = self.start_date
        if end is None: end = self.end_date
        self.hours_perdev = defaultdict(lambda: defaultdict(int))

        # Find all timesheet hours
        intervals = self._get_intervals_in_daterange(start, end)
        for i in intervals:
            try:
                self._parse_interval(i)
            except Exception, e:
                l.error('Problem with interval: ' + etree.tostring(i))
                raise e

        # now add non-timesheet elapsed time for bugs resolved in that
        # period, using resolvedby as dev
        resp = self.fbapi.call(
            'search', q='resolved:"%s..%s"'%(start.strftime('%m/%d/%Y'),
                                             end.strftime('%m/%d/%Y')),
            cols=('ixBug,ixPerson,hrsElapsedExtra,tags,sProject,'
                  'ixPersonResolvedBy'),
        )
        for b in resp.find('cases').iterfind('case'):
            hours = float(b.find('hrsElapsedExtra').text)
            if hours == 0:
                continue
            bug_id = int(b.find('ixBug').text)
            dev_id = int(b.find('ixPersonResolvedBy').text)
            dev_name = self.devs[dev_id]['name']
            tags = self.bugs[bug_id]['tags']
            project = self.bugs[bug_id]['project']
            for t in tags:
                self.hours_perdev[dev_name][t] += hours
            if not tags:
                self.hours_perdev[dev_name]['None'] += hours
            self.hours_perdev[dev_name]['total'] += hours
            self.hours_perdev[dev_name]['non-timesheet'] += hours
            if len(tags) != 1:
                l.warning("Bug with %d tag: %d" % (len(tags), bug_id))
                self.bad_num_tags.add(bug_id)
            if tags:
                self.all_tags.update(tags)

        if self.bad_num_tags:
            l.warning(u"Some bugs covered by this timesheet have no "
                      u"associated tags, or more than 1 tag: " 
                      + ', '.join(`b` for b in self.bad_num_tags))