| #!/usr/bin/env python3 |
| """Update JIRA from a CVS file. |
| |
| New users that appear in the CSV can be added if needed. |
| New components can be added or existing components updated. |
| |
| example.csv |
| Project,Group,Component Name,Owner,Email,Note |
| FPII,arima,Music,Foo Bar,foobar@fairphone.com, |
| FPII,arima,OOBE,Andrew Appleby,andrewa@fairphone.com, |
| |
| CSV fields: |
| * 'Component name' - Name of component to change or add |
| * 'Group' - Group to add users to |
| * 'Note' - Description to be used for the component |
| * 'Owner' - JIRA user name responsible for a component |
| * 'Project' - Project to add components to |
| * 'Email' - Email address for the JIRA user |
| """ |
| import argparse |
| import csv |
| import os |
| import sys |
| import logging |
| from jira.exceptions import JIRAError |
| from jira_fairphone import JiraFairphone |
| import log |
| |
| __log__ = logging.getLogger(__name__) |
| |
| FIELD_COMPONENT = "Component Name" |
| FIELD_EMAIL = "Email" |
| FIELD_GROUP = "Group" |
| FIELD_NOTE = "Note" |
| FIELD_PROJECT = "Project" |
| FIELD_USER_NAME = "Owner" |
| |
| |
| def cmdline_args(): |
| """Add and parse command line arguments. |
| |
| :returns Namespace: |
| Populated namespace for the command line arguments. |
| """ |
| parser = argparse.ArgumentParser( |
| description=__doc__, formatter_class=argparse.RawTextHelpFormatter |
| ) |
| parser.add_argument( |
| "--user", |
| type=str, |
| dest="jira_user", |
| default=os.environ.get("JIRA_USER", None), |
| help="JIRA username to log into JIRA with.\nDefault: env JIRA_USER", |
| ) |
| parser.add_argument( |
| "--password", |
| type=str, |
| dest="jira_password", |
| default=os.environ.get("JIRA_PASSWORD", None), |
| help="JIRA password.\nDefault: env JIRA_PASSWORD", |
| ) |
| parser.add_argument( |
| "csvfile", |
| type=argparse.FileType("r"), |
| help="CSV file to import from.\n", |
| ) |
| parser.add_argument( |
| "--update-groups", |
| action="store_true", |
| default=False, |
| dest="update_groups", |
| help="If a JIRA group is missing, create it.\n", |
| ) |
| parser.add_argument( |
| "--update-users", |
| action="store_true", |
| dest="update_users", |
| help="Add or update JIRA users.\n" |
| "Required CSV fields:\n" |
| " * Owner\n" |
| " * Email\n" |
| "Optional CSV fields:\n" |
| " * Group\n", |
| ) |
| parser.add_argument( |
| "--update-components", |
| action="store_true", |
| dest="update_components", |
| help="Add or update JIRA components.\n" |
| "Required CSV fields:\n" |
| " * Component name\n" |
| " * Project\n" |
| "Optional CSV fields:\n" |
| " * Owner\n" |
| " * Note\n", |
| ) |
| parser.add_argument( |
| "--dry-run", |
| action="store_true", |
| help="Run script without modifying JIRA.", |
| ) |
| parser.add_argument( |
| "-l", |
| "--log", |
| default=sys.stderr, |
| type=argparse.FileType("w"), |
| help="Log file. Default: stderr.", |
| ) |
| parser.add_argument( |
| "-v", |
| "--verbose", |
| default=0, |
| action="count", |
| help="Increase log level. May be used several times.", |
| ) |
| parser.add_argument( |
| "-q", |
| "--quiet", |
| default=0, |
| action="count", |
| help="Decrease log level. May be used several times.", |
| ) |
| |
| args = parser.parse_args() |
| |
| log.configure_logger("", args.log, args.verbose, args.quiet) |
| |
| __log__.info("JIRA username: %s", args.jira_user) |
| if args.update_groups: |
| __log__.info("Update JIRA groups") |
| if args.update_users: |
| __log__.info("Update JIRA users") |
| if args.update_components: |
| __log__.info("Update JIRA components") |
| if args.dry_run: |
| __log__.info("Dry run") |
| |
| return args |
| |
| |
| def update_groups(jira, csv_list): |
| """Add new JIRA groups from CVS list. |
| |
| Args: |
| jira: Fairphone jira wrapper to interact with. |
| csv_list: A list of user information populated from the CSV file. |
| """ |
| for row in csv_list: |
| group = row.get(FIELD_GROUP, None) |
| if group and not jira.groups(query=group): |
| jira.add_group(group) |
| |
| |
| def update_users(jira, csv_list): |
| """Update or add new JIRA users from CVS list. |
| |
| Args: |
| jira: Fairphone jira wrapper to interact with. |
| csv_list: A list of user information populated from the CSV file. |
| """ |
| for row in csv_list: |
| username = row.get(FIELD_USER_NAME, None) |
| email = row.get(FIELD_EMAIL, None) |
| |
| if not username: |
| __log__.warning("User name missing") |
| continue |
| |
| if not email: |
| __log__.warning("Email missing for user %s", username) |
| continue |
| |
| jira.add_user(username, email) |
| |
| group = row.get(FIELD_GROUP, None) |
| if group: |
| jira.add_user_to_group(username, group) |
| |
| |
| def update_components(jira, csv_list): |
| """Add and update JIRA components from a csv list. |
| |
| Args: |
| jira: Fairphone jira wrapper to interact with. |
| csv_list: A list of user information populated from the CSV file. |
| |
| """ |
| jira_component_dict = {} |
| |
| def get_jira_component_info(project): |
| return { |
| jira_component.name: jira_component.id |
| for jira_component in jira.project_components(project) |
| } |
| |
| for row in csv_list: |
| |
| project = row.get(FIELD_PROJECT, None) |
| if not project: |
| __log__.warning("Missing project name") |
| continue |
| |
| component_name = row.get(FIELD_COMPONENT, None) |
| if not component_name: |
| __log__.warning("Missing component name") |
| continue |
| |
| if project not in jira_component_dict: |
| jira_component_dict[project] = get_jira_component_info(project) |
| |
| jira_component_id = jira_component_dict[project].get( |
| component_name, None |
| ) |
| |
| if not jira_component_id: |
| jira.create_component( |
| component_name, |
| project, |
| description=row.get(FIELD_NOTE, None), |
| leadUserName=row.get(FIELD_USER_NAME, None), |
| ) |
| else: |
| jira.update_component( |
| jira_component_id, |
| project, |
| description=row.get(FIELD_NOTE, None), |
| leadUserName=row.get(FIELD_USER_NAME, None), |
| ) |
| |
| |
| if __name__ == "__main__": |
| ARGS = cmdline_args() |
| |
| try: |
| JIRA = JiraFairphone(ARGS.jira_user, ARGS.jira_password, ARGS.dry_run) |
| |
| CSV_READER = csv.DictReader(ARGS.csvfile) |
| CSV_LIST = list(CSV_READER) |
| |
| if ARGS.update_groups: |
| update_groups(JIRA, CSV_LIST) |
| |
| if ARGS.update_users: |
| update_users(JIRA, CSV_LIST) |
| |
| if ARGS.update_components: |
| update_components(JIRA, CSV_LIST) |
| |
| except JIRAError as error: |
| __log__.exception("Jira exception. Status: %d", error.status_code) |