def process_commits(secret=None): if context_secret and secret != context_secret: abort(403) yt = Connection(yt_url, yt_login, yt_password) try: cmd_pattern = re.compile( r'#((?:%s)-\d+)(?:\s+(.+))?' % '|'.join(yt.getProjects().keys()), re.IGNORECASE | re.MULTILINE) except YouTrackException: app.logger.warning('Cannot get projects from YT') cmd_pattern = re.compile(r'#([A-z]+-\d+)(?:\s+(.+))?', re.MULTILINE) payload = json.loads(request.form.get('payload')) commits_url_template = get_commits_url_template(payload) for commit in payload['commits']: message = commit['message'].encode('utf-8') issue_refs = cmd_pattern.findall(message) if not issue_refs: continue commit_node = commit['node'] commit_url = commits_url_template % commit['raw_node'] timestamp = commit['utctimestamp'] author = commit['author'].encode('utf-8') match = re.search(r'<(.+?)>', commit['raw_author']) if not match: app.logger.error("Cannot get author's email address.") abort(400) users = yt.getUsers(params={'q': match.group(1)}) if not users: app.logger.error('Cannot find user with email ' + match.group(1)) abort(400) if len(users) != 1: app.logger.error('Not unique email address ' + match.group(1)) abort(400) comment = "Commit [%s %s] made by '''%s''' on ''%s''\n{quote}%s{quote}" \ % (commit_url, commit_node, author, timestamp, message) cmd_exec_result = True for issue_id, command in issue_refs: if command is None: command = '' try: app.logger.info( "Adding commit %s to issue %s (command: %s)" % (commit_node, issue_id, command)) yt.executeCommand(issue_id, command, comment, run_as=users[0].login) except YouTrackException as e: cmd_exec_result = False app.logger.error( 'Failed to add commit %s to issue %s: %s' % (commit_node, issue_id, e.message)) if not cmd_exec_result: abort(500) return 'success'
def process_commits(secret=None): if context_secret and secret != context_secret: abort(403) yt = Connection(yt_url, yt_login, yt_password) try: cmd_pattern = re.compile( r'#((?:%s)-\d+)(?:\s+(.+))?' % '|'.join(yt.getProjects().keys()), re.IGNORECASE | re.MULTILINE) except YouTrackException: app.logger.warning('Cannot get projects from YT') cmd_pattern = re.compile(r'#([A-z]+-\d+)(?:\s+(.+))?', re.MULTILINE) payload = json.loads(request.form.get('payload')) commits_url_template = get_commits_url_template(payload) for commit in payload['commits']: message = commit['message'].encode('utf-8') issue_refs = cmd_pattern.findall(message) if not issue_refs: continue commit_node = commit['node'] commit_url = commits_url_template % commit['raw_node'] timestamp = commit['utctimestamp'] author = commit['author'].encode('utf-8') match = re.search(r'<(.+?)>', commit['raw_author']) if not match: app.logger.error("Cannot get author's email address.") abort(400) users = yt.getUsers(params={'q': match.group(1)}) if not users: app.logger.error('Cannot find user with email ' + match.group(1)) abort(400) if len(users) != 1: app.logger.error('Not unique email address ' + match.group(1)) abort(400) comment = "Commit [%s %s] made by '''%s''' on ''%s''\n{quote}%s{quote}" \ % (commit_url, commit_node, author, timestamp, message) cmd_exec_result = True for issue_id, command in issue_refs: if command is None: command = '' try: app.logger.info("Adding commit %s to issue %s (command: %s)" % (commit_node, issue_id, command)) yt.executeCommand(issue_id, command, comment, run_as=users[0].login) except YouTrackException as e: cmd_exec_result = False app.logger.error('Failed to add commit %s to issue %s: %s' % (commit_node, issue_id, e.message)) if not cmd_exec_result: abort(500) return 'success'
def host_elegido(bot, update, user_data): bot.sendChatAction(chat_id=update.callback_query.from_user.id, action=ChatAction.TYPING) usuario = usuarios.getCollection().find_one( {'chat_id': update.callback_query.from_user.id}) logger.info('Elegir host Opción {}'.format(update.callback_query.data)) host = next(x for x in usuario['hosts'] if x['host'] == update.callback_query.data) user_data['host'] = host try: connection = Connection(user_data['host']['host'], user_data['host']['username'], user_data['host']['pass']) connections[usuario['chat_id']] = connection proyectos = connection.getProjects() except YouTrackException as e: logger.error(e) del user_data['host']['pass'] usuarios.getCollection().update( {'chat_id': update.callback_query.from_user.id}, {'$pull': { 'hosts': user_data['host'] }}) return CONFIRMAR keyboard = [] for proyecto in proyectos.keys(): keyboard.append( InlineKeyboardButton(proyectos[proyecto], callback_data=proyecto)) # Acomodo el teclado keyboard = [keyboard[i:i + 3] for i in range(0, len(keyboard), 3)] reply_markup = InlineKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True) update.callback_query.edit_message_text(text="Bien! Elegí un proyecto", reply_markup=reply_markup) return PROYECTO
def main(a_pat, yt_url, yt_login, yt_pass): """ Creates connections to Asana and your YouTrack site, then migrates tasks """ a_conn = asana.Client.access_token(a_pat) yt_conn = Connection(yt_url, yt_login, yt_pass) print 'Logged in with Asana User {} and YouTrack User {}'.format( a_conn.users.me()['name'], yt_conn.getUser(yt_login).login) # Get list of Asana Workspaces to migrate - all except Personal Projects a_workspaces = [ w for w in a_conn.workspaces.find_all() if w['name'] != "Personal Projects" ] print "Found Asana Workspaces (excluding Personal Projects): {}".format( [a['name'] for a in a_workspaces]) yt_projects = [yt_conn.getProject(p) for p in yt_conn.getProjects()] print "Found existing YouTrack Projects: {}".format( [y.name for y in yt_projects]) for a_work in a_workspaces: if a_work['name'] not in [p.name for p in yt_projects]: print "Creating YouTrack Project from {} Workspace".format( a_work['name']) yt_proj = None for p in yt_projects: if p.name == a_work['name']: yt_proj = p break if yt_proj is None: yt_proj = yt.Project() yt_proj.name = a_work['name'] yt_proj.id = a_work['name'].replace(' ', '').upper() yt_proj.lead = yt_login print yt_conn.createProject(yt_proj) field_name = 'AsanaID' cf = yt.CustomField() cf.name = field_name cf.type = 'string' cf.isPrivate = False cf.visibleByDefault = False # print "Existing Project Fields: {}".format(yt_conn.getProjectCustomFields(yt_proj.id)) try: asana_id_exists = yt_conn.getCustomField(field_name) except: asana_id_exists = False if not asana_id_exists: print 'Creating YouTrack Custom Field to save our Asana ID in' yt_conn.createCustomField(cf) try: asana_id_exists = yt_conn.getProjectCustomField( yt_proj.id, 'Due Date') except: asana_id_exists = None if not asana_id_exists: print 'Adding YouTrack Due Date Field to {} Project'.format( yt_proj.id) yt_conn.createProjectCustomFieldDetailed(yt_proj.id, 'Due Date', '') try: asana_id_exists = yt_conn.getProjectCustomField( yt_proj.id, field_name) except: asana_id_exists = False if not asana_id_exists: print 'Adding YouTrack Custom Field {} to {} Project'.format( field_name, yt_proj.id) yt_conn.createProjectCustomFieldDetailed(yt_proj.id, field_name, '') # Migrate users and save our list for later so we can assign people migrate_workspace_users(a_conn, yt_conn, a_work) migrate_projects_to_subsystems(a_conn, yt_conn, a_work, yt_proj) migrate_tasks_to_issues(a_conn, yt_conn, a_work, yt_proj, yt_login)
def identificar(bot, update, user_data): info = update.message.text bot.sendChatAction(chat_id=update.message.chat_id, action=ChatAction.TYPING) if not user_data.get('host', None) or not user_data['host'].get( 'host', None): logger.info("Host received {}".format(info)) try: info = checkAndFixUrl(info) user_data['host'] = {} user_data['host']['host'] = info keyboard = [[ InlineKeyboardButton(text="Correcto", callback_data='host_ok'), InlineKeyboardButton(text="Corregir", callback_data='host_ko') ]] reply_markup = InlineKeyboardMarkup(keyboard, resize_keyboard=False, one_time_keyboard=True) update.message.reply_text("Es correcto el host? {}:".format( user_data['host']['host']), reply_markup=reply_markup) return CONFIRMAR except Exception as e: logger.error(e) update.message.reply_text( "{} no parece ser un host correcto, intentá de nuevo".format( info)) return IDENTIFICAR elif not user_data['host'].get('username', None): logger.info("Username received {}".format(info)) user_data['host']['username'] = info keyboard = [[ InlineKeyboardButton(text="Correcto", callback_data='username_ok'), InlineKeyboardButton(text="Corregir", callback_data='username_ko') ]] reply_markup = InlineKeyboardMarkup(keyboard, resize_keyboard=False, one_time_keyboard=True) update.message.reply_text("Es correcto el usuario? {}:".format( user_data['host']['username']), reply_markup=reply_markup) return CONFIRMAR else: logger.info("Password received") # Try to login user_data['host']['pass'] = info try: connection = Connection(user_data['host']['host'], user_data['host']['username'], user_data['host']['pass']) logger.info("good login") usuario = usuarios.getCollection().find_one( {'chat_id': update.message.chat_id}) if not usuario: usuarios.getCollection().insert_one({ 'chat_id': update.message.chat_id, 'hosts': [user_data['host']] }) else: usuarios.getCollection().update_one( {'chat_id': update.message.chat_id}, {'$push': { 'hosts': user_data['host'] }}) logger.info(user_data['host']) proyectos = connection.getProjects() keyboard = [] for proyecto in proyectos.keys(): keyboard.append([ InlineKeyboardButton(proyectos[proyecto], callback_data=proyecto) ]) reply_markup = InlineKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True) update.message.reply_text("Bien! Elegí un proyecto", reply_markup=reply_markup) return PROYECTO except Exception as e: logger.error(e) update.message.reply_text("Clave incorrecta") return IDENTIFICAR return CONFIRMAR
class YouTrackExporter(object): def __init__(self, yt, influx): self.yt = YouTrack(**yt) self.influx = InfluxDBClient(**influx) self.logger = logging.getLogger('worktime_reporter') self.project_blacklist = ['RRS', 'RR_INS'] def get_all_issues(self, project): def _gen(): skip = 0 while True: issues = self.yt.getIssues(project.id, 'Spent time: -?', skip, 50) if not issues: break skip += len(issues) yield issues return itertools.chain.from_iterable(_gen()) def issue_to_measurement(self, project, issue): self.logger.info(f'Looking into issue {issue.id}') lead_time = getattr(issue, 'Lead time', None) stream = getattr(issue, 'Stream', None) target = getattr(issue, 'Target', 'Core') _type = getattr(issue, 'Type', None) resolved = getattr(issue, 'resolved', None) estimation = getattr(issue, 'Estimation', None) tags = { 'project': project.id, 'issue': issue.id, 'stream': stream, 'target': target, 'type': _type } counter = defaultdict(lambda: 0) for work_item in self.yt.getWorkItems(issue.id): _tags = dict(tags) _tags['author'] = getattr(work_item, 'authorLogin', None) created = getattr(work_item, 'created', None) work_type = getattr(work_item, 'worktype', None) date = getattr(work_item, 'date', created) duration = int(work_item.duration) if not created: continue created_dt = datetime.datetime.fromtimestamp(float(created)/1000) date_dt = datetime.datetime.fromtimestamp(float(date)/1000) time_dt = datetime.datetime.combine(date_dt.date(), created_dt.time(), date_dt.tzinfo) time_ts = int(datetime.datetime.timestamp(time_dt)*1000) counter[work_type] += duration yield { 'measurement': 'work_item', 'tags': _tags, 'time': time_ts, 'fields': { work_type: duration } } cycle_time = counter['Analytics'] + counter['Development'] + counter['Testing'] created = getattr(issue, 'created', None) if estimation: yield { 'measurement': 'issue', 'tags': tags, 'time': int(created), 'fields': { 'estimation': int(estimation) } } if lead_time and resolved: yield { 'measurement': 'issue', 'tags': tags, 'time': int(resolved), 'fields': { 'lead_time': float(lead_time), 'cycle_time': cycle_time, } } def process_project(self, project): if project.id in self.project_blacklist: self.logger.info(f'Skipping project {project.id}, blacklisted') return ts = self.yt.getProjectTimeTrackingSettings(project['id']) if not ts.Enabled: self.logger.info( f'Skipping project {project.id}, no time tracking') return self.logger.info(f'Looking into project {project.id}') issues = self.get_all_issues(project) measurements = (self.issue_to_measurement(project, issue) for issue in issues) return itertools.chain.from_iterable(measurements) def export(self): def _gen(): projects = self.yt.getProjects() for project_id in projects: project = self.yt.getProject(project_id) measurements = self.process_project(project) if measurements: yield measurements self.influx.drop_measurement('issue') self.influx.drop_measurement('work_item') measurements = itertools.chain.from_iterable(_gen()) for chunk in grouper(measurements, 50): filtered = filter(lambda x: x is not None, chunk) self.influx.write_points(filtered, time_precision='ms')