def test_make_assembled_entry_for_widget_should_make_entry_and_tags(self): widget = self.__make_fake_widget() widget_tag_template = datacatalog.TagTemplate() widget_tag_template.name = 'tagTemplates/sisense_widget_metadata' jaql_tag_template = datacatalog.TagTemplate() jaql_tag_template.name = 'tagTemplates/sisense_jaql_metadata' tag_templates_dict = { 'sisense_widget_metadata': widget_tag_template, 'sisense_jaql_metadata': jaql_tag_template } fake_entry = ('test-widget', {}) entry_factory = self.__mock_entry_factory entry_factory.make_entry_for_widget.return_value = fake_entry tag_factory = self.__mock_tag_factory fake_widget_tag = datacatalog.Tag() fake_widget_tag.template = 'tagTemplates/sisense_widget_metadata' tag_factory.make_tag_for_widget.return_value = fake_widget_tag fake_field_tag = datacatalog.Tag() fake_field_tag.template = 'tagTemplates/sisense_jaql_metadata' tag_factory.make_tags_for_widget_fields.return_value = [fake_field_tag] fake_filter_tag = datacatalog.Tag() fake_filter_tag.template = 'tagTemplates/sisense_jaql_metadata' tag_factory.make_tags_for_widget_filters.return_value = [ fake_filter_tag ] assembled_entry = self.__factory\ ._AssembledEntryFactory__make_assembled_entry_for_widget( widget, tag_templates_dict) self.assertEqual('test-widget', assembled_entry.entry_id) self.assertEqual({}, assembled_entry.entry) entry_factory.make_entry_for_widget.assert_called_once_with(widget) tags = assembled_entry.tags self.assertEqual(3, len(tags)) self.assertEqual('tagTemplates/sisense_widget_metadata', tags[0].template) self.assertEqual('tagTemplates/sisense_jaql_metadata', tags[1].template) self.assertEqual('tagTemplates/sisense_jaql_metadata', tags[2].template) tag_factory.make_tag_for_widget.assert_called_once_with( widget_tag_template, widget) tag_factory.make_tags_for_widget_fields.assert_called_once_with( jaql_tag_template, widget) tag_factory.make_tags_for_widget_filters.assert_called_once_with( jaql_tag_template, widget)
def make_tag_for_folder(self, tag_template, folder): tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', folder.id) self._set_string_field(tag, 'name', folder.name) self._set_string_field(tag, 'parent_id', folder.parent_id) self._set_bool_field(tag, 'has_children', folder.child_count > 0) self._set_double_field(tag, 'children_count', folder.child_count) has_dashboards = hasattr(folder, 'dashboards') if has_dashboards: has_dashboards = True if folder.dashboards else False self._set_bool_field(tag, 'has_dashboards', has_dashboards) dashboards_count = len(folder.dashboards) if has_dashboards else 0 self._set_double_field(tag, 'dashboards_count', dashboards_count) has_looks = hasattr(folder, 'looks') if has_looks: has_looks = True if folder.looks else False self._set_bool_field(tag, 'has_looks', has_looks) looks_count = len(folder.looks) if has_looks else 0 self._set_double_field(tag, 'looks_count', looks_count) self._set_string_field(tag, 'instance_url', self.__instance_url) return tag
def make_tag_for_workbook(cls, tag_template, workbook_metadata): tag = datacatalog.Tag() tag.template = tag_template.name cls._set_string_field(tag, 'luid', workbook_metadata.get('luid')) site = workbook_metadata.get('site') if site: cls._set_string_field(tag, 'site_name', site.get('name')) cls._set_string_field(tag, 'project_name', workbook_metadata.get('projectName')) owner = workbook_metadata.get('owner') if owner: cls._set_string_field(tag, 'owner_username', owner.get('username')) cls._set_string_field(tag, 'owner_name', owner.get('name')) upstream_tables = cls.make_upstream_tables_field_value( workbook_metadata) if upstream_tables: cls._set_string_field( tag, 'upstream_table_definition', 'DATABASE NAME (CONNECTION TYPE) / TABLE NAME') cls._set_string_field(tag, 'upstream_tables', ', '.join(upstream_tables)) return tag
def make_tag_for_measure(self, tag_template, measure_metadata): tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', measure_metadata.get('qInfo').get('qId')) q_measure = measure_metadata.get('qMeasure') self._set_string_field(tag, 'expression', q_measure.get('qDef')) self._set_string_field(tag, 'label_expression', q_measure.get('qLabelExpression')) self._set_bool_field(tag, 'is_custom_formatted', q_measure.get('isCustomFormatted')) q_meta_def = measure_metadata.get('qMetaDef') self._set_string_field(tag, 'tags', ', '.join(q_meta_def.get('tags'))) app_metadata = measure_metadata.get('app') if app_metadata: self._set_string_field(tag, 'app_id', app_metadata.get('id')) self._set_string_field(tag, 'app_name', app_metadata.get('name')) self._set_string_field(tag, 'site_url', self.__site_url) return tag
def make_tag_for_entity(self, entity, entity_types_dict, enum_types_dict): tag = datacatalog.Tag() guid = entity['guid'] data = entity['data'] entity_type_name = data['typeName'] entity_type = entity_types_dict[entity_type_name] formatted_name = attr_normalizer.DataCatalogAttributeNormalizer.\ format_name(entity_type_name) tag_template_id = attr_normalizer.DataCatalogAttributeNormalizer.\ create_tag_template_id(formatted_name, constant.ENTITY_TYPE_PREFIX, entity_type['data']['version']) tag.template = datacatalog.DataCatalogClient.tag_template_path( project=self.__project_id, location=self.__location_id, tag_template=tag_template_id) attributes = data['attributes'] super()._set_bool_field(tag, formatted_name, True) super()._set_string_field(tag, constant.ENTITY_GUID, guid) super()._set_string_field(tag, constant.INSTANCE_URL_FIELD, self.__instance_url) attribute_defs = entity_type['data']['attributeDefs'] self.__add_fields_from_attributes(tag, attributes, attribute_defs, enum_types_dict) self.__create_custom_fields_for_entity_type(tag, entity_type_name, attributes) return tag
def make_tag_for_dimension(self, tag_template, dimension_metadata): tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', dimension_metadata.get('qInfo').get('qId')) q_dim = dimension_metadata.get('qDim') grouping = self.__QLIK_TO_DC_DIM_GROUPING_MAPPING.get( q_dim.get('qGrouping')) self.__set_enum_field(tag, 'grouping', grouping) self._set_string_field(tag, 'fields', ', '.join(q_dim.get('qFieldDefs'))) self._set_string_field(tag, 'field_labels', ', '.join(q_dim.get('qFieldLabels'))) q_meta_def = dimension_metadata.get('qMetaDef') self._set_string_field(tag, 'tags', ', '.join(q_meta_def.get('tags'))) app_metadata = dimension_metadata.get('app') if app_metadata: self._set_string_field(tag, 'app_id', app_metadata.get('id')) self._set_string_field(tag, 'app_name', app_metadata.get('name')) self._set_string_field(tag, 'site_url', self.__site_url) return tag
def make_tag_for_table_container_metadata(self, tag_template, table_container): """ Create Tags for the Table Container technical fields that aren't support yet by Datacatalog api. :param tag_template: A datacatalog.TagTemplate :param table_container: A dict with metadata from the table_container :return: tag """ tag = datacatalog.Tag() tag.template = tag_template.name tables = table_container.get('tables') num_tables = 0 if tables: num_tables = len(tables) self._set_double_field(tag, 'num_tables', num_tables) self.__add_creator_value_to_tag( self.__metadata_definition['table_container_def']['type'], table_container, tag) self.__add_owner_value_to_tag( self.__metadata_definition['table_container_def']['type'], table_container, tag) self.__add_update_user_value_to_tag( self.__metadata_definition['table_container_def']['type'], table_container, tag) return tag
def __create_tag(cls): tag = datacatalog.Tag() tag.name = 'tag_template' tag.template = 'template' bool_field = datacatalog.TagField() bool_field.bool_value = True tag.fields['bool-field'] = bool_field double_field = datacatalog.TagField() double_field.double_value = 1 tag.fields['double-field'] = double_field string_field = datacatalog.TagField() string_field.string_value = 'Test String Value' tag.fields['string-field'] = string_field timestamp = timestamp_pb2.Timestamp() timestamp.FromJsonString('2019-09-06T11:00:00-03:00') timestamp_field = datacatalog.TagField() timestamp_field.timestamp_value = timestamp tag.fields['timestamp-field'] = timestamp_field enum_field = datacatalog.TagField() enum_field.enum_value.display_name = 'Test ENUM Value' tag.fields['enum-field'] = enum_field return tag
def make_tag_for_custom_property_definition(self, tag_template, custom_property_def_metadata): tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', custom_property_def_metadata.get('id')) self._set_string_field( tag, 'modified_by_username', custom_property_def_metadata.get('modifiedByUserName')) self._set_string_field(tag, 'value_type', custom_property_def_metadata.get('valueType')) choice_values = custom_property_def_metadata.get('choiceValues') or [] self._set_string_field(tag, 'choice_values', ', '.join(choice_values)) object_types = custom_property_def_metadata.get('objectTypes') or [] self._set_string_field(tag, 'object_types', ', '.join(object_types)) self._set_string_field(tag, 'site_url', self.__site_url) return tag
def make_tag_for_widget(self, tag_template: TagTemplate, widget_metadata: Dict[str, Any]) -> Tag: tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', widget_metadata.get('oid')) self._set_string_field(tag, 'type', widget_metadata.get('type')) self._set_string_field(tag, 'subtype', widget_metadata.get('subtype')) owner = widget_metadata.get('ownerData') if owner: self._set_string_field(tag, 'owner_username', owner.get('userName')) first_name = owner.get('firstName') or '' last_name = owner.get('lastName') or '' self._set_string_field(tag, 'owner_name', f'{first_name} {last_name}') dashboard = widget_metadata.get('dashboardData') self._set_string_field(tag, 'dashboard_id', dashboard.get('oid')) self._set_string_field(tag, 'dashboard_title', dashboard.get('title')) datasource = widget_metadata.get('datasource') if isinstance(datasource, dict): self._set_string_field(tag, 'datasource', datasource.get('title')) elif isinstance(datasource, str): self._set_string_field(tag, 'datasource', datasource) self._set_string_field(tag, 'server_url', self.__server_address) return tag
def test_make_tag_for_jaql_filter_by_should_return_tag( self, mock_make_tags_for_jaql): tag_template = datacatalog.TagTemplate() tag_template.name = 'tagTemplates/sisense_jaql_metadata' metadata = { 'filter': { 'top': 10, 'by': { 'dim': '[table_a.column_a]', 'title': 'Total Revenue', } }, 'title': 'TEST', } fake_tag = datacatalog.Tag() mock_make_tags_for_jaql.return_value = [fake_tag] tag = self.__factory \ ._DataCatalogTagFactory__make_tag_for_jaql_filter_by( tag_template, metadata, 'JAQL Filter By test') self.assertEqual(fake_tag, tag) mock_make_tags_for_jaql.assert_called_once_with( tag_template, { 'dim': '[table_a.column_a]', 'title': 'Total Revenue', }, 'JAQL Filter By test.filterBy')
def make_tag_for_dashboard_element(self, tag_template, element, dashboard): tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', element.id) self._set_string_field(tag, 'type', element.type) self._set_string_field(tag, 'dashboard_id', element.dashboard_id) if dashboard: self._set_string_field(tag, 'dashboard_title', dashboard.title) self._set_string_field(tag, 'look_id', element.look_id) if element.look: self._set_string_field(tag, 'look_title', element.look.title) self._set_string_field(tag, 'lookml_link_id', element.lookml_link_id) if element.query_id: self._set_double_field(tag, 'query_id', element.query_id) elif element.result_maker: self._set_double_field(tag, 'query_id', element.result_maker.query_id) self._set_string_field(tag, 'instance_url', self.__instance_url) return tag
def test_set_string_field_should_truncate_uft8_bytes_size_mixed(self): """ - Input string: 1990 'a' chars + 10 'ã' chars; - Expected field value: '1990 "a" chars + 3 "ã" chars + ...' since each 'ã' char needs 2 bytes, 'a' chars and periods need 1 byte when encoded in UTF-8. """ tag = datacatalog.Tag() str_value = u'' for _ in range(10): str_value += u'ã' prepare.BaseTagFactory._set_string_field( tag, 'string', u'{}{}'.format('a' * 1990, str_value)) str_value = u'' for _ in range(3): str_value += u'ã' self.assertEqual(1996, len(tag.fields['string'].string_value)) self.assertEqual(u'{}{}...'.format('a' * 1990, str_value), tag.fields['string'].string_value) self.assertEqual( 1999, len(tag.fields['string'].string_value.encode('UTF-8')))
def test_make_assembled_entry_for_folder_should_make_entry_and_tags(self): folder = self.__make_fake_folder() tag_template = datacatalog.TagTemplate() tag_template.name = 'tagTemplates/sisense_folder_metadata' tag_templates_dict = {'sisense_folder_metadata': tag_template} fake_entry = ('test-folder', {}) entry_factory = self.__mock_entry_factory entry_factory.make_entry_for_folder.return_value = fake_entry fake_tag = datacatalog.Tag() fake_tag.template = 'tagTemplates/sisense_folder_metadata' tag_factory = self.__mock_tag_factory tag_factory.make_tag_for_folder.return_value = fake_tag assembled_entry = self.__factory\ ._AssembledEntryFactory__make_assembled_entry_for_folder( folder, tag_templates_dict) self.assertEqual('test-folder', assembled_entry.entry_id) self.assertEqual({}, assembled_entry.entry) entry_factory.make_entry_for_folder.assert_called_once_with(folder) tags = assembled_entry.tags self.assertEqual(1, len(tags)) self.assertEqual('tagTemplates/sisense_folder_metadata', tags[0].template) tag_factory.make_tag_for_folder.assert_called_once_with( tag_template, folder)
def test_set_string_field_should_skip_empty_value(self, mock_truncate_string): tag = datacatalog.Tag() prepare.BaseTagFactory._set_string_field(tag, 'string', '') self.assertNotIn('string', tag.fields) mock_truncate_string.assert_not_called()
def make_fake_tag(): tag = datacatalog.Tag() tag.template = 'test_template' bool_field = datacatalog.TagField() bool_field.bool_value = True tag.fields['test_bool_field'] = bool_field double_field = datacatalog.TagField() double_field.double_value = 1 tag.fields['test_double_field'] = double_field string_field = datacatalog.TagField() string_field.string_value = 'Test String Value' tag.fields['test_string_field'] = string_field timestamp = timestamp_pb2.Timestamp() timestamp.FromJsonString('2019-10-15T01:00:00-03:00') timestamp_field = datacatalog.TagField() timestamp_field.timestamp_value = timestamp tag.fields['test_timestamp_field'] = timestamp_field enum_field = datacatalog.TagField() enum_field.enum_value.display_name = 'Test ENUM Value' tag.fields['test_enum_field'] = enum_field return tag
def make_tag_for_visualization(self, tag_template, visualization_metadata): tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', visualization_metadata.get('qInfo').get('qId')) self._set_string_field(tag, 'title', visualization_metadata.get('title')) self._set_string_field(tag, 'subtitle', visualization_metadata.get('subtitle')) self._set_string_field(tag, 'footnote', visualization_metadata.get('footnote')) self._set_string_field(tag, 'type', visualization_metadata.get('visualization')) q_meta_def = visualization_metadata.get('qMetaDef') self._set_string_field(tag, 'tags', ', '.join(q_meta_def.get('tags'))) app_metadata = visualization_metadata.get('app') if app_metadata: self._set_string_field(tag, 'app_id', app_metadata.get('id')) self._set_string_field(tag, 'app_name', app_metadata.get('name')) self._set_string_field(tag, 'site_url', self.__site_url) return tag
def test_make_assembled_entry_for_dashboard_should_make_entry_and_tags( self): dashboard = self.__make_fake_dashboard() dashboard_tag_template = datacatalog.TagTemplate() dashboard_tag_template.name = 'tagTemplates/sisense_dashboard_metadata' jaql_tag_template = datacatalog.TagTemplate() jaql_tag_template.name = 'tagTemplates/sisense_jaql_metadata' tag_templates_dict = { 'sisense_dashboard_metadata': dashboard_tag_template, 'sisense_jaql_metadata': jaql_tag_template } fake_entry = ('test-dashboard', {}) entry_factory = self.__mock_entry_factory entry_factory.make_entry_for_dashboard.return_value = fake_entry tag_factory = self.__mock_tag_factory fake_dashboard_tag = datacatalog.Tag() fake_dashboard_tag.template = 'tagTemplates/sisense_dashboard_metadata' tag_factory.make_tag_for_dashboard.return_value = fake_dashboard_tag fake_filter_tag = datacatalog.Tag() fake_filter_tag.template = 'tagTemplates/sisense_jaql_metadata' tag_factory.make_tags_for_dashboard_filters.return_value = [ fake_filter_tag ] assembled_entry = self.__factory\ ._AssembledEntryFactory__make_assembled_entry_for_dashboard( dashboard, tag_templates_dict) self.assertEqual('test-dashboard', assembled_entry.entry_id) self.assertEqual({}, assembled_entry.entry) entry_factory.make_entry_for_dashboard.assert_called_once_with( dashboard) tags = assembled_entry.tags self.assertEqual(2, len(tags)) self.assertEqual('tagTemplates/sisense_dashboard_metadata', tags[0].template) self.assertEqual('tagTemplates/sisense_jaql_metadata', tags[1].template) tag_factory.make_tag_for_dashboard.assert_called_once_with( dashboard_tag_template, dashboard) tag_factory.make_tags_for_dashboard_filters.assert_called_once_with( jaql_tag_template, dashboard)
def test_set_string_field_should_set_given_value(self, mock_truncate_string): expected_value = '{}...'.format('a' * 1997) mock_truncate_string.return_value = expected_value tag = datacatalog.Tag() prepare.BaseTagFactory._set_string_field(tag, 'string', 'a' * 2001) self.assertEqual(expected_value, tag.fields['string'].string_value)
def test_set_timestamp_field_should_set_given_value(self): tag = datacatalog.Tag() prepare.BaseTagFactory._set_timestamp_field( tag, 'timestamp-field', parser.isoparse('2019-09-12T16:30:00+0000')) date = parser.isoparse('2019-09-12T16:30:00+0000') self.assertEqual( int(calendar.timegm(date.utctimetuple())), tag.fields['timestamp-field'].timestamp_value.timestamp())
def __mock_make_tag_classification(cls, classification, *args): tag = datacatalog.Tag() template = 'fake_template/{}'.format(classification['typeName']) if len(args) > 1: template = '{}/column'.format(template) tag.template = template return tag
def create_fake_tag(cls): tag = datacatalog.Tag() tag.name = 'tag_template' tag.template = 'test-template' tag.fields['test-bool-field'].bool_value = True tag.fields['test-double-field'].double_value = 1 tag.fields['test-string-field'].string_value = 'Test String Value' tag.fields['test-timestamp-field'].timestamp_value.FromJsonString( '2019-09-06T11:00:00-03:00') tag.fields['test-enum-field'].enum_value.display_name = \ 'Test ENUM Value' return tag
def create_tag(self, entry, tag_template, fields_descriptors): """Create a Tag.""" tag = datacatalog.Tag() tag.template = tag_template.name for descriptor in fields_descriptors: field = datacatalog.TagField() self.__set_tag_field_value(field, descriptor['value'], descriptor['primitive_type']) tag.fields[descriptor['id']] = field return self.__datacatalog.create_tag(parent=entry.name, tag=tag)
def make_tag(cls, tag_template: TagTemplate, fields: Dict[str, object], column: str = None) -> Tag: tag = datacatalog.Tag() tag.template = tag_template.name if column: tag.column = column cls.__set_tag_fields(tag, tag_template, fields) return tag
def make_tag_for_query(self, tag_template, assembled_query_metadata): tag = datacatalog.Tag() tag.template = tag_template.name query = assembled_query_metadata.query generated_sql = assembled_query_metadata.generated_sql model_explore = assembled_query_metadata.model_explore connection = assembled_query_metadata.connection self._set_double_field(tag, 'id', query.id) if query.fields: value = self.__STRING_VALUE_ARRAY_ELEM_SEP.join(query.fields) self._set_string_field(tag, 'fields', value) if query.pivots: value = self.__STRING_VALUE_ARRAY_ELEM_SEP.join(query.pivots) self._set_string_field(tag, 'pivots', value) if query.sorts: value = self.__STRING_VALUE_ARRAY_ELEM_SEP.join(query.sorts) self._set_string_field(tag, 'sorts', value) self._set_double_field(tag, 'runtime', query.runtime) self._set_string_field(tag, 'client_id', query.client_id) self._set_string_field(tag, 'query_timezone', query.query_timezone) self._set_string_field(tag, 'lookml_model', query.model) self._set_string_field(tag, 'explore_name', query.view) self._set_string_field(tag, 'sql', generated_sql) if model_explore: self._set_string_field(tag, 'lookml_project', model_explore.project_name) self._set_string_field(tag, 'connection', model_explore.connection_name) if connection: self._set_string_field(tag, 'host', connection.host) self._set_string_field(tag, 'database', connection.database) self._set_string_field(tag, 'connection_dialect', connection.dialect_name) self._set_string_field(tag, 'connection_username', connection.username) self._set_string_field(tag, 'instance_url', self.__instance_url) return tag
def test_set_string_field_should_truncate_uft8_bytes_size(self): """ - Input string: 2001 'a' chars; - Expected field value: '1997 "a" chars + ...' since each char needs 1 byte when encoded in UTF-8. """ tag = datacatalog.Tag() prepare.BaseTagFactory._set_string_field(tag, 'string', 'a' * 2001) self.assertEqual(2000, len(tag.fields['string'].string_value)) self.assertEqual('{}...'.format('a' * 1997), tag.fields['string'].string_value) self.assertEqual( 2000, len(tag.fields['string'].string_value.encode('UTF-8')))
def __make_fake_tag(cls, string_fields=None, double_fields=None): tag = datacatalog.Tag() if string_fields: for field in string_fields: string_field = datacatalog.TagField() string_field.string_value = field[1] tag.fields[field[0]] = string_field if double_fields: for field in double_fields: double_field = datacatalog.TagField() double_field.double_value = field[1] tag.fields[field[0]] = double_field return tag
def __make_fake_tag(cls, string_fields=None, double_fields=None): tag = datacatalog.Tag() if string_fields: for string_field in string_fields: tag_field = datacatalog.TagField() tag_field.string_value = string_field[1] tag.fields[string_field[0]] = tag_field if double_fields: for double_field in double_fields: tag_field = datacatalog.TagField() tag_field.double_value = double_field[1] tag.fields[double_field[0]] = tag_field return tag
def __make_tag_for_sql_object_metadata(self, sql_object_config, sql_object_item, tag_template): tag = datacatalog.Tag() tag.template = tag_template.name metadata_def = sql_object_config[ constants.SQL_OBJECT_ITEM_METADATA_DEF_KEY] sql_object_fields = metadata_def[constants.SQL_OBJECT_FIELDS] for sql_object_field in sql_object_fields: self.___add_field_for_sql_object_field(sql_object_field, sql_object_item, tag) return tag
def make_tag_for_dashboard(self, tag_template: TagTemplate, dashboard_metadata: Dict[str, Any]) -> Tag: tag = datacatalog.Tag() tag.template = tag_template.name self._set_string_field(tag, 'id', dashboard_metadata.get('oid')) owner = dashboard_metadata.get('ownerData') if owner: self._set_string_field(tag, 'owner_username', owner.get('userName')) first_name = owner.get('firstName') or '' last_name = owner.get('lastName') or '' self._set_string_field(tag, 'owner_name', f'{first_name} {last_name}') folder = dashboard_metadata.get('folderData') if folder: # The root folder's ``oid`` field is not fulfilled. folder_id = folder.get('oid') or folder.get('name') self._set_string_field(tag, 'folder_id', folder_id) self._set_string_field(tag, 'folder_name', folder.get('name')) datasource = dashboard_metadata.get('datasource') if datasource: self._set_string_field(tag, 'datasource', datasource.get('title')) last_publish_time = dashboard_metadata.get('lastPublish') if last_publish_time: self._set_timestamp_field( tag, 'last_publish', datetime.strptime(last_publish_time, self.__INCOMING_TIMESTAMP_UTC_FORMAT)) last_opened_time = dashboard_metadata.get('lastOpened') if last_opened_time: self._set_timestamp_field( tag, 'last_opened', datetime.strptime(last_opened_time, self.__INCOMING_TIMESTAMP_UTC_FORMAT)) self._set_string_field(tag, 'server_url', self.__server_address) return tag