def fetch(self, attr: str = None) -> None: """Fetch the latest object state from the I-Server. This will overwrite object attribute values by calling all REST API endpoints defining this object. """ functions = self._API_GETTERS if attr: functions = { k: v for k, v in self._API_GETTERS.items() if k == attr } functions = functions if functions else self._API_GETTERS for key, func in functions.items(): param_value_dict = helper.auto_match_args(func, self) response = func(**param_value_dict) if response.ok: response = response.json() response = helper.camel_to_snake(response) if type(response) == dict: for k, v in response.items(): k = key if key and len(response) == 1 else k # use keys and values to propagate AVAILABLE_ATTRIBUTES dict on runtime self._AVAILABLE_ATTRIBUTES.update({k: type(v)}) # Check if any attributes need mapping from defined ENUMs v = self._ENUM_MAP[k](v).name if self._ENUM_MAP.get( k) else v super().__setattr__(k, v) if type(response) == list: self._AVAILABLE_ATTRIBUTES.update({key: type(response)}) super().__setattr__(key, response)
def __fetch(self) -> None: """Retrieve object metadata.""" response = schedules.get_schedule(self.connection, self.id) if response.ok: response = response.json() response = helper.camel_to_snake(response) for key, value in response.items(): self._AVAILABLE_ATTRIBUTES.update({key: type(value)}) self.__setattr__(key, value)
def from_dict(cls, dictionary): """Initialize Delivery object from dictionary.""" obj = cls.__new__(cls) super(DeliveryDictable, obj).__init__() dictionary = camel_to_snake(dictionary) for key, value in dictionary.items(): if key == 'zip': obj.__setattr__(key, ZipSettings.from_dict(value)) else: obj.__setattr__(key, value) return obj
def from_dict(cls, source: Dict[str, Any], connection: Optional["Connection"] = None): source = camel_to_snake(source) predicate_id = source.get("predicate_id") source = source.get('predicate_tree') filter = FilterRef.from_dict(source.get("filter"), connection) is_independent = bool(source.get("is_independent")) return PredicateFilter(filter, is_independent, predicate_id)
def from_dict(cls, source, **kwargs): """Initialize Delivery object from dictionary.""" obj = cls.__new__(cls) super(DeliveryDictable, obj).__init__() source = camel_to_snake(source) for key, value in source.items(): if key == 'zip': setattr(obj, key, ZipSettings.from_dict(value)) else: setattr(obj, key, value) return obj
def from_dict(cls, source, **kwargs): """Initialize Delivery object from dictionary.""" obj = cls.__new__(cls) super(Delivery, obj).__init__() source = camel_to_snake(source) for key, value in source.items(): if not obj.change_mode( key, value): # Change mode or set attr if its not mode param setattr(obj, key, value) return obj
def to_dict(self, camel_case=True) -> dict: result = { 'name': self.name, 'id': self.id, 'physicalAddress': self.physical_address, 'deliveryType': self.delivery_type.value, 'deviceId': self.device.id, 'deviceName': self.device.name, 'isDefault': self.is_default } return result if camel_case else camel_to_snake(result)
def __fetch(self): """Retrieve object metadata.""" response = subscriptions.get_subscription(self.connection, self.id, self.application_id) if response.ok: response = response.json() response = helper.camel_to_snake(response) for key, value in response.items(): self._AVAILABLE_ATTRIBUTES.update({key: type(value)}) self.__setattr__(key, value) if key == "delivery": self._delivery = Delivery.from_dict(value)
def _list_loaded_projects(cls, connection: Connection, to_dictionary: bool = False, **filters) -> Union[List["Project"], List[dict]]: response = projects.get_projects(connection, whitelist=[('ERR014', 403)]) list_of_dicts = response.json() if response.ok else [] list_of_dicts = helper.camel_to_snake(list_of_dicts) # Convert keys raw_project = helper.filter_list_of_dicts(list_of_dicts, **filters) if to_dictionary: # return list of project names return raw_project else: # return list of Project objects return [cls.from_dict(source=obj, connection=connection) for obj in raw_project]
def _from_single_response(cls, connection: Connection, response): """Instantiate an object from response without calling any additional getters.""" obj = cls.__new__(cls) # Does not call __init__ super(EntityBase, obj).__init__() # call any polymorphic base class initializers super(EntityBase, obj).__setattr__("connection", connection) response = helper.camel_to_snake(response) if type(response) == dict: for key, value in response.items(): cls._AVAILABLE_ATTRIBUTES.update({key: type(value)}) value = cls._ENUM_MAP[key](value).name if cls._ENUM_MAP.get( key) else value super(EntityBase, obj).__setattr__(key, value) return obj
def list_schedules(self, **filters): """List all schedules. Args: **filters: Available filter parameters:['name':, 'id', 'description', 'scheduleType', 'scheduleNextDelivery',] """ # TODO add limit, and support for objects, to_datafram, to_dictionary response = schedules.list_schedules(self.connection) if response.ok: response = helper.camel_to_snake(response.json()["schedules"]) return helper.filter_list_of_dicts(response, **filters)
def from_dict(cls, source: Dict[str, Any], connection: Optional["Connection"] = None): source = camel_to_snake(source) predicate_id = source.get("predicate_id") source = source.get('predicate_tree') level = [ AttributeRef.from_dict(obj, connection) for obj in source.get("level") ] tuples = [[ AttributeElement.from_dict(elem, connection) for elem in obj ] for obj in source.get("tuples", [])] return PredicateJointElementList(predicate_id, level, tuples)
def from_dict(cls, source: Dict[str, Any], connection: Optional["Connection"] = None): source = camel_to_snake(source) predicate_id = source.get("predicate_id") source = source.get('predicate_tree') function = PredicateFormFunction(source.get("function")) parameters = [] for parameter in source.get("parameters", []): parameter_type = parameter.get("parameterType") if parameter_type == ParameterType.CONSTANT.value: parameters.append( ConstantParameter.from_dict(parameter, connection)) elif parameter_type == ParameterType.OBJECT_REFERENCE.value: parameters.append( ObjectReferenceParameter.from_dict(parameter, connection)) elif parameter_type == ParameterType.EXPRESSION.value: parameters.append( ExpressionParameter.from_dict(parameter, connection)) elif parameter_type == ParameterType.PROMPT.value: parameters.append( PromptParameter.from_dict(parameter, connection)) elif parameter_type == ParameterType.DYNAMIC_DATE_TIME.value: parameters.append( DynamicDateTimeParameter.from_dict(parameter, connection)) elif parameter_type == ParameterType.ARRAY.value: parameters.append( ConstantArrayParameter.from_dict(parameter, connection)) attribute = AttributeRef.from_dict(source.get("attribute"), connection) form = source.get("form", {}) form_sub_type = form.get("sub_type") if form_sub_type == "attribute_form_system": form = AttributeFormSystemRef.from_dict(source.get("form"), connection) elif form_sub_type == "attribute_form_normal": form = AttributeFormNormalRef.from_dict(source.get("form"), connection) else: form = None data_locale = source.get("data_locale") return PredicateForm(function, parameters, attribute, form, data_locale, predicate_id)
def _list_loaded_applications(cls, connection: "Connection", to_dictionary: bool = False, **filters) -> List["Application"]: response = projects.get_projects(connection, whitelist=[('ERR014', 403)]) list_of_dicts = response.json() if response.ok else [] list_of_dicts = helper.camel_to_snake(list_of_dicts) # Convert keys raw_applications = helper.filter_list_of_dicts(list_of_dicts, **filters) if to_dictionary: # return list of application names return raw_applications else: # return list of Application objects return cls._from_bulk_response(connection, raw_applications)
def _update_nested_properties(self, objects, path: str, op: str) -> None: body = { "operationList": [{ "op": op, "path": '/{}'.format(path), "value": objects }] } response = security.update_security_role(self.connection, self.id, body) response = response.json() if type(response) == dict: response = helper.camel_to_snake(response) for key, value in response.items(): super(Entity, self).__setattr__(key, value)
def from_dict(cls, connection, dictionary, application_id=None, application_name=None): """Initialize Subscription object from dictionary.""" obj = cls.__new__(cls) super(Subscription, obj).__init__() obj.connection = connection obj.application_id = Subscription._app_id_check( connection, application_id, application_name) dictionary = helper.camel_to_snake(dictionary) for key, value in dictionary.items(): obj._AVAILABLE_ATTRIBUTES.update({key: type(value)}) obj.__setattr__(key, value) if key == 'delivery': obj._delivery = Delivery.from_dict(value) return obj
def from_dict(cls, source: Dict[str, Any], connection: Optional["Connection"] = None): source = camel_to_snake(source) predicate_id = source.get("predicate_id") source = source.get('predicate_tree') function = PredicateElementListFunction(source.get("function")) attribute = AttributeRef.from_dict(source.get("attribute"), connection) elements = [ AttributeElement.from_dict(elem, connection) for elem in source.get("elements") ] elements_prompt = None if source.get("elements_prompt") is None else [ ElementPromptRef.from_dict(elem, connection) for elem in source.get("elements_prompt") ] return PredicateElementList(function, attribute, elements, elements_prompt, predicate_id)
def _list_security_roles( cls, connection: "Connection", to_dictionary: bool = False, to_dataframe: bool = False, **filters ) -> Union[List["SecurityRole"], List[Dict[str, Any]], DataFrame]: if to_dictionary and to_dataframe: helper.exception_handler( "Please select either to_dictionary=True or to_dataframe=True, but not both.", ValueError) response = security.get_security_roles(connection=connection).json() response = helper.camel_to_snake(response) if filters: response = helper.filter_list_of_dicts(response, **filters) if to_dictionary: return response elif to_dataframe: return DataFrame(response) else: return cls._from_bulk_response(connection, response)
def __save_info(self, cube_cache_dict): cube_cache_dict = camel_to_snake(cube_cache_dict) self._project_id = cube_cache_dict.get('project_id', None) self._source = cube_cache_dict.get('source', None) self._state = cube_cache_dict.get('state', None) self._last_update_time = cube_cache_dict.get('last_update_time', None) self._last_hit_time = cube_cache_dict.get('last_hit_time', None) self._hit_count = cube_cache_dict.get('hit_count', None) self._size = cube_cache_dict.get('size', None) self._creator_name = cube_cache_dict.get('creator_name', None) self._creator_id = cube_cache_dict.get('creator_id', None) self._last_update_job = cube_cache_dict.get('last_update_job', None) self._open_view_count = cube_cache_dict.get('open_view_count', None) self._creation_time = cube_cache_dict.get('creation_time', None) self._historic_hit_count = cube_cache_dict.get('historic_hit_count', None) self._database_connections = cube_cache_dict.get('database_connections', []) self._file_name = cube_cache_dict.get('file_name', None) self._data_languages = cube_cache_dict.get('data_languages', []) self._row_count = cube_cache_dict.get('row_count', None) self._column_count = cube_cache_dict.get('column_count', None) self._job_execution_statistics = cube_cache_dict.get('job_execution_statistics', None)
def from_dict(cls, source: Dict[str, Any], connection: Optional["Connection"] = None): source = camel_to_snake(source) function = LogicFunction(source.get("function")) children = [] for child in source.get("children"): child_type = child.get("type") if child_type == "predicate_form_qualification": children.append(PredicateForm.from_dict(child, connection)) elif child_type == "predicate_joint_element_list": children.append( PredicateJointElementList.from_dict(child, connection)) elif child_type == "predicate_filter_qualification": children.append(PredicateFilter.from_dict(child, connection)) elif child_type == "predicate_element_list": children.append( PredicateElementList.from_dict(child, connection)) elif child_type == "operator": children.append(LogicOperator.from_dict(child, connection)) return LogicOperator(function, children)
def from_dict(cls, dictionary): """Initialize Delivery object from dictionary.""" obj = cls.__new__(cls) super(Delivery, obj).__init__() dictionary = camel_to_snake(dictionary) for key, value in dictionary.items(): if key == 'email': obj.__setattr__(key, cls.Email.from_dict(value)) elif key == 'file': obj.__setattr__(key, cls.File.from_dict(value)) elif key == 'mobile': obj.__setattr__(key, cls.Mobile.from_dict(value)) elif key == 'ftp': obj.__setattr__(key, cls.Ftp.from_dict(value)) elif key == 'cache': obj.__setattr__(key, cls.Cache.from_dict(value)) elif key == 'history_list': obj.__setattr__(key, cls.HistoryList.from_dict(value)) else: obj.__setattr__(key, value) return obj
def list_privileges(cls, connection, to_dictionary=False, to_dataframe=False, **filters): """Get list of privilege objects or privilege dicts. Optionally, use the `to_dataframe` parameter to display privileges in a `DataFrame`. Filter the privileges by specifying the `filters` keyword arguments. Args: connection: MicroStrategy connection object returned by `connection.Connection()`. to_dictionary: If `True` returns dict, by default (False) returns User objects. to_dataframe: If `True`, returns `DataFrame`. **filters: Available filter parameters: ['id', 'name', 'description', 'categories', 'is_project_level_privilege'] Examples: >>> Privilege.list_privileges(connection, to_dataframe=True, is_project_level_privilege='True', id=[1,2,3,4,5]) """ if to_dictionary and to_dataframe: helper.exception_handler( "Please select either `to_dictionary=True` or `to_dataframe=True`, but not both.", ValueError) response = security.get_privileges(connection).json() response = helper.camel_to_snake(response) if filters: response = helper.filter_list_of_dicts(response, **filters) if to_dictionary: return response elif to_dataframe: return pd.DataFrame(response) else: return [cls._from_single_response(connection, r) for r in response]
def _alter_properties(self, **properties): """Generic alter method that has to be implemented in child classes where arguments will be specified.""" body = {} func = self._API_PATCH[0] properties = helper.snake_to_camel(properties) if func == objects.update_object: # Update using the generic update_object() for property, value in properties.items(): body[property] = self._validate_type(property, value) else: # Update using different update method, if one was specified body = {"operationList": []} for property, value in properties.items(): body['operationList'].append({ "op": "replace", "path": "/{}".format(property), "value": self._validate_type(property, value) }) # send patch request from the specified update wrapper param_value_dict = helper.auto_match_args(func, self) param_value_dict['body'] = body response = func(**param_value_dict) if response.ok: if config.verbose: print("{} '{}' has been modified.".format( type(self).__name__, self.name)) response = response.json() if type(response) == dict: response = helper.camel_to_snake(response) for key, value in response.items(): super().__setattr__(key, value)
def alter( self, # NOSONAR name: Optional[str] = None, allow_delivery_changes: Optional[bool] = None, allow_personalization_changes: Optional[bool] = None, allow_unsubscribe: Optional[bool] = None, send_now: bool = False, owner_id: Optional[str] = None, schedules: Optional[Union[str, List[str], Schedule, List[Schedule]]] = None, contents: Optional[Content] = None, recipients: Optional[Union[List[str], List[dict]]] = None, delivery: Optional[Union[Delivery, dict]] = None, delivery_mode: Optional[str] = None, custom_msg: Optional[str] = None, delivery_expiration_date: Optional[str] = None, contact_security: Optional[bool] = None, filename: Optional[str] = None, compress: Optional[bool] = None, space_delimiter: Optional[str] = None, email_subject: Optional[str] = None, email_message: Optional[str] = None, email_send_content_as: Optional[str] = None, overwrite_older_version: Optional[bool] = None, zip_filename: Optional[str] = None, zip_password_protect: Optional[bool] = None, zip_password: Optional[str] = None, file_burst_sub_folder: Optional[str] = None, printer_copies: Optional[int] = None, printer_range_start: Optional[int] = None, printer_range_end: Optional[int] = None, printer_collated: Optional[bool] = None, printer_orientation: Optional[str] = None, printer_use_print_range: Optional[bool] = None, cache_cache_type: Optional[str] = None, cache_shortcut_cache_format: Optional[str] = None, mobile_client_type: Optional[str] = None, device_id: Optional[str] = None, do_not_create_update_caches: Optional[bool] = None, re_run_hl: Optional[bool] = None, cache_library_cache_types: List[Union[LibraryCacheTypes, str]] = [ LibraryCacheTypes.WEB ], cache_reuse_dataset_cache: bool = False, cache_is_all_library_users: bool = False, delivery_notification_enabled: bool = False, delivery_personal_notification_address_id: Optional[str] = None): """ Alter subscription. Args: connection(Connection): a MicroStrategy connection object name(str): name of the subscription, project_id(str): project ID, allow_delivery_changes(bool): whether the recipients can change the delivery of the subscription, allow_personalization_changes(bool): whether the recipients can personalize the subscription, allow_unsubscribe(bool): whether the recipients can unsubscribe from the subscription, send_now(bool): indicates whether to execute the subscription immediately, owner_id(str): ID of the subscription owner, by default logged in user ID, schedules (Union[str, List[str], Schedule, List[Schedule]]): Schedules IDs or Schedule objects, contents (Content): The content of the subscription. recipients (Union[List[str], List[dict]]): list of recipients IDs or dicts, delivery_mode(str, enum): the subscription delivery mode [EMAIL, FILE, PRINTER, HISTORY_LIST, CACHE, MOBILE, FTP, SNAPSHOT, PERSONAL_VIEW, SHARED_LINK, UNSUPPORTED], delivery_expiration_date(str): expiration date of the subscription, format should be yyyy-MM-dd, contact_security(bool): whether to use contact security for each contact group member, filename(str): the filename that will be delivered when the subscription is executed, compress(bool): whether to compress the file space_delimiter(str): space delimiter, email_subject(str): email subject associated with the subscription, email_message(str): email body of subscription, email_send_content_as(str,enum): [data, data_and_history_list, data_and_link_and_history_list, link_and_history_list], overwrite_older_version(bool): whether the current subscription will overwrite earlier versions of the same report or document in the history list, zip_filename(str): filename of the compressed content, zip_password_protect(bool): whether to password protect zip file, zip_password(str): optional password for the compressed file file_burst_sub_folder(str): burst sub folder, printer_copies(int): the number of copies that should be printed, printer_range_start(int): the number indicating the first report page that should be printed, printer_range_end(int): the number indicating the last report page that should be printed, printer_collated(bool): whether the printing should be collated, printer_orientation(str,enum): [ PORTRAIT, LANDSCAPE ] printer_use_print_range(bool): whether print range should be used, cache_cache_type(str,enum): [RESERVED, SHORTCUT, SHORTCUTWITHBOOKMARK] cache_shortcut_cache_format(str,enum): [RESERVED, JSON, BINARY, BOTH] mobile_client_type(str,enum): [RESERVED, BLACKBERRY, PHONE, TABLET, ANDROID] device_id(str): the mobile target project, do_not_create_update_caches(bool): whether the current subscription will overwrite earlier versions of the same report or document in the history list, re_run_hl(bool): whether subscription will re-run against warehouse cache_library_cache_types: Set of library cache types, available types can be web, android, ios cache_reuse_dataset_cache: Whether to reuse dataset cache cache_is_all_library_users: Whether for all library users delivery_notification_enabled: Whether notification is enabled, notification applies to cache delivery_personal_notification_address_id: Notification details """ # Schedules logic schedules = self.__validate_schedules(schedules=schedules) if not schedules: schedules = [{'id': sch.id} for sch in self.schedules] # Content logic if contents: contents = self.__validate_contents(contents) else: contents = [cont.to_dict() for cont in self.contents] # Delivery logic if delivery: temp_delivery = (Delivery.from_dict(delivery) if isinstance( delivery, dict) else delivery) else: temp_delivery = self.__change_delivery_properties( delivery_mode, delivery_expiration_date, contact_security, email_subject, email_message, filename, compress, None, zip_filename, zip_password, zip_password_protect, space_delimiter, email_send_content_as, overwrite_older_version, file_burst_sub_folder, printer_copies, printer_range_start, printer_range_end, printer_collated, printer_orientation, printer_use_print_range, client_type=mobile_client_type, device_id=device_id, do_not_create_update_caches=do_not_create_update_caches, re_run_hl=re_run_hl, cache_type=cache_cache_type, shortcut_cache_format=cache_shortcut_cache_format, library_cache_types=cache_library_cache_types, reuse_dataset_cache=cache_reuse_dataset_cache, is_all_library_users=cache_is_all_library_users, notification_enabled=delivery_notification_enabled, personal_notification_address_id= delivery_personal_notification_address_id) delivery = temp_delivery.to_dict(camel_case=True) # Recipients logic recipients = self.__is_val_changed(recipients=recipients) recipients = Subscription._validate_recipients(self.connection, contents, recipients, self.project_id, delivery['mode']) body = { "name": self.__is_val_changed(name=name), "allowDeliveryChanges": self.__is_val_changed( allow_delivery_changes=allow_delivery_changes), "allowPersonalizationChanges": self.__is_val_changed( allow_personalization_changes=allow_personalization_changes), "allowUnsubscribe": self.__is_val_changed(allow_unsubscribe=allow_unsubscribe), "sendNow": send_now, 'owner': { 'id': self.__is_val_changed(nested=self.owner.id, owner_id=owner_id) }, "schedules": schedules, "contents": contents, "recipients": recipients, "delivery": delivery, } body = helper.delete_none_values(body) response = subscriptions.update_subscription(self.connection, self.id, self.project_id, body) if response.ok: response = response.json() response = helper.camel_to_snake(response) self._set_object_attributes(**response) if config.verbose: msg = f"Updated subscription '{self.name}' with ID: {self.id}." msg = custom_msg if custom_msg else msg logger.info(msg)
def create(cls, connection: Connection, project: Union[Project, str], user: Union[User, str], ds_connection: Union[DatasourceConnection, str], datasource: Union[DatasourceInstance, str], login: Union[DatasourceLogin, str], locale: Optional[Locale] = None, locale_id: Optional[str] = None, locale_name: Optional[str] = None) -> "DatasourceMap": """Create a new Datasource Map object on the server. If more than one locale related parameters are provided, `locale` has priority, then `locale_id`. Args: connection: A MicroStrategy connection object project: The project the Map is to be assigned to user: The User to be mapped ds_connection: The Datasource Connection to be mapped datasource: The Datasource Instance to be mapped login: The Datasource Login to be mapped locale: The locale to be mapped. locale_id: The id of locale to be mapped. locale_name: The name of locale to be mapped. Returns: DatasourceMap object """ project_id = get_objects_id(project, Project) user_id = get_objects_id(user, User) connection_id = get_objects_id(ds_connection, DatasourceConnection) datasource_id = get_objects_id(datasource, DatasourceInstance) login_id = get_objects_id(login, DatasourceLogin) body = { "projectId": project_id, "user": { "id": user_id }, "connection": { "id": connection_id }, "datasource": { "id": datasource_id }, "login": { "id": login_id }, } if locale and isinstance(locale, Locale): body["locale"] = locale.to_dict() elif locale_id and isinstance(locale_id, str): body["locale"] = {"id": locale} elif locale_name and isinstance(locale_name, str): body["locale"] = {"name": locale} jresponse = datasources.create_datasource_mapping( connection=connection, body=body).json() if config.verbose: logger.info( f"Successfully created datasource connection map with ID: '{jresponse.get('id')}'" ) return cls.from_dict(source=helper.camel_to_snake(jresponse), connection=connection)
def alter( self, name: str = None, allow_delivery_changes: bool = None, allow_personalization_changes: bool = None, allow_unsubscribe: bool = None, send_now: bool = False, owner_id: str = None, schedules_ids: Union[str, List[str]] = None, contents: Content = None, recipients: Union[List[str], List[dict]] = None, delivery: Union[Delivery, dict] = None, delivery_mode: str = None, custom_msg=None, delivery_expiration_date: str = None, contact_security: bool = None, filename: str = None, compress: bool = None, space_delimiter: str = None, email_subject: str = None, email_message: str = None, email_send_content_as: str = None, overwrite_older_version: bool = None, zip_filename: str = None, zip_password_protect: bool = None, zip_password: str = None, file_burst_sub_folder: str = None, printer_copies: int = None, printer_range_start: int = None, printer_range_end: int = None, printer_collated: bool = None, printer_orientation: str = None, printer_use_print_range: bool = None, cache_type: str = None, shortcut_cache_format: str = None, mobile_client_type: str = None, device_id: str = None, do_not_create_update_caches: bool = None, re_run_hl: bool = None, ): """Alter subscription. Args: connection(Connection): a MicroStrategy connection object name(str): name of the subscription, application_id(str): application ID, allow_delivery_changes(bool): whether the recipients can change the delivery of the subscription, allow_personalization_changes(bool): whether the recipients can personalize the subscription, allow_unsubscribe(bool): whether the recipients can unsubscribe from the subscription, send_now(bool): indicates whether to execute the subscription immediately, owner_id(str): ID of the subscription owner, by default logged in user ID, schedules_ids (Union[str, List[str]]) = Schedules IDs, contents (Content): The content of the subscription. recipients (Union[List[str], List[dict]]): list of recipients IDs or dicts, delivery_mode(str, enum): the subscription delivery mode [EMAIL, FILE, PRINTER, HISTORY_LIST, CACHE, MOBILE, FTP, SNAPSHOT, PERSONAL_VIEW, SHARED_LINK, UNSUPPORTED], delivery_expiration_date(str): expiration date of the subscription, format should be yyyy-MM-dd, contact_security(bool): whether to use contact security for each contact group member, filename(str): the filename that will be delivered when the subscription is executed, compress(bool): whether to compress the file space_delimiter(str): space delimiter, email_subject(str): email subject associated with the subscription, email_message(str): email body of subscription, email_send_content_as(str,enum): [data, data_and_history_list, data_and_link_and_history_list, link_and_history_list], overwrite_older_version(bool): whether the current subscription will overwrite earlier versions of the same report or document in the history list, zip_filename(str): filename of the compressed content, zip_password_protect(bool): whether to password protect zip file, zip_password(str): optional password for the compressed file file_burst_sub_folder(str): burst sub folder, printer_copies(int): the number of copies that should be printed, printer_range_start(int): the number indicating the first report page that should be printed, printer_range_end(int): the number indicating the last report page that should be printed, printer_collated(bool): whether the printing should be collated, printer_orientation(str,enum): [ PORTRAIT, LANDSCAPE ] printer_use_print_range(bool): whether print range should be used, cache_type(str,enum): [RESERVED, SHORTCUT, BOOKMARK, SHORTCUTWITHBOOKMARK] shortcut_cache_format(str,enum): [RESERVED, JSON, BINARY, BOTH] mobile_client_type(str,enum): [RESERVED, BLACKBERRY, PHONE, TABLET, ANDROID] device_id(str): the mobile target application, do_not_create_update_caches(bool): whether the current subscription will overwrite earlier versions of the same report or document in the history list, re_run_hl(bool): whether subscription will re-run against warehouse """ def validate(body): for key, value in body.items(): if key == 'send_now': pass elif type(value) is not self._AVAILABLE_ATTRIBUTES.get(key): helper.exception_handler( "{} is not a valid type of {}, valid type is {}". format(type(value), key, self._AVAILABLE_ATTRIBUTES.get(key)), TypeError) def is_changed(nested=None, **kwargs): for key, value in kwargs.items(): if nested: return value if value != nested and value is not None else nested else: current_val = self.__dict__.get(key) # if not current_val: we need to get return value if value != current_val and value is not None else current_val # Schedules logic schedules_ids = schedules_ids if isinstance(schedules_ids, list) else [schedules_ids] schedules_ids = [s for s in schedules_ids if s is not None] schedules = [{ 'id': sch_id } for sch_id in schedules_ids] if schedules_ids else [{ 'id': trig['id'] } for trig in self.schedules] # Content logic if contents: contents = contents if isinstance(contents, list) else [contents] content_type_msg = "Contents must be dictionaries or Content objects." contents = [ content.to_dict( camel_case=True) if isinstance(content, Content) else content if isinstance(content, dict) else helper.exception_handler(content_type_msg, TypeError) for content in contents ] else: contents = self.contents # Delivery logic if delivery: temp_delivery = Delivery.from_dict(delivery) if isinstance( delivery, dict) else delivery else: temp_delivery = self.__change_delivery_properties( delivery_mode, delivery_expiration_date, contact_security, email_subject, email_message, filename, compress, None, zip_password, zip_password_protect, space_delimiter, email_send_content_as, overwrite_older_version, file_burst_sub_folder, printer_copies, printer_range_start, printer_range_end, printer_collated, printer_orientation, printer_use_print_range, cache_type, shortcut_cache_format, mobile_client_type, device_id, do_not_create_update_caches, re_run_hl) delivery = temp_delivery.to_dict(camel_case=True) # Recipients logic recipients = is_changed(recipients=recipients) recipients = Subscription._validate_recipients(self.connection, contents, recipients, self.application_id, delivery['mode']) body = { "name": is_changed(name=name), "allowDeliveryChanges": is_changed(allow_delivery_changes=allow_delivery_changes), "allowPersonalizationChanges": is_changed( allow_personalization_changes=allow_personalization_changes), "allowUnsubscribe": is_changed(allow_unsubscribe=allow_unsubscribe), "sendNow": send_now, 'owner': { 'id': is_changed(nested=self.owner['id'], owner_id=owner_id) }, "schedules": schedules, "contents": contents, "recipients": recipients, "delivery": delivery, } validate(helper.camel_to_snake(body)) body = helper.delete_none_values(body) response = subscriptions.update_subscription(self.connection, self.id, self.application_id, body) if response.ok: response = response.json() response = helper.camel_to_snake(response) for key, value in response.items(): self.__setattr__(key, value) if config.verbose: print(custom_msg if custom_msg else "Updated subscription '{}' with ID: {}.". format(self.name, self.id))