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)
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))