def set_delimiter(dhis_version, object_filter): """ Operator and rootJunction alias validation for the DHIS2 API (see DHIS2 developer docs) :param dhis_version: DHIS 2 version as an integer (e.g. 32): :param object_filter: Metadata object filter :return: tuple of aliases for OR / AND """ if not object_filter: return None, None if '^' in object_filter: if dhis_version >= 28: raise PKClientException( "ArgumentError: Operator '^' was replaced with '$' in 2.28 onwards. Nothing shared." ) if '||' in object_filter: if dhis_version < 25: raise PKClientException( "ArgumentError: rootJunction 'OR' / '||' is only supported 2.25 onwards. Nothing shared." ) if '&&' in object_filter: raise PKClientException( "ArgumentError: Not allowed to combine delimiters '&&' and '||'. Nothing shared" ) return '||', 'OR' return '&&', 'AND'
def create_obj(self, response): """ Create ShareableObjects from the response :param response: server response :return yielded ShareableObjects """ for elem in response: try: public_access = Permission.from_symbol(elem['publicAccess']) except ValueError as e: raise PKClientException(e) except KeyError: raise PKClientException( "ServerError: Public access is not set " "for {} {} '{}'".format(self.name, elem['id'], elem['name'])) else: yield ShareableObject(obj_type=self.name, uid=elem['id'], name=elem['name'], code=elem.get('code'), public_access=public_access, usergroup_accesses={ UserGroupAccess.from_dict(d) for d in elem['userGroupAccesses'] })
def set_delimiter(version, argument): """ Operator and rootJunction Alias validation :param version: DHIS2 version :param argument: Argument as received from parser :return: tuple(delimiter, rootJunction) """ if not argument: return None, None if '^' in argument: if version >= 28: raise PKClientException( "ArgumentError: Operator '^' was replaced with '$' in 2.28 onwards. Nothing shared." ) if '||' in argument: if version < 25: raise PKClientException( "ArgumentError: rootJunction 'OR' / '||' is only supported 2.25 onwards. Nothing shared." ) if '&&' in argument: raise PKClientException( "ArgumentError: Not allowed to combine delimiters '&&' and '||'. Nothing shared" ) return '||', 'OR' return '&&', 'AND'
def validate_data_access(public_access, collection, usergroups, dhis_version): if dhis_version < NEW_SYNTAX: if public_access.data or any( [group.permission.data for group in usergroups.accesses]): raise PKClientException( "ArgumentError: You cannot set DATA access on DHIS2 versions below 2.29 " "- check your arguments (-a) and (-g)") else: if collection.data_sharing_enabled: log_msg = "ArgumentError: Missing {} permission for DATA access for '{}' (Argument {})" if not public_access.data: raise PKClientException( log_msg.format('Public Access', collection.name, '-a')) if not all( [group.permission.data for group in usergroups.accesses]): raise PKClientException( log_msg.format('User Groups', collection.name, '-g')) else: log_msg = "ArgumentError: Not possible to set {} permission for DATA access for '{}' (Argument {})" if public_access.data: raise PKClientException( log_msg.format('Public Access', collection.name, '-a')) if any([group.permission.data for group in usergroups.accesses]): raise PKClientException( log_msg.format('User Group', collection.name, '-g'))
def validate_args(args, dhis_version): if len(args.public_access) not in (1, 2): raise PKClientException( "ArgumentError: Must use -a METADATA [DATA] - max. 2 arguments") if args.groups: for group in args.groups: try: metadata_permission = group[1] except IndexError: raise PKClientException( "ArgumentError: Missing User Group permission for METADATA access" ) if metadata_permission not in access.keys(): raise PKClientException( 'ArgumentError: User Group permission for METADATA access not valid: "{}"' .format(metadata_permission)) if dhis_version >= NEW_SYNTAX: try: data_permission = group[2] except IndexError: pass else: if data_permission not in access.keys(): raise PKClientException( 'ArgumentError: User Group permission for DATA access not valid: "{}"' .format(data_permission))
def validate_data_access(public_access, collection, usergroups, dhis_version): """ Validate DATA access against public access, the collection and user groups. Raises Exception if invalid. :param public_access: Permission instance :param collection: ShareableObjectCollection instance :param usergroups: UserGroupsCollection instance :param dhis_version: DHIS 2 version as an integer (e.g. 32) :return: None """ if dhis_version < NEW_SYNTAX: if public_access and public_access != PUBLIC_ACCESS_INHERITED and public_access.data\ or any([group.permission.data for group in usergroups.accesses]): raise PKClientException( "ArgumentError: You cannot set DATA access on DHIS2 versions below 2.29 " "- check your arguments (-a) and (-g)") elif public_access != PUBLIC_ACCESS_INHERITED: if collection.data_sharing_enabled: log_msg = "ArgumentError: Missing {} permission for DATA access for '{}' (argument {})" if not public_access.data: raise PKClientException( log_msg.format('Public Access', collection.name, '-a')) if not all( [group.permission.data for group in usergroups.accesses]): raise PKClientException( log_msg.format('User Groups', collection.name, '-g')) else: log_msg = "ArgumentError: Not possible to set {} permission for DATA access for '{}' (argument {})" if public_access.data: raise PKClientException( log_msg.format('Public Access', collection.name, '-a')) if any([group.permission.data for group in usergroups.accesses]): raise PKClientException( log_msg.format('User Group', collection.name, '-g'))
def get_attribute_name(api, uid): try: return api.get('attributes/{}'.format(uid)).json()['name'] except APIException as exc: if exc.code == 404: raise PKClientException( "Attribute {} could not be found".format(uid)) else: raise PKClientException("Error: {}".format(exc))
def validate_csv(data): if not data[0].get('uid', None) or not data[0].get('attributeValue', None): raise PKClientException( "CSV not valid: CSV must have 'uid' and 'attributeValue' as headers" ) object_uids = [obj['uid'] for obj in data] for uid in object_uids: if not valid_uid(uid): raise PKClientException( "Object '{}' is not a valid UID in the CSV".format(uid)) if len(object_uids) != len(set(object_uids)): raise PKClientException("Duplicate Objects (rows) found in the CSV.") return True
def get_name(self, obj_type): shareable = self.schema('shareable') for name, plural in iteritems(shareable): if obj_type.lower() in (name.lower(), plural.lower()): return name, plural raise PKClientException( "No DHIS2 object type for '{}'".format(obj_type))
def attribute_is_on_model(api, attribute, typ): attr_get = {'fields': 'id,name,{}Attribute'.format(typ[:-1])} attr = api.get('attributes/{}'.format(attribute.uid), params=attr_get).json() if attr['{}Attribute'.format(typ[:-1])] is False: raise PKClientException( "Attribute {} ({}) is not assigned to type {}".format( attribute.name, attribute.uid, typ[:-1]))
def parse_args(): description = "{}Readable indicator definition to CSV.{}".format( Style.BRIGHT, Style.RESET_ALL) usage = "\n{}Example:{} dhis2-pk-indicator-definitions -s play.dhis2.org/demo -u admin -p district -t indicators".format( Style.BRIGHT, Style.RESET_ALL) types = {'programIndicators', 'indicators'} parser = argparse.ArgumentParser(usage=usage, description=description) parser._action_groups.pop() required = parser.add_argument_group('required arguments') required.add_argument('-t', dest='indicator_type', action='store', metavar='INDICATOR_TYPE', help="{}".format(" or ".join(types)), choices=types, required=True) optional = parser.add_argument_group('optional arguments') optional.add_argument('-s', dest='server', action='store', help="DHIS2 server URL") optional.add_argument( '-f', dest='indicator_filter', action='store', help= "Indicator filter, e.g. -f 'name:like:HIV' - see dhis2-pk-share --help" ) optional.add_argument('-u', dest='username', action='store', help="DHIS2 username") optional.add_argument('-p', dest='password', action='store', help="DHIS2 password") optional.add_argument('-v', dest='api_version', action='store', type=int, help='DHIS2 API version e.g. -v=28') args = parser.parse_args() if not args.password: if not args.username: raise PKClientException( "ArgumentError: Must provide a username via argument -u") password = getpass.getpass( prompt="Password for {} @ {}: ".format(args.username, args.server)) else: password = args.password return args, password
def get_name(self, obj_type): """ Get an object collection's name and it's plural name :param obj_type: type of object, e.g. dataelement :return: tuple of name and its plural name, e.g. dataElement, dataElements """ shareable = self.schema('shareable') for name, plural in iteritems(shareable): if obj_type.lower() in (name.lower(), plural.lower()): return name, plural raise PKClientException( "No DHIS2 object type for '{}'".format(obj_type))
def validate_args(args, dhis_version): """ Validate arguments. Raises Exception if invalid :param args: the argparse arguments :param dhis_version: DHIS 2 version as an integer (e.g. 32) :return: None """ if args.extend: if not args.groups and not args.public_access: raise PKClientException( "ArgumentError: Must supply user groups when extending sharing - check your argument (-g)" ) else: if not args.public_access or len(args.public_access) not in (1, 2): raise PKClientException( "ArgumentError: Must use -a METADATA [DATA] - max. 2 arguments" ) if args.groups: for group in args.groups: try: metadata_permission = group[1] except IndexError: raise PKClientException( "ArgumentError: Missing User Group permission for METADATA access" ) if metadata_permission not in access.keys(): raise PKClientException( 'ArgumentError: User Group permission for METADATA access not valid: "{}"' .format(metadata_permission)) if dhis_version >= NEW_SYNTAX: try: data_permission = group[2] except IndexError: pass else: if data_permission not in access.keys(): raise PKClientException( 'ArgumentError: User Group permission for DATA access not valid: "{}"' .format(data_permission))
def get_usergroup_uids(self, filter_list, root_junction='AND'): """ Get UserGroup UIDs :param filter_list: List of filters, e.g. ['name:like:ABC', 'code:eq:XYZ'] :param root_junction: AND or OR :return: List of UserGroup UIDs """ params = {'fields': 'id,name', 'paging': False, 'filter': filter_list} if root_junction == 'OR': params['rootJunction'] = root_junction endpoint = 'userGroups' response = self.api.get(endpoint, params=params).json() if len(response['userGroups']) > 0: return {ug['id']: ug['name'] for ug in response['userGroups']} else: raise PKClientException( "No userGroup found with {}".format(filter_list))
def parse_args(): description = "{}Post CSS stylesheet to a server.{}".format( Style.BRIGHT, Style.RESET_ALL) usage = "\n{}Example:{} dhis2-pk-post-css -s=play.dhis2.org/dev -u=admin -p=district -c=file.css".format( Style.BRIGHT, Style.RESET_ALL) parser = argparse.ArgumentParser(usage=usage, description=description) parser._action_groups.pop() required = parser.add_argument_group('required arguments') required.add_argument('-c', dest='css', action='store', required=True, help="Path to CSS file") optional = parser.add_argument_group('optional arguments') optional.add_argument('-s', dest='server', action='store', help="DHIS2 server URL") optional.add_argument('-u', dest='username', action='store', help='DHIS2 username') optional.add_argument('-p', dest='password', action='store', help='DHIS2 password') args = parser.parse_args() if not args.password: if not args.username: raise PKClientException( "ArgumentError: Must provide a username via argument -u") password = getpass.getpass( prompt="Password for {} @ {}: ".format(args.username, args.server)) else: password = args.password return args, password
def parse_args(): description = "{}Analyze data integrity.{}".format(Style.BRIGHT, Style.RESET_ALL) usage = "\n{}Example:{} dhis2-pk-data-integrity -s play.dhis2.org/demo -u admin -p district".format( Style.BRIGHT, Style.RESET_ALL) parser = argparse.ArgumentParser(usage=usage, description=description) parser.add_argument('-s', dest='server', action='store', help="DHIS2 server URL") parser.add_argument('-u', dest='username', action='store', help="DHIS2 username") parser.add_argument('-p', dest='password', action='store', help="DHIS2 password") parser.add_argument('-v', dest='api_version', action='store', required=False, type=int, help='DHIS2 API version e.g. -v=28') args = parser.parse_args() if not args.password: if not args.username: raise PKClientException( "ArgumentError: Must provide a username via argument -u") password = getpass.getpass( prompt="Password for {} @ {}: ".format(args.username, args.server)) else: password = args.password return args, password
def parse_args(): description = "{}Set Attribute Values sourced from CSV file.{}".format( Style.BRIGHT, Style.RESET_ALL) usage = """ {}Example:{} dhis2-pk-attribute-setter -s play.dhis2.org/dev -u admin -p district -c file.csv -t organisationUnits -a pt5Ll9bb2oP {}CSV file structure:{} uid | attributeValue ------|--------------- UID | myValue """.format(Style.BRIGHT, Style.RESET_ALL, Style.BRIGHT, Style.RESET_ALL) parser = argparse.ArgumentParser( usage=usage, description=description, formatter_class=argparse.RawTextHelpFormatter) parser._action_groups.pop() required = parser.add_argument_group('required arguments') required.add_argument( '-t', dest='object_type', action='store', required=True, help= "Object type to set attributeValues to: {organisationUnits, dataElements, ...}" ) required.add_argument('-c', dest='source_csv', action='store', required=True, help="Path to CSV file with Attribute Values") required.add_argument('-a', dest='attribute_uid', action='store', help='Attribute UID', required=True) optional = parser.add_argument_group('optional arguments') optional.add_argument('-s', dest='server', action='store', help="DHIS2 server URL") optional.add_argument('-u', dest='username', action='store', help="DHIS2 username") optional.add_argument('-p', dest='password', action='store', help="DHIS2 password") args = parser.parse_args() if args.object_type not in OBJ_TYPES: raise PKClientException( "argument -t must be a valid object_type - one of:\n{}".format( ', '.join(sorted(OBJ_TYPES)))) if not valid_uid(args.attribute_uid): raise PKClientException("Attribute {} is not a valid UID".format( args.attribute_uid)) return args
def validate_file(filename): if not os.path.exists(filename): raise PKClientException("File does not exist: {}".format(filename)) if not os.path.getsize(filename) > 0: raise PKClientException("File is empty: {}".format(filename))
def parse_args(): """Argument parsing""" description = "{}Share DHIS2 objects with userGroups via filters.{}".format( Style.BRIGHT, Style.RESET_ALL) usage = """ {}Example:{} dhis2-pk-share -s play.dhis2.org/dev -u admin -p district -f 'id:eq:P3jJH5Tu5VC' -t dataelement -a readonly -g 'name:like:Admin' readwrite -g 'name:like:Research' readwrite """.format(Style.BRIGHT, Style.RESET_ALL) parser = argparse.ArgumentParser( usage=usage, description=description, formatter_class=argparse.RawTextHelpFormatter) parser._action_groups.pop() required = parser.add_argument_group('required arguments') required.add_argument( '-t', dest='object_type', action='store', required=True, help="DHIS2 object type to apply sharing, e.g. -t sqlView") optional = parser.add_argument_group('optional arguments') optional.add_argument('-a', dest='public_access', action='append', required=False, nargs='+', metavar='PUBLICACCESS', choices=access.keys(), help=textwrap.dedent('''\ Public Access for all objects. Valid choices are: {{{}}} For setting DATA access, add second argument, e.g. -a readwrite readonly '''.format(', '.join(access.keys())))) optional.add_argument('-f', dest='filter', action='store', required=False, help=textwrap.dedent('''\ Filter on objects with DHIS2 field filter. To add multiple filters: - '&&' joins filters with AND - '||' joins filters with OR Example: -f 'name:like:ABC||code:eq:X' ''')) optional.add_argument('-g', dest='groups', action='append', required=False, metavar='USERGROUP', nargs='+', help=textwrap.dedent('''\ User Group to share objects with: FILTER METADATA [DATA] - FILTER: Filter all User Groups. See -f for filtering mechanism - METADATA: Metadata access for this User Group. {readwrite, none, readonly} - DATA: Data access for this User Group. {readwrite, none, readonly} Example: -g 'id:eq:OeFJOqprom6' readwrite none ''')) optional.add_argument( '-o', dest='overwrite', action='store_true', required=False, default=False, help= "Overwrite sharing - updates 'lastUpdated' field of all shared objects" ) optional.add_argument('-e', dest='extend', action='store_true', required=False, default=False, help="Extend existing sharing settings") optional.add_argument( '-l', dest='logging_to_file', action='store', required=False, metavar='FILEPATH', help="Path to Log file (default level: INFO, pass -d for DEBUG)") optional.add_argument('-v', dest='api_version', action='store', required=False, type=int, help='DHIS2 API version e.g. -v 28') optional.add_argument('-s', dest='server', action='store', metavar='URL', help="DHIS2 server URL") optional.add_argument('-u', dest='username', action='store', help='DHIS2 username, e.g. -u admin') optional.add_argument('-p', dest='password', action='store', help='DHIS2 password, e.g. -p district') optional.add_argument('-d', dest='debug', action='store_true', default=False, required=False, help="Debug flag") args = parser.parse_args() if not args.password: if not args.username: raise PKClientException( "ArgumentError: Must provide a username via argument -u") password = getpass.getpass( prompt="Password for {} @ {}: ".format(args.username, args.server)) else: password = args.password return args, password