def __init__(self, url, key, project=None, component_conf={}, custom_fields=[], reporter_field=None): """Initialise the client :param str url: the URL where the redmine is hosted :param str key: the authentication API REST key :param int or str project: the project identifier. It is None, then it takes the default one. :param dict component_conf: the component configuration to set the component based on the configuration file. :param list custom_fields: the custom fields that are mandatory to set when creating the issue. :param dict reporter_field: field to set reporter email """ self.redmine = redmine.Redmine(url, key=key) self.project = project self.component_conf = component_conf self.custom_fields = custom_fields self.reporter_field = reporter_field # Use Bug by now, we can look for Crash Report tracker as well tracker_elems = filter(lambda t: t['name'] == 'Bug', self.redmine.tracker.all()) if len(tracker_elems) > 0: self.tracker_id = tracker_elems[0]['id'] else: self.tracker_id = None
def __init__(self, host, key): self.connection = redmine.Redmine(host, key=key) # Prefetch redmine activities data = self.connection.enumeration.filter( \ resource="time_entry_activities") for activity in data: name = activity.name.lower() self.activities.append(name) self.activities_id[name] = activity.id
def run(self, ticket): version = self.dispatcher.call_sync('system.info.version') project_name = '-'.join(version.split('-')[:2]).lower() attachments = [] debug_file_name = os.path.join( DEFAULT_DEBUG_DUMP_DIR, version + '_' + time.strftime('%Y%m%d%H%M%S') + '.tar.gz') try: rm_connection = redmine.Redmine(BUGTRACKER_ADDRESS, username=ticket['username'], password=unpassword( ticket['password'])) rm_connection.auth() for attachment in ticket.get('attachments', []): attachment = os.path.normpath(attachment) attachments.append({ 'path': attachment, 'filename': os.path.split(attachment)[-1] }) if not os.path.exists(attachment): raise TaskException( errno.ENOENT, 'File {} does not exists.'.format(attachment)) if ticket.get('debug'): self.run_subtask_sync('debug.save_to_file', debug_file_name) attachments.append({ 'path': debug_file_name, 'filename': os.path.split(debug_file_name)[-1] }) redmine_response = rm_connection.issue.create( project_id=project_name, subject=ticket['subject'], description=ticket['description'], category_id=ticket['category'], custom_fields=[{ 'id': 2, 'value': VERSION_CODES['BETA2'] }], is_private=ticket.get('debug', False), tracker_id=1 if ticket['type'] == 'bug' else 2, uploads=attachments) except redmine.exceptions.AuthError: raise TaskException(errno.EINVAL, 'Invalid username or password') finally: if ticket.get('debug') and os.path.exists(debug_file_name): os.remove(debug_file_name) return redmine_response.id
def main(this, argv): parser = this.get_base_parser() (options, args) = parser.parse_known_args(argv) config = this.parse_config(options) subcommand_parser = this.get_subcommand_parser() this.parser = subcommand_parser if options.help or not argv: subcommand_parser.print_help() return 0 api_key = None if options.unauthenticated is False: api_key = os.getenv('REDCLI_API_KEY', None) if api_key is None: try: api_key = config.get('credentials', 'api_key') except ConfigParser.Error as e: print >>sys.stderr, "You need to specify your API key either via " \ "$REDCLI_API_KEY or in your configuration file" return 1 auth_url = options.auth_url if auth_url is None: try: auth_url = config.get('global', 'auth_url') except ConfigParser.Error as e: print >>sys.stderr, "You need to specify your authentication url either via " \ "--auth-url or in your configuration file" return 2 args = subcommand_parser.parse_args(argv) # Merge args from command line and config file this.merge_config_args(args) # Does the user provides some callbacks for printing this.handle_user_exit(args) if args.func == this.do_help: this.do_help(args) return 0 cli = redmine.Redmine(auth_url, key=api_key, debug=args.debug, version=args.redmine_version) return args.func(cli, args)
def _redmine_connect(self): # Задаем формат даты, не проверям сертификат сервера на валидность, возвращаем redmine дескриптор self.logger.debug("issue._redmine_connect started") if not self.rd: server = self.parameters.get("server") key = self.parameters.get("key") self.rd = redmine.Redmine(server, key=key, date_format='%Y.%m.%d', requests={'verify': False}) self.logger.debug("issue._redmine_connect: rd={0} created".format( self.rd)) return self.rd
def __init__(self, url, key, project=None, component_conf={}, custom_fields=[], reporter_field=None, status={}): """Initialise the client :param str url: the URL where the redmine is hosted :param str key: the authentication API REST key :param int or str project: the project identifier. It is None, then it takes the default one. :param dict component_conf: the component configuration to set the component based on the configuration file. :param list custom_fields: the custom fields that are mandatory to set when creating the issue. :param dict reporter_field: field to set reporter email :param dict status: indicate which are valid closed status identifiers and reopened status identifiers for reopening closed issues once a new duplicate is uploaded. *closed* is a tuple with valid closed status ids *reopened* is the status to go after reopening an issue """ self.redmine = redmine.Redmine(url, key=key) self.project = project self.component_conf = component_conf self.custom_fields = custom_fields self.reporter_field = reporter_field # Use Bug by now, we can look for Crash Report tracker as well tracker_elems = filter(lambda t: t['name'] == 'Bug', self.redmine.tracker.all()) if len(tracker_elems) > 0: self.tracker_id = tracker_elems[0]['id'] else: self.tracker_id = None self.status = None # Make sure closed and reopened are consistent if 'closed' in status and 'reopened' in status: self.status = status # Make sure an iterable is always used for closed status if not hasattr(self.status['closed'], '__contains__'): self.status['closed'] = (self.status['closed'], )
def main(): config = get_config('redmine.yml') ## connect to redmine redmine_instance = redmine.Redmine(config['url'], username=config['username'], password=config['password']) user_id = redmine_instance.user.get('current').id all_statuses = redmine_instance.issue_status.all() all_projects = redmine_instance.project.all() filters = get_filters(config, all_statuses, all_projects) status_id_filter_str = filters['status_id_filter_str'] filtered_projects = filters['filtered_projects'] included_custom_fields = filters['included_custom_fields'] statuses_by_importance = get_statuses_by_importance(config, all_statuses) critical_statuses = statuses_by_importance['critical_statuses'] unimportant_statuses = statuses_by_importance['unimportant_statuses'] # get sorted list of issues issues = sorted(redmine_instance.issue.filter( assigned_to_id=user_id, status_id=status_id_filter_str), key=lambda issue: issue.priority['id'], reverse=True) for issue in issues: project_id = issue.project['id'] if project_id not in filtered_projects: if hasattr(issue, 'custom_fields'): # convert redmine.resources.CustomField object list to list of dicts issue_custom_fields = list() for custom_field in issue.custom_fields: issue_custom_fields.append({ 'name': custom_field['name'], 'value': custom_field['value'] }) for custom_field in included_custom_fields: if custom_field in issue_custom_fields: notification = build_notification( issue, statuses_by_importance) send_notification(notification) continue
def fetch_redmine_data(redmine_url, redmine_project, redmine_version, redmine_user, redmine_password, redmine_key): """Return a dict of redmine data resources :param redmine_url: Redmine access http/s url :param redmine_project: Name of the Redmine project to use :param redmine_version: Version to search for in the Redmine project :param redmine_user: Login name for the a valid Redmine user :param redmine_password: Password for ``redmine_user`` :param redmine_key: User's auth token (alternative to ``redmine_user`` and ``redmine_password``) :returns: Context dict populated with the following data: - redmine_api: Object for redmine at *redmine_url* - redmine_project: Object for *redmine_project* at *redmine_url* - redmine_version: Object for *redmine_version* at *redmine_project* - redmine_issues: List of objects for open issues in *redmine_project* at *redmine_version* """ api = redmine.Redmine(redmine_url, username=redmine_user, password=redmine_password, key=redmine_key) project = get_project_by_name(api, redmine_project) if project is None: error(u'redmine: Project "{}" not found'.format(redmine_project)) version = get_version_by_name(project, redmine_version) if version is None: error(u'redmine: Project "{}" has no version "{}"' .format(redmine_project, redmine_version)) issues = api.issue.filter( project_id=project.id, fixed_version_id=version.id, status_id='closed' ) return dict( redmine_api=api, redmine_issues=issues, redmine_project=project, redmine_version=version )
def __init__(self, url, user, password, project_name, verify=True): """ Initializes the Redmine reader and connects to the REST service :param url: Redmine url, e.g. https://redmine.com/ :param user:User to access redmine with, e.g. admin :param password:Password to access redmine with, e.g. paSSw0rd :param project_name:Name of the project, this can be read from the URL, e.g. https://redmine.com/projects/{project} :param verify:Flag indicating whether to accept non-verified SSL certificates """ requests_config = {'verify': verify} self.redmine = redmine.Redmine(url, username=user, password=password, requests=requests_config) self.entry_meta = {} self.issue_meta = {} # Open project self.project = self.redmine.project.get(project_name)
import csv import redmine from django.shortcuts import render, redirect, HttpResponse, render_to_response, RequestContext from django.views.generic.edit import View import django.forms as forms from django.core.context_processors import csrf from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator import redmine from . import settings as s REDMINE_OBJECT = redmine.Redmine(s.REDMINE_SETTINGS['server'], username=s.REDMINE_SETTINGS['username'], password=s.REDMINE_SETTINGS['password']) try: OUTPUT_CSV = s.OUTPUT_CSV except: OUTPUT_CSV = 'rm_report.csv' # Create your views here. class ExportForm(forms.Form): def __init__(self, *args, rm=REDMINE_OBJECT, **kwargs): super(ExportForm, self).__init__(*args, **kwargs) projects = rm.project.all() self.fields['project'].choices = [(p.identifier, p.name) for p in projects]
def main(): # We get a redmine connection rm = redmine.Redmine(redmine_url, key=redmine_key) if os.path.exists(".rev_prev"): rev_prev = int(open(".rev_prev", 'r').read()) else: logging.critical("Not having the .rev_prev file is very BAD !!!") sys.exit(1) # We list all SVN logs since last time logs = pysvn.Client().log( svn_url, revision_start=pysvn.Revision(pysvn.opt_revision_kind.number, rev_prev), revision_end=pysvn.Revision(pysvn.opt_revision_kind.head), limit=rev_limit ) last_rev = None for log in logs: author = log["author"] revision = log.revision.number date = time.ctime(log.date) message = log.message logging.info("* {revision} - {date} - {author} : {message}".format(revision=revision, date=date, author=author, message=message.replace("\n", ".").replace("\r", "."))) if not last_rev or revision > last_rev: last_rev = revision changes_by_issue = handle_log(message, author, revision, date) if changes_by_issue: logging.debug("Changes: %s", json.dumps(changes_by_issue)) for issue_id, changes in changes_by_issue.iteritems(): logging.info("Considering update of issue %s ...", issue_id) try: issue = rm.issue.get(issue_id) if not issue: logging.warning("Issue %s doesn't exist !!!", issue_id) break else: msg = "SVN r{rev},".format(rev=revision) for jl in issue.journals: logging.debug(" Note: "+jl.notes.replace("\n", ".").replace("\r", ".")) if msg in jl.notes: logging.warning("There's already a reference to our notes !") changes = None break if not test_only and changes: logging.info("Updating issue %s ...", issue_id) try: rm.issue.update(issue_id, **changes) except redmine.ValidationError: if "status_id" in changes.keys(): logging.info("Removing status change for issue %s", issue_id) del changes["status_id"] rm.issue.update(issue_id, **changes) else: logging.exception("Our issue wasn't validated %s", issue_id) except Exception: logging.exception("Problem handling issue %s", issue_id) if last_rev: open(".rev_prev.tmp", 'w').write(str(last_rev)) os.rename(".rev_prev.tmp", ".rev_prev")
if bConsole: print 'Are your sure above information is correct?' print 'Press y to continue, q to quit.' while 1: c = getkey() if c == 'y' or c == 'Y': break elif c == 'q' or c == 'Q': sys.exit(1) ''' Redmine Connection ''' if bAPIKey: print "Auth by 'API access key'" + IESUX demo = redmine.Redmine('http://rd1-1/redmine', key=APIKEY) else: print "Auth by 'username/password'" + IESUX demo = redmine.Redmine('http://rd1-1/redmine', username=USER, password=PASS) ''' Created project under "OBM projects >> OBM 2012 Project" ''' # Get the parent Project, e.g. OBM projects: http://rd1-1/redmine/projects/obm-projects obm_prj = demo.getProject(SubProjectOf) try: parent_prj = obm_prj.newSubProject( name=PRJ_MODELNAME,
import pytz import redmine import re import datetime import inflect from refreshbooks import api as refreshbooks my_redmine_user_id = 22 noop = False timezone = pytz.timezone("Australia/Brisbane") inflector = inflect.engine() redmine_url = os.environ["REDMINE_URL"] redmine_key = os.environ["REDMINE_KEY"] redmine_client = redmine.Redmine(redmine_url, key=redmine_key, version=1.4) freshbooks_host = os.environ["FRESHBOOKS_HOST"] freshbooks_key = os.environ["FRESHBOOKS_KEY"] freshbooks_client = refreshbooks.TokenClient(freshbooks_host, freshbooks_key) redmine_id_regex = re.compile("^Redmine ID: (\d+)$", re.MULTILINE) project_lookup = dict() task_lookup = dict() time_entry_lookup = dict() def freshbooks_items(item_name, **kwargs): '''Iterates over collection of items from `item_name` from all pages.''' collection = inflector.plural(item_name)
def get_redmine_client(config): return redmine.Redmine(config['redmine-base-address'], key=config['api-key'])
notice.show() return # config config_filename = 'redmine.yml' config = yaml.load( open(path.join(path.dirname(path.abspath(__file__)), config_filename))) redmine_url = config['url'] redmine_username = config['username'] redmine_password = config['password'] redmine_ieca = redmine.Redmine(redmine_url, username=redmine_username, password=redmine_password) user_id = redmine_ieca.user.get('current').id statuses = redmine_ieca.issue_status.all() projects = redmine_ieca.project.all() # fiter by status status_filter = config['status_filter'] filtered_statuses = [ status['id'] for status in statuses if status['name'] in status_filter ] status_id_filter_str = '!' + '|'.join( str(status_id) for status_id in filtered_statuses) # filter by project
def connect_redmine(self, configuration): self.project_name = configuration["redmine_projectname"] self.configuration = configuration self.redmine = redmine.Redmine(configuration["redmine_url"], key=configuration["redmine_api_key"], raise_attr_exception=False)
def sync_hours_for_date(password, date_str): in_date = strptime(date_str, '%d/%m/%Y') doy = in_date.tm_yday print "Syncing %d/%d/%d (%s)" % (in_date.tm_mday, in_date.tm_mon, in_date.tm_year, doy) h = Harvest(HARVEST_URL_ROOT, HARVEST_USER_EMAIL, password) rm = redmine.Redmine(REDMINE_URL_ROOT, key=REDMINE_API_KEY) rm_date = date(*in_date[:3]) rm_users = rm.users rm_user = rm_users[6] day = h.get_day(doy, 2016) activities = rm.time_entry_activities development = None meeting = None proj_man = None for activity in activities: if activity.name == 'Development': development = activity elif activity.name == 'Meeting': meeting = activity if activity.name == 'Project Management': proj_man = activity if development is None or meeting is None or proj_man is None: raise ValueError('Cant find all activity types') at_map = { 'Coding': development, 'Meeting': meeting, 'Project Management': proj_man } for day_entry in day['day_entries']: if day_entry['client'] != CLIENT_NAME: continue activity = at_map.get(day_entry['task']) if not activity: print "Can't map activity '%s'" % day_entry['task'] continue if day_entry['notes'] is not None and 'logged' in day_entry[ 'notes'].lower(): continue elif day_entry['notes'] is None: day_entry['notes'] = '' if activity == development or day_entry['notes'].startswith('#'): if day_entry['notes'].startswith('#'): try: ticket_id = int(day_entry['notes'][1:]) except (TypeError, ValueError): print "Can't parse ID on %s" % day_entry['notes'] continue entry_notes = '' else: print "Not logging {}".format(day_entry['notes']) continue issue = rm.issues[ticket_id] try: te = rm.time_entries.new(issue=issue, activity=activity, spent_on=rm_date.strftime('%Y-%m-%d'), user=rm_user, hours=day_entry['hours'], comments=entry_notes) except Exception as e: print e.read() return if day_entry['notes'] == '': day_entry['notes'] = 'Logged' else: day_entry['notes'] += ' Logged' try: h.update(day_entry['id'], day_entry) except Exception as e: print "Failed to save time for %d. Delete manually" % ticket_id return print "Logged %02f hours for #%d" % (day_entry['hours'], ticket_id)