コード例 #1
1
ファイル: log_time.py プロジェクト: rhinosaurus/jira_goodies
def getEm():
	options = { 'server': 'https://jira.server.com' }

	jira = JIRA( options=options, basic_auth=('user','pass') )

	tickets = jira.search_issues( 'Participants = currentUser() AND updated > startOfDay() ORDER BY updated DESC' )

	for ticket in tickets:
		comments = jira.comments( ticket.key )
		for comment in comments:
			if comment.author.name == 'username':
				t = comment.created
				t = unicodedata.normalize( 'NFKD', t ).encode( 'UTF-8', 'ignore' )
				t = t.split("T")
				t = t[0]
				now = time.strftime("%Y-%m-%d")
				if t == now:
					minute_len = len( comment.body.split() )
					if minute_len <= 40:
						minutes = "3m"
					else:
						minutes = str( int( round( math.ceil( minute_len/40 )*3 ) ) ) + "m"
					jira.add_worklog( issue=str(ticket.key), timeSpent=minutes )
コード例 #2
0
ファイル: worklog.py プロジェクト: willdeberry/worklog
def log_to_jira( worklog ):
    config_path = os.path.expanduser( '~/.worklog/config.json' )

    try:
        with open( config_path ) as json_data:
            auth_file = json.load( json_data )

            try:
                options = { 'server': '{}'.format( auth_file['server'] ) }
            except KeyError:
                server = imput( '\nJira Server: ' )
                options = { 'server': server }

            try:
                username = auth_file['username']
            except KeyError:
                username = input( '\nJira Username: '******'password']
            except KeyError:
                password = getpass()

    except OSError as e:
        if e.errno ==  errno.ENOENT:
            username = input( '\nJira Username: '******'{}-{}-{}T{}:{}:00.000-0400'.format(
                    task.start.year,
                    task.start.month,
                    task.start.day,
                    task.start.hour,
                    task.start.minute
                )
                ticket = jira.issue( task.ticket )
                sys.stdout.write( '\nLogging {} to ticket {}\n'.format( time, ticket ) )
                jira.add_worklog(
                    issue = ticket,
                    timeSpent = str( time ),
                    started = datetime.strptime( started, '%Y-%m-%dT%H:%M:%S.000%z' )
                )
コード例 #3
0
ファイル: wlbak.py プロジェクト: haxwithaxe/worklog
def log_to_jira(worklog, config):

    with open(config.jira.oauth_pem_filename) as pem:
        pem_data = pem.read()

    oauth_dict = {
        'access_token': config.jira.oauth_token,
        'access_token_secret': config.jira.oauth_token_secret,
        'consumer_key': config.jira.oauth_consumer_key,
        'key_cert': pem_data
    }

    options = {'server': config.jira.server or input('\nJira Server: ')}
    username = config.jira.username or input('\nJira Username: '******'Logging work ...')
    if len(worklog) > 0:
        for task, next_task in worklog.pairwise():
            if isinstance(task, GoHome):
                continue
            if task.ticket and not task.logged:
                duration = Duration(delta=next_task.start - task.start)
                if not duration.seconds:
                    continue
                started = '{}-{}-{}T{}:{}:00.000-0400'.format(
                    task.start.year, task.start.month, task.start.day,
                    task.start.hour, task.start.minute)
                print('ticket', task.ticket)
                ticket = jira.issue(task.ticket)
                print('\nLogging {} to ticket {}'.format(duration, ticket))
                jira.add_worklog(issue=ticket,
                                 timeSpent=str(duration),
                                 started=datetime.strptime(
                                     started, '%Y-%m-%dT%H:%M:%S.000%z'))
                task.logged = True
    print('Done.')
コード例 #4
0
ファイル: cv_time.py プロジェクト: cliff85/jira_scripts
def jira_enter(user, password, start, end):
    shared, internal, project, project_jiras, total = query_db(user, start, end)
    time_entry = check_time(shared, internal, project)
    jira_password = password
    jira = JIRA(basic_auth=(user, jira_password), options={"server": "https://jira.crossview.inc", "verify": False})
    end_s = end.split("-")
    # print end_s
    if_cal = calendar.monthrange(int(end_s[0]), int(end_s[1]))
    if_cal = int(if_cal[1])
    if int(end_s[2]) == if_cal or int(end_s[2]) == (if_cal - 1):
        # If it is the either last day or one more day until last of the month, input previous Sunday. Just taking one day off start date.
        new_date = end
        new_date = datetime.strptime(new_date, "%Y-%m-%d").toordinal()
        sunday = new_date - (new_date % 7)
        new_date = datetime.fromordinal(sunday)
        print new_date
        new_date = new_date.isoformat()
        new_date = new_date + ".937-0500"
        print "Entering on date %s" % new_date
    else:
        # If it is NOT he last day of the month, input on Saturday. Taking 1 day from end date
        day = int(end_s[2]) - 1
        new_date = "%s-%s-%s" % (end_s[0], end_s[1], day)
        new_date = datetime.strptime(new_date, "%Y-%m-%d").isoformat()
        new_date = new_date + ".937-0500"
        print "Entering on date %s" % new_date
    ignoretimes = ["TIME-3", "TIME-7", "TIME-9", "TIME-45", "TIME-590", "TIME-720", "TIME-820", "TIME-821"]
    for key, value in time_entry.iteritems():
        if key == "TIME-654":
            list_jiras = []
            for i in project_jiras[0]:
                list_jiras.append(i)
            project_jiras = ", ".join(list_jiras)
            print "adding worklog for %s timespent %sh and comment %s, on %s" % (key, value, project_jiras, new_date)
            jira.add_worklog(issue=key, timeSpent="%sh" % value, started=new_date, comment=project_jiras)
        elif key in ignoretimes:
            print "ignoring %s entry" % key
        else:
            print "adding worklog for %s timespent %sh on %s" % (key, value, new_date)
            jira.add_worklog(issue=key, timeSpent="%sh" % value, started=new_date)
コード例 #5
0
ファイル: loghours.py プロジェクト: mgaitan/hamster2jira
    def handle_noargs(self, **options):
        jira = JIRA(basic_auth=(settings.JIRA_USERNAME, settings.JIRA_PASSWORD),
                    options={'server': settings.JIRA_BASE_URL})

        print "Logged in..."

        categories = [p.key for p in jira.projects()]
        tag_logged, _ = Tag.objects.get_or_create(name='_logged_')

        facts = Fact.objects \
                    .exclude(tags=tag_logged) \
                    .exclude(end_time=None) \
                    .filter(activity__category__name__in=categories)

        for f in facts:
            reopened = False
            try:
                issue_key = '%s-%s' % (f.category, f.activity.name)
                issue = jira.issue(issue_key)

                if  issue.fields.status.name in ('Closed', 'Resolve'):
                    reopened = True
                    # reopen to allow worklogs
                    jira.transition_issue(issue, TO_REOPEN)
                    jira.add_comment(issue, 'hamster2jira: Automatically reopened for a'
                                            'while to allow add a worklog')
            except JIRAError:
                continue

            spent = days_hours_minutes(f.duration)
            #and post the fact into dotproject!
            worklog = jira.add_worklog(issue, spent)
            worklog.update(comment=f.description)

            #create a tag to associate this worklog whit the fact
            wl_tag = Tag.objects.create(name='wl%s' % worklog.id)
            FactTag.objects.create(tag=wl_tag, fact=f)
            print "Succesfully log %s into %s" % (spent, issue_key)

            #then mark the fact as logged.
            FactTag.objects.create(fact=f, tag=tag_logged)
            if reopened:
                jira.transition_issue(issue, TO_CLOSE)
コード例 #6
0
class JiraClient(object):
    def __init__(self, server, username, password, default_project=None):
        self.username = username
        self.password = password
        self.default_project = default_project
        self.server = server

        self.jira = JIRA(options={'server': self.server},
                         basic_auth=(self.username, self.password))

    def my(self, **kwargs):
        self._list_my_issues(**kwargs)

    def issue(self, **kwargs):
        issue_id = kwargs['issue_id']
        issue = self.jira.issue(issue_id)

        print 'ID: %s' % issue.key
        print 'URL: %s/browse/%s' % (self.server, issue.key)
        print 'Assignee: %s' % issue.fields.assignee.name
        print 'Status: %s' % issue.fields.status.name
        print ''
        print 'Summary: %s' % issue.fields.summary
        print ''
        print 'Details: %s' % issue.fields.description

    def log(self, **kwargs):
        issue_id = kwargs['issue_id']
        issue = self.jira.issue(issue_id)

        time_spent = kwargs['time']
        msg = kwargs['message']

        #FIXME: add comment as a part of worklog (investigate API)
        self.jira.add_comment(issue, msg)
        self.jira.add_worklog(issue, time_spent)
        print 'Your worklog was saved'

    def resolve(self, **kwargs):
        issue_id = kwargs['issue_id']
        issue = self.jira.issue(issue_id)

        self.jira.transition_issue(issue,
                                   '5',
                                   resolution={'name': 'Implemented'})
        print 'Issue %s was resolved as implemented' % issue_id

    def assign(self, **kwargs):
        issue_id = kwargs['issue_id']
        issue = self.jira.issue(issue_id)

        self.jira.assign_issue(issue, self.username)
        print 'Issue %s was assigned to %s' % (issue_id, self.username)
        if kwargs['start']:
            self.jira.transition_issue(issue, '4')
            print 'Issue %s was started' % issue_id

    def todo(self, **kwargs):
        query_parts = ['status = Open', 'type != Story', 'created >= "-14d"']

        if kwargs['project'] is not None:
            query_parts.append('project=%s' % kwargs['project'])
        elif self.default_project is not None:
            query_parts.append('project=%s' % self.default_project)

        query = ' and '.join(query_parts)
        self._perform_and_print_query(query)

    def _list_my_issues(self, **kwargs):
        query_parts = ['assignee=%s' % (self.username)]

        if kwargs['project'] is not None:
            query_parts.append('project=%s' % kwargs['project'])
        elif self.default_project is not None:
            query_parts.append('project=%s' % self.default_project)

        if kwargs['unresolved']:
            query_parts.append('resolution=unresolved')

        query = ' and '.join(query_parts)
        self._perform_and_print_query(query)

    def _perform_and_print_query(self, query):
        my_issues = self.jira.search_issues(query)

        for issue in my_issues:
            print '%s\t%s' % (issue.key, issue.fields.summary)
コード例 #7
0
class JiraRestBridge(JiraBridge):

    def __init__(self, base_url, config, persist=False):
        super(JiraRestBridge, self).__init__(base_url, config, persist)
        self.jira = None

    @cached('resolutions')
    def get_resolutions(self):
        return dict((r.name.lower(), rest_recursive_dict(r.raw)) for r in self.jira.resolutions())

    @cached('filters')
    def get_filters(self):
        filters = dict((f.name, rest_recursive_dict(f.raw)) for f in self.jira.favourite_filters())
        return filters

    def clean_issue(self, issue):
        _issue = {}
        for k,v in list(issue.fields.__dict__.items()):
            if isinstance(v, Resource):
                _issue[k] = map_rest_resource(v)
            elif v is not None:
                _issue[k] = v
        _issue['key'] = issue.key
        _issue['type'] = map_rest_resource(_issue['issuetype'])
        return _issue

    def get_issue(self, issue_id, raw=False):
        try:
            if not raw:
                return self.clean_issue(self.jira.issue(issue_id))
            else:
                return self.jira.issue(issue_id)
        except:
            return None

    def search_issues(self, free_text, project=None, limit=100):
        query = '(summary~"%s" or description~"%s")' % (free_text, free_text)
        if project:
            query += ' and project=%s' % project
        query += ' order by key'
        return [self.clean_issue(issue) for issue in self.jira.search_issues(query,maxResults=100)]

    def search_issues_jql(self, query, limit=100, project=None):
        return [self.clean_issue(issue) for issue in self.jira.search_issues(query, maxResults=100)]

    def get_issues_by_filter(self, *filters):
        return self.search_issues_jql(
            "filter in (%s)" % ",".join(['"%s"' % f for f in filters])
        )

    def add_comment(self, issue, comment):
        self.jira.add_comment(issue, comment)

    def transition_issue(self, issue, transition, resolution):
        transitions = self.get_available_transitions(issue)
        fields = {}
        if resolution:
            fields["resolution"] = self.get_resolutions()[resolution.lower()]
        try:
            return self.jira.transition_issue(issue, transitions[transition]['id'], fields=fields)
        except KeyError:
            raise JiraCliError("Invalid transition '%s'. Use one of [%s]" % (transition, ",".join(transitions)))

    def ping(self):
        return False

    def create_issue(self, project, type='bug', summary="", description="",
                     priority="minor", parent=None, assignee="", reporter="",
                     labels=[], components={}, **extras):
        issue = {
            "project": {'key':project.upper()},
            "summary": summary,
            "description": description,
            "priority": {'id':self.get_priorities()[priority.lower()]["id"]},
            "components": [{"name": k} for k in list(components.keys())]
        }
        if labels:
            issue['labels'] = labels
        if not issue["components"]:
            issue.pop("components")
        if type.lower() == 'epic':
            issue['customfield_11401'] = summary
        if parent:
            issue['issuetype'] = {'id':self.get_subtask_issue_types()[type.lower()]['id']}
            issue['parent'] = {'key':parent}
        else:
            issue['issuetype'] = {'id':self.get_issue_types()[type.lower()]['id']}
        if extras:
            issue.update(extras)
        issue = self.jira.create_issue(issue)
        if not (assignee or reporter):
            return self.clean_issue(issue)
        else:
            key = issue.key
            if assignee:
                issue = self.clean_issue(self.assign_issue(key, assignee))
            if reporter:
                issue = self.clean_issue(self.change_reporter(key, reporter))
            return issue

    def login(self, username, password):
        try:
            self.jira = JIRA(options={'server': self.base_url, 'check_update': False},
                         basic_auth=(username, password), get_server_info=False, validate=False
            )
        except JIRAError:
            raise JiraAuthenticationError('failure to authenticate')
        except RequestException:
            raise JiraInitializationError('failure to communicate with jira')

    def get_available_transitions(self, issue):
        return dict((t['name'].lower(), t) for t in self.jira.transitions(issue))

    @cached('issue_types')
    def get_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types() if not k.subtask)
        for k in types:
            if k=='id':
                types['id'] = types['id'][0]
        return types

    @cached('subtask_types')
    def get_subtask_issue_types(self):
        types = dict((k.name.lower(), k.raw) for k in self.jira.issue_types() if k.subtask)
        for k in types:
            if k=='id':
                types['id'] = types['id'][0]
        return types

    def update_issue(self, issue_id, update={}, **kwargs):
        issue = self.jira.issue(issue_id)
        issue.update(update=update, **kwargs)
        return self.jira.issue(issue_id)

    def assign_issue(self, issue_id, assignee):
        return self.update_issue(
            issue_id, assignee={"name": assignee}
        )

    def change_reporter(self, issue_id, reporter):
        return self.update_issue(
            issue_id, reporter={"name": reporter}
        )

    def add_labels(self, issue_id, labels, merge=False):
        issue = self.jira.issue(issue_id)
        issue.fields.labels.extend(labels)
        return issue.update(fields={"labels": issue.fields.labels})

    @cached('projects')
    def get_projects(self):
        return dict((k.name.lower(), k.raw) for k in self.jira.projects())

    @cached('priorities')
    def get_priorities(self):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.priorities())

    @cached('components')
    def get_components(self, project):
        return [k.raw for k in self.jira.project_components(project)]

    def list_versions(self, project):
        versions = self.jira.project(project).versions
        versions.sort(cmp=lambda l, r: cmp(l.id, l.id))
        return [k.raw for k in versions]

    @cached('statuses')
    def get_statuses(self):
        return dict((k.name.lower(), dict(k.raw)) for k in self.jira.statuses())

    def get_issue_comments(self, issue):
        return [
            dict(author=comment.author.name
                 , body=comment.body
                 , created=comment.created
            )
            for comment in self.jira.comments(issue)
        ]

    def add_versions(self, issue, versions, type):
        args = {}

        if type == 'fix':
            args = {'fixVersions': [{"add": {"name": v}} for v in versions]}
        elif type == 'affects':
            args = {'versions': [{"add": {"name": v}} for v in versions]}

        return self.update_issue(issue, **args)

    def remove_versions(self, issue, versions, type):
        args = {}

        if type == 'fix':
            args = {'fixVersions': [{"remove": {"name": v}} for v in versions]}
        elif type == 'affects':
            args = {'versions': [{"remove": {"name": v}} for v in versions]}

        return self.update_issue(issue, **args)

    def log_work(self, issue, spent, remaining=None, comment=""):
        if remaining is None:
            adjust = "auto"
        else:
            adjust = "new"
        self.jira.add_worklog(issue=issue, timeSpent=spent, comment=comment, newEstimate=remaining, adjustEstimate=adjust)
コード例 #8
0
class JiraIssueTrackingPlugin(BaseNotificationPlugin):
    def __init__(self, server, username, password, ticket_regexps):
        super(JiraIssueTrackingPlugin, self).__init__()
        self.jira = JIRA(server, basic_auth=(username, password))
        self.ticket_regexps = ticket_regexps

    def execute_if_it_has_to(self, company):
        # jira doesn't need to check if it _has_to_
        pending_updates = Task.select().join(Company).switch(Task).join(
            JiraTaskUpdated,
            JOIN_LEFT_OUTER).where((Company.id == company.id) & (
                (~(fn.Exists(JiraTaskUpdated.select().where(
                    JiraTaskUpdated.task == Task.id))))
                | (JiraTaskUpdated.updated_at < Task.updated_at)))

        company_tz = pytz.timezone(company.timezone)

        for task in list(pending_updates):
            # get the ticket id
            current_best_position = len(task.description)
            current_match = None
            for regexp in self.ticket_regexps:
                match = re.search('\\b(' + regexp + ')\\b', task.description)
                if match is not None and match.start(
                        1) < current_best_position:
                    current_best_position = match.start(1)
                    current_match = match.group(1)

            if current_match is not None:
                issue = None
                try:
                    issue = self.jira.issue(current_match)
                except:
                    pass

                if issue is not None:
                    # found a ticket!
                    description = task.description
                    if current_best_position == 0:
                        description = re.sub(
                            '^[^a-zA-Z0-9\\(]*', '',
                            task.description[len(current_match):])

                    worklog_ready = False
                    for worklog in self.jira.worklogs(issue.id):
                        started = parse(
                            worklog.started).astimezone(company_tz).date()
                        if task.date == started and worklog.comment == description:
                            if worklog.timeSpentSeconds != task.time_spent_seconds:
                                worklog.update(
                                    timeSpentSeconds=task.time_spent_seconds)
                            worklog_ready = True

                    if not worklog_ready:
                        # get the timezone suffix on the task's date (considering DST)
                        task_date_with_time = datetime.datetime.combine(
                            task.date, datetime.datetime.min.time())
                        suffix = company_tz.localize(
                            task_date_with_time).strftime('%z')

                        # make it 6pm wherever they are
                        dt = parse(
                            task.date.strftime('%Y-%m-%dT18:00:00') + suffix)
                        self.jira.add_worklog(
                            issue.id,
                            timeSpentSeconds=task.time_spent_seconds,
                            started=dt,
                            comment=description)

                    if task.jira_tasks_updated.count() == 0:
                        task_updated = JiraTaskUpdated()
                    else:
                        task_updated = task.jira_tasks_updated[0]

                    task_updated.task = task
                    task_updated.updated_at = datetime.datetime.utcnow()
                    task_updated.save()
コード例 #9
0
        print("\n")
        print("Comments ")
        print("=================================================")
        print(issue.fields.comment.comments)
        userResponse = str(input("Enter (Y/y) to continue: "))
        if userResponse.lower() != "y":
            executionFlag = False
    elif command == "5":
        timeSpent = str(
            input(
                "Please enter the time you want to log (eg. 3w 4d 12h 30m): "))
        jira.add_worklog(issue,
                         timeSpent=timeSpent,
                         timeSpentSeconds=None,
                         adjustEstimate=None,
                         newEstimate=None,
                         reduceBy=None,
                         comment=None,
                         started=None,
                         user=None)
        userResponse = str(input("Enter (Y/y) to continue: "))
        if userResponse.lower() != "y":
            executionFlag = False
    elif command == "6":
        jira.assign_issue(issue.id, username)

        userResponse = str(input("Enter (Y/y) to continue: "))
        if userResponse.lower() != "y":
            executionFlag = False
    elif command == "7":
        # print(issue.fields.project.key)
コード例 #10
0
ファイル: exportrt.py プロジェクト: freedayko/hamster
class ExportRtController(gtk.Object):
    __gsignals__ = {
        "on-close": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
    }

    def __init__(self, parent = None, facts = None):
        gtk.Object.__init__(self)
        
        self.source = conf.get("activities_source")
        self.rt = None
        self.redmine = None
        self.jira = None

        if self.source == SOURCE_RT:
#            Init RT
            self.rt_url = conf.get("rt_url")
            self.rt_user = conf.get("rt_user")
            self.rt_pass = conf.get("rt_pass")

            self.rt = rt.Rt(self.rt_url, self.rt_user, self.rt_pass)
            if not self.rt.login():
                self.rt = None
        elif self.source == SOURCE_REDMINE:
            self.rt_url = conf.get("rt_url")
            self.rt_user = conf.get("rt_user")
            self.rt_pass = conf.get("rt_pass")

            if self.rt_url and self.rt_user and self.rt_pass:
                try:
                    self.redmine = redmine.Redmine(self.rt_url, auth=(self.rt_user,self.rt_pass))
                    if not self.redmine:
                        self.source = SOURCE_NONE
                except:
                    self.source = SOURCE_NONE
            else:
                self.source = SOURCE_NONE

        elif jira_active and self.source == SOURCE_JIRA:
            self.jira_url = conf.get("jira_url")
            self.jira_user = conf.get("jira_user")
            self.jira_pass = conf.get("jira_pass")
            self.jira_query = conf.get("jira_query")
            self.jira_category = conf.get("jira_category_field")
            self.jira_fields=','.join(['summary', self.jira_category])
            if self.jira_url and self.jira_user and self.jira_pass:
                try:
                    options = {'server': self.jira_url}
                    self.jira = JIRA(options, basic_auth = (self.jira_user, self.jira_pass), validate = True)
                except Exception as e:
                    logging.warn('jira connection failed: '+str(e))
                    self.source = SOURCE_NONE
            else:
                self.source = SOURCE_NONE
                
        self._gui = load_ui_file("export_rt.ui")
        self.window = self.get_widget('report_rt_window')

        self.parent, self.facts = parent, facts

        self.done_button = self.get_widget("done_button")
#        self.done_button.set_sensitive(False)
        
#        Model
        self.tree_store = gtk.TreeStore(gobject.TYPE_PYOBJECT)
        self.rows = list([ExportRow(fact) for fact in facts])
        self.rows.sort(key = lambda row: row.id)
        grouped_rows = {}
        for issue_id, rows in groupby(self.rows, lambda export_row: export_row.id):
            grouped_rows[issue_id] = list(rows)
        for issue_id in grouped_rows.keys():
            #ściągnąć nazwę ticketa
            if self.source == SOURCE_RT:
                row_data = self.rt.get_ticket(issue_id)
#                 row_data['source'] = SOURCE_RT
            elif self.source == SOURCE_REDMINE:
                issue = self.redmine.getIssue(issue_id)
                row_data = {}
                row_data['id'] = issue.id
                row_data['Subject'] = str(issue_id)+': '+issue.fields.summary
#                 row_data['source'] = SOURCE_REDMINE
            elif self.source == SOURCE_JIRA:
                issue = self.jira.issue(issue_id)
                row_data = {}
                row_data['id'] = issue.id
                row_data['Subject'] = issue.fields.summary
#                 row_data['source'] = SOURCE_JIRA

            if row_data:
                parent = self.tree_store.append( None, (TicketRow(row_data), ) )
                for row in grouped_rows[issue_id]:
                    self.tree_store.append(parent, (row, ))
            
#        self.tree_store.append(parent, (row.comment))
        self.view = gtk.TreeView(self.tree_store);
        self.view.set_headers_visible(False)
        
        
        id_cell = gtk.CellRendererText()
        id_column = gtk.TreeViewColumn("", id_cell, text=0)
        id_column.set_cell_data_func(id_cell, id_painter)
        id_column.set_max_width(100)
        self.view.append_column(id_column)
        
        name_comment_cell = gtk.CellRendererText()
        name_comment_cell.connect("edited", self.on_comment_edited)
        name_comment_column = gtk.TreeViewColumn("", name_comment_cell, text=0)
        name_comment_column.set_cell_data_func(name_comment_cell, name_comment_painter)
        name_comment_column.set_expand(True)
        self.view.append_column(name_comment_column)
        
        time_cell = gtk.CellRendererSpin()
        time_cell.connect("edited", self.on_time_worked_edited)
        time_column = gtk.TreeViewColumn("", time_cell, text=0)
        time_column.set_cell_data_func(time_cell, time_painter)
        time_column.set_min_width(60)
        self.view.append_column(time_column)
        self.view.expand_all()
        
        self.start_button = self.get_widget("start_button")
        self.get_widget("activities").add(self.view)
        self.aggregate_comments_checkbox = self.get_widget("aggregate_comments_checkbox")
        self.aggregate_comments_checkbox.set_active(True)
        self.test_checkox = self.get_widget("test_checkbox")
        self.test_checkox.set_active(False)
        self.progressbar = self.get_widget("progressbar")
        self.progressbar.set_text(_("Waiting for action"))
        self.progressbar.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
        
        self._gui.connect_signals(self)

        self.window.show_all()
    
    
    def on_time_worked_edited(self, widget, path, value):
        row = self.tree_store[path][0]
        row.time_worked = int(value)
        
    def on_comment_edited(self, widget, path, value):
        row = self.tree_store[path][0]
        row.comment = value

    def get_widget(self, name):
        """ skip one variable (huh) """
        return self._gui.get_object(name)

    def show(self):
        self.window.show()
        
    def on_start_activate(self, button):
        if self.rt or self.redmine or self.jira:
            group_comments = self.aggregate_comments_checkbox.get_active()
            it = self.tree_store.get_iter_first()
            to_report_list = []
            while it:
                ticket_row = self.tree_store.get_value(it, 0)
                child_iter = self.tree_store.iter_children(it)
                #get childs
                export_rows = []
                while child_iter:
                    export_rows.append(self.tree_store.get_value(child_iter, 0))
                    child_iter = self.tree_store.iter_next(child_iter)
                #report tickets
                if group_comments:
                    comment = "\n".join("%s - %s min"% (row.comment, row.time_worked) for row in export_rows)
                    time_worked = sum([row.time_worked for row in export_rows])
                    facts = [row.fact for row in export_rows]
                    to_report_list.append({'id':ticket_row.id, 'name':ticket_row.name, 'comment':comment, 'time':time_worked, 'facts':facts, 'date': row.date})
                else:
                    for row in export_rows:
                        to_report_list.append({'id':ticket_row.id, 'name':ticket_row.name, 'comment':"%s - %s min"% (row.comment, row.time_worked), 'time':row.time_worked, 'facts':[row.fact], 'date': row.date})
#                        self.__add_rt_worklog(ticket_row.id, "%s - %s min"% (row.comment, row.time_worked), row.time_worked, [row.fact])
                it = self.tree_store.iter_next(it)
            to_report_len = len(to_report_list)
            self.progressbar.set_fraction(0.0)
            for i in range(to_report_len):
                to_report = to_report_list[i]
                self.progressbar.set_text(_("Reporting: #%s: %s - %smin") % (to_report['id'], to_report['name'], to_report['time']))
                self.progressbar.set_fraction(float(i)/to_report_len)
                while gtk.events_pending(): 
                    gtk.main_iteration()
                if self.source == SOURCE_RT:
                    self.__add_rt_worklog(to_report['id'], to_report['comment'], to_report['time'], to_report['facts'])
                elif self.source == SOURCE_REDMINE:
                    self.__add_redmine_worklog(to_report['id'], to_report['date'], math.ceil(to_report['time']*100/60)/100, to_report['comment'], to_report['facts'])
                elif self.source == SOURCE_JIRA:
                    self.__add_jira_worklog(to_report['id'], to_report['comment'], to_report['time'], to_report['facts'])
            self.progressbar.set_text("Done")
            self.progressbar.set_fraction(1.0)
#            for fact in self.facts:
#                match = re.match(TICKET_NAME_REGEX, fact.activity)
#                if fact.end_time and match:
#                    ticket_id = match.group(1)
#                    text = self.get_text(fact)
#                    time_worked = stuff.duration_minutes(fact.delta)
#                    logging.warn(ticket_id)
#                    logging.warn(text)
#                    logging.warn("minutes: %s" % time_worked)
##                    external.rt.comment(ticket_id, text, time_worked)
#                else:
#                    logging.warn("Not a RT ticket or in progress: %s" % fact.activity)
        else:
            logging.warn(_("Not connected to/logged in RT"))
        self.start_button.set_sensitive(False)
        #TODO only if parent is overview
        self.parent.search()
            
    def __add_rt_worklog(self, ticket_id, text, time_worked, facts):
        test = self.test_checkox.get_active()
#        logging.warn(_("updating ticket #%s: %s min, comment: \n%s") % (ticket_id, time_worked, text))
        if not test:
            time = time_worked
        else:
            time = 0

        if self.rt.comment(ticket_id, text, time) and not test:
            for fact in facts:
                runtime.storage.update_fact(fact.id, fact, False,True)
#                fact_row.selected = False
            
    def __add_jira_worklog(self, issue_id, text, time_worked, facts):
        test = self.test_checkox.get_active()
#        logging.warn(_("updating ticket #%s: %s min, comment: \n%s") % (ticket_id, time_worked, text))
        if not test:
            time = time_worked
        else:
            time = 0

        if self.jira.add_worklog(issue = issue_id, comment = text, timeSpent = time) and not test:
            for fact in facts:
                runtime.storage.update_fact(fact.id, fact, False,True)
            
    def __add_redmine_worklog(self, issue_id, spent_on, hours, comments, facts):
        test = self.test_checkox.get_active()
        logging.warn(_("updating issue #%s: %s hrs, comment: \n%s") % (issue_id, hours, comments))
        time_entry_data = {'time_entry': {}}
        time_entry_data['time_entry']['issue_id'] = issue_id
        time_entry_data['time_entry']['spent_on'] = spent_on
        time_entry_data['time_entry']['hours'] = hours
        time_entry_data['time_entry']['comments'] = comments
        time_entry_data['time_entry']['activity_id'] = 9
        
        r = self.redmine.createTimeEntry(time_entry_data)
        logging.warn(r.status_code)
        logging.warn(r.content)
        if r.status_code == 201 and not test:
            for fact in facts:
                runtime.storage.update_fact(fact.id, fact, False,True)
#                fact_row.selected = False

    def get_text(self, fact):
        text = "%s, %s-%s" % (fact.date, fact.start_time.strftime("%H:%M"), fact.end_time.strftime("%H:%M"))
        if fact.description:
            text += ": %s" % (fact.description)
        if fact.tags:
            text += " ("+", ".join(fact.tags)+")"
        return text

    def on_window_key_pressed(self, tree, event_key):
        popups = self.start_date.popup.get_property("visible") or \
                 self.start_time.popup.get_property("visible") or \
                 self.end_time.popup.get_property("visible") or \
                 self.new_name.popup.get_property("visible") or \
                 self.new_tags.popup.get_property("visible")

        if (event_key.keyval == gtk.keysyms.Escape or \
           (event_key.keyval == gtk.keysyms.w and event_key.state & gtk.gdk.CONTROL_MASK)):
            if popups:
                return False

            self.close_window()

        elif event_key.keyval in (gtk.keysyms.Return, gtk.keysyms.KP_Enter):
            if popups:
                return False
            self.on_save_button_clicked(None)

    def on_done_activate(self, button):
        self.on_close(button, None)

    def on_close(self, widget, event):
        if self.source == SOURCE_RT:
            self.rt.logout();
        self.close_window()

    def close_window(self):
        if not self.parent:
            gtk.main_quit()
        else:
            self.window.destroy()
            self.window = None
            self._gui = None
            self.emit("on-close")

    def show_facts(self):
        self.view.detach_model()
        for fact in self.facts:
            self.view.add_fact(fact)
        self.view.attach_model()