Exemplo n.º 1
0
 def __init__( self, name = 'new api permissions', access_key = None, basic_permissions = None, search_tag_filter = None ):
     
     if access_key is None:
         
         access_key = HydrusData.GenerateKey()
         
     
     if basic_permissions is None:
         
         basic_permissions = set()
         
     
     if search_tag_filter is None:
         
         search_tag_filter = ClientTags.TagFilter()
         
     
     HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
     
     self._access_key = access_key
     
     self._basic_permissions = set( basic_permissions )
     self._search_tag_filter = search_tag_filter
     
     self._last_search_results = None
     self._search_results_timeout = 0
     
     self._lock = threading.Lock()
    def test_SERIALISABLE_TYPE_TAG_FILTER(self):
        def test(obj, dupe_obj):

            self.assertEqual(obj._tag_slices_to_rules,
                             dupe_obj._tag_slices_to_rules)

        tags = set()

        tags.add('title:test title')
        tags.add('series:neon genesis evangelion')
        tags.add('series:kill la kill')
        tags.add('smile')
        tags.add('blue eyes')

        #

        tag_filter = ClientTags.TagFilter()

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(tags), {
                'smile', 'blue eyes', 'title:test title',
                'series:neon genesis evangelion', 'series:kill la kill'
            })

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('', CC.FILTER_BLACKLIST)
        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(tags), set())

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('', CC.FILTER_BLACKLIST)
        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('series:', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(tags),
            {'series:neon genesis evangelion', 'series:kill la kill'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('', CC.FILTER_BLACKLIST)
        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('series:kill la kill', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(tags), {'series:kill la kill'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('', CC.FILTER_BLACKLIST)
        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('smile', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(tags), {'smile'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(tags), {'smile', 'blue eyes'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('series:', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(tags), {
                'smile', 'blue eyes', 'series:neon genesis evangelion',
                'series:kill la kill'
            })

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule(':', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('series:kill la kill', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(tags),
                         {'smile', 'blue eyes', 'series:kill la kill'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('series:', CC.FILTER_BLACKLIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(tags),
                         {'smile', 'blue eyes', 'title:test title'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('series:', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('series:neon genesis evangelion',
                           CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(tags), {
                'smile', 'blue eyes', 'title:test title',
                'series:neon genesis evangelion'
            })

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('', CC.FILTER_BLACKLIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(tags), {
                'title:test title', 'series:neon genesis evangelion',
                'series:kill la kill'
            })

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('blue eyes', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(tags), {
                'title:test title', 'series:neon genesis evangelion',
                'series:kill la kill', 'blue eyes'
            })

        # blacklist namespace test

        blacklist_tags = {'nintendo', 'studio:nintendo'}

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('nintendo', CC.FILTER_BLACKLIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(tag_filter.Filter(blacklist_tags),
                         {'studio:nintendo'})

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('nintendo', CC.FILTER_BLACKLIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(
                blacklist_tags,
                apply_unnamespaced_rules_to_namespaced_tags=True), set())

        #

        tag_filter = ClientTags.TagFilter()

        tag_filter.SetRule('nintendo', CC.FILTER_BLACKLIST)
        tag_filter.SetRule('studio:nintendo', CC.FILTER_WHITELIST)

        self._dump_and_load_and_test(tag_filter, test)

        self.assertEqual(
            tag_filter.Filter(
                blacklist_tags,
                apply_unnamespaced_rules_to_namespaced_tags=True),
            {'studio:nintendo'})
    def test_SERIALISABLE_TYPE_DUPLICATE_ACTION_OPTIONS(self):
        def test(obj, dupe_obj):

            self.assertEqual(obj.ToTuple(), dupe_obj.ToTuple())

        duplicate_action_options_delete_and_move = ClientDuplicates.DuplicateActionOptions(
            [(CC.DEFAULT_LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE,
              ClientTags.TagFilter())],
            [(TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE),
             (TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY,
              HC.CONTENT_MERGE_ACTION_MOVE)])
        duplicate_action_options_copy = ClientDuplicates.DuplicateActionOptions(
            [(CC.DEFAULT_LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY,
              ClientTags.TagFilter())],
            [(TC.LOCAL_RATING_LIKE_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_COPY),
             (TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY,
              HC.CONTENT_MERGE_ACTION_COPY)])
        duplicate_action_options_merge = ClientDuplicates.DuplicateActionOptions(
            [(CC.DEFAULT_LOCAL_TAG_SERVICE_KEY,
              HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter())],
            [(TC.LOCAL_RATING_LIKE_SERVICE_KEY,
              HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE),
             (TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY,
              HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE)])

        inbox = True
        size = 40960
        mime = HC.IMAGE_JPEG
        width = 640
        height = 480
        duration = None
        num_frames = None
        has_audio = False
        num_words = None

        local_locations_manager = ClientMediaManagers.LocationsManager(
            {CC.LOCAL_FILE_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY},
            set(), set(), set(), inbox)
        trash_locations_manager = ClientMediaManagers.LocationsManager(
            {CC.TRASH_SERVICE_KEY, CC.COMBINED_LOCAL_FILE_SERVICE_KEY}, set(),
            set(), set(), inbox)
        deleted_locations_manager = ClientMediaManagers.LocationsManager(
            set(), {CC.COMBINED_LOCAL_FILE_SERVICE_KEY}, set(), set(), inbox)

        # duplicate to generate proper dicts

        one_tags_manager = ClientMediaManagers.TagsManager(
            {
                CC.DEFAULT_LOCAL_TAG_SERVICE_KEY: {
                    HC.CONTENT_STATUS_CURRENT: {'one'}
                }
            }, {
                CC.DEFAULT_LOCAL_TAG_SERVICE_KEY: {
                    HC.CONTENT_STATUS_CURRENT: {'one'}
                }
            }).Duplicate()
        two_tags_manager = ClientMediaManagers.TagsManager(
            {
                CC.DEFAULT_LOCAL_TAG_SERVICE_KEY: {
                    HC.CONTENT_STATUS_CURRENT: {'two'}
                }
            }, {
                CC.DEFAULT_LOCAL_TAG_SERVICE_KEY: {
                    HC.CONTENT_STATUS_CURRENT: {'two'}
                }
            }).Duplicate()
        substantial_tags_manager = ClientMediaManagers.TagsManager(
            {
                CC.DEFAULT_LOCAL_TAG_SERVICE_KEY: {
                    HC.CONTENT_STATUS_CURRENT:
                    {'test tag', 'series:namespaced test tag'}
                }
            }, {
                CC.DEFAULT_LOCAL_TAG_SERVICE_KEY: {
                    HC.CONTENT_STATUS_CURRENT:
                    {'test tag', 'series:namespaced test tag'}
                }
            }).Duplicate()
        empty_tags_manager = ClientMediaManagers.TagsManager({},
                                                             {}).Duplicate()

        one_ratings_manager = ClientMediaManagers.RatingsManager({
            TC.LOCAL_RATING_LIKE_SERVICE_KEY:
            1.0,
            TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY:
            0.8
        })
        two_ratings_manager = ClientMediaManagers.RatingsManager({
            TC.LOCAL_RATING_LIKE_SERVICE_KEY:
            0.0,
            TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY:
            0.6
        })
        substantial_ratings_manager = ClientMediaManagers.RatingsManager({
            TC.LOCAL_RATING_LIKE_SERVICE_KEY:
            1.0,
            TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY:
            0.8
        })
        empty_ratings_manager = ClientMediaManagers.RatingsManager({})

        notes_manager = ClientMediaManagers.NotesManager({})

        file_viewing_stats_manager = ClientMediaManagers.FileViewingStatsManager.STATICGenerateEmptyManager(
        )

        #

        local_hash_has_values = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            1, local_hash_has_values, size, mime, width, height, duration,
            num_frames, has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, substantial_tags_manager,
            local_locations_manager, substantial_ratings_manager,
            notes_manager, file_viewing_stats_manager)

        local_media_has_values = ClientMedia.MediaSingleton(media_result)

        #

        other_local_hash_has_values = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            2, other_local_hash_has_values, size, mime, width, height,
            duration, num_frames, has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, substantial_tags_manager,
            local_locations_manager, substantial_ratings_manager,
            notes_manager, file_viewing_stats_manager)

        other_local_media_has_values = ClientMedia.MediaSingleton(media_result)

        #

        local_hash_empty = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            3, local_hash_empty, size, mime, width, height, duration,
            num_frames, has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, empty_tags_manager, local_locations_manager,
            empty_ratings_manager, notes_manager, file_viewing_stats_manager)

        local_media_empty = ClientMedia.MediaSingleton(media_result)

        #

        trashed_hash_empty = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            4, trashed_hash_empty, size, mime, width, height, duration,
            num_frames, has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, empty_tags_manager, trash_locations_manager,
            empty_ratings_manager, notes_manager, file_viewing_stats_manager)

        trashed_media_empty = ClientMedia.MediaSingleton(media_result)

        #

        deleted_hash_empty = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            5, deleted_hash_empty, size, mime, width, height, duration,
            num_frames, has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, empty_tags_manager, deleted_locations_manager,
            empty_ratings_manager, notes_manager, file_viewing_stats_manager)

        deleted_media_empty = ClientMedia.MediaSingleton(media_result)

        #

        one_hash = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            6, one_hash, size, mime, width, height, duration, num_frames,
            has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, one_tags_manager, local_locations_manager,
            one_ratings_manager, notes_manager, file_viewing_stats_manager)

        one_media = ClientMedia.MediaSingleton(media_result)

        #

        two_hash = HydrusData.GenerateKey()

        file_info_manager = ClientMediaManagers.FileInfoManager(
            7, two_hash, size, mime, width, height, duration, num_frames,
            has_audio, num_words)

        media_result = ClientMediaResult.MediaResult(
            file_info_manager, two_tags_manager, local_locations_manager,
            two_ratings_manager, notes_manager, file_viewing_stats_manager)

        two_media = ClientMedia.MediaSingleton(media_result)

        #

        self._dump_and_load_and_test(duplicate_action_options_delete_and_move,
                                     test)
        self._dump_and_load_and_test(duplicate_action_options_copy, test)
        self._dump_and_load_and_test(duplicate_action_options_merge, test)

        #

        def assertSCUEqual(one, two):

            self.assertEqual(
                TC.ConvertServiceKeysToContentUpdatesToComparable(one),
                TC.ConvertServiceKeysToContentUpdatesToComparable(two))

        file_deletion_reason = 'test delete'

        #

        result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates(
            local_media_has_values,
            local_media_empty,
            delete_second=True,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.LOCAL_FILE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_FILES,
                                     HC.CONTENT_UPDATE_DELETE,
                                     {local_hash_empty},
                                     reason=file_deletion_reason)
        ]

        assertSCUEqual(result, scu)

        #

        result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates(
            local_media_has_values,
            trashed_media_empty,
            delete_second=True,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.TRASH_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_FILES,
                                     HC.CONTENT_UPDATE_DELETE,
                                     {trashed_hash_empty},
                                     reason=file_deletion_reason)
        ]

        assertSCUEqual(result, scu)

        #

        result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates(
            local_media_has_values,
            deleted_media_empty,
            delete_second=True,
            file_deletion_reason=file_deletion_reason)

        self.assertEqual(result, {})

        #

        result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates(
            local_media_has_values,
            other_local_media_has_values,
            delete_second=True,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.DEFAULT_LOCAL_TAG_SERVICE_KEY] = [
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE,
                ('test tag', {other_local_hash_has_values})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE,
                ('series:namespaced test tag', {other_local_hash_has_values}))
        ]
        scu[TC.LOCAL_RATING_LIKE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (None, {other_local_hash_has_values}))
        ]
        scu[TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (None, {other_local_hash_has_values}))
        ]
        scu[CC.LOCAL_FILE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_FILES,
                                     HC.CONTENT_UPDATE_DELETE,
                                     {other_local_hash_has_values},
                                     reason=file_deletion_reason)
        ]

        assertSCUEqual(result, scu)

        #

        result = duplicate_action_options_delete_and_move.ProcessPairIntoContentUpdates(
            local_media_empty,
            other_local_media_has_values,
            delete_second=True,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.DEFAULT_LOCAL_TAG_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_MAPPINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     ('test tag', {local_hash_empty})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD,
                ('series:namespaced test tag', {local_hash_empty})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE,
                ('test tag', {other_local_hash_has_values})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_DELETE,
                ('series:namespaced test tag', {other_local_hash_has_values}))
        ]
        scu[TC.LOCAL_RATING_LIKE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (1.0, {local_hash_empty})),
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (None, {other_local_hash_has_values}))
        ]
        scu[TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (0.8, {local_hash_empty})),
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (None, {other_local_hash_has_values}))
        ]
        scu[CC.LOCAL_FILE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_FILES,
                                     HC.CONTENT_UPDATE_DELETE,
                                     {other_local_hash_has_values},
                                     reason=file_deletion_reason)
        ]

        assertSCUEqual(result, scu)

        #
        #

        result = duplicate_action_options_copy.ProcessPairIntoContentUpdates(
            local_media_has_values,
            local_media_empty,
            file_deletion_reason=file_deletion_reason)

        self.assertEqual(result, {})

        #

        result = duplicate_action_options_copy.ProcessPairIntoContentUpdates(
            local_media_empty,
            other_local_media_has_values,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.DEFAULT_LOCAL_TAG_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_MAPPINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     ('test tag', {local_hash_empty})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD,
                ('series:namespaced test tag', {local_hash_empty}))
        ]
        scu[TC.LOCAL_RATING_LIKE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (1.0, {local_hash_empty}))
        ]
        scu[TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (0.8, {local_hash_empty}))
        ]

        assertSCUEqual(result, scu)

        #
        #

        result = duplicate_action_options_merge.ProcessPairIntoContentUpdates(
            local_media_has_values,
            local_media_empty,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.DEFAULT_LOCAL_TAG_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_MAPPINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     ('test tag', {local_hash_empty})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD,
                ('series:namespaced test tag', {local_hash_empty}))
        ]
        scu[TC.LOCAL_RATING_LIKE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (1.0, {local_hash_empty}))
        ]
        scu[TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (0.8, {local_hash_empty}))
        ]

        assertSCUEqual(result, scu)

        #

        result = duplicate_action_options_merge.ProcessPairIntoContentUpdates(
            local_media_empty,
            other_local_media_has_values,
            file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.DEFAULT_LOCAL_TAG_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_MAPPINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     ('test tag', {local_hash_empty})),
            HydrusData.ContentUpdate(
                HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD,
                ('series:namespaced test tag', {local_hash_empty}))
        ]
        scu[TC.LOCAL_RATING_LIKE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (1.0, {local_hash_empty}))
        ]
        scu[TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     (0.8, {local_hash_empty}))
        ]

        assertSCUEqual(result, scu)

        #

        result = duplicate_action_options_merge.ProcessPairIntoContentUpdates(
            one_media, two_media, file_deletion_reason=file_deletion_reason)

        scu = {}

        scu[CC.DEFAULT_LOCAL_TAG_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_MAPPINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     ('one', {two_hash})),
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_MAPPINGS,
                                     HC.CONTENT_UPDATE_ADD,
                                     ('two', {one_hash}))
        ]
        scu[TC.LOCAL_RATING_LIKE_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD, (1.0, {two_hash}))
        ]
        scu[TC.LOCAL_RATING_NUMERICAL_SERVICE_KEY] = [
            HydrusData.ContentUpdate(HC.CONTENT_TYPE_RATINGS,
                                     HC.CONTENT_UPDATE_ADD, (0.8, {two_hash}))
        ]

        assertSCUEqual(result, scu)
    def _UpdateSerialisableInfo(self, version, old_serialisable_info):

        if version == 1:

            (serialisable_service_actions,
             delete_second_file) = old_serialisable_info

            tag_service_actions = []
            rating_service_actions = []

            # As the client isn't booted when this is loaded in options, there isn't a good way to figure out tag from rating
            # So, let's just dupe and purge later on, in serialisation
            for (service_key_encoded, action) in serialisable_service_actions:

                service_key = bytes.fromhex(service_key_encoded)

                tag_filter = ClientTags.TagFilter()

                tag_service_actions.append((service_key, action, tag_filter))

                rating_service_actions.append((service_key, action))

            serialisable_tag_service_actions = [
                (service_key.hex(), action, tag_filter.GetSerialisableTuple())
                for (service_key, action, tag_filter) in tag_service_actions
            ]
            serialisable_rating_service_actions = [
                (service_key.hex(), action)
                for (service_key, action) in rating_service_actions
            ]

            sync_archive = delete_second_file
            delete_both_files = False

            new_serialisable_info = (serialisable_tag_service_actions,
                                     serialisable_rating_service_actions,
                                     delete_second_file, sync_archive,
                                     delete_both_files)

            return (2, new_serialisable_info)

        if version == 2:

            (serialisable_tag_service_actions,
             serialisable_rating_service_actions, delete_second_file,
             sync_archive, delete_both_files) = old_serialisable_info

            sync_urls_action = None

            new_serialisable_info = (serialisable_tag_service_actions,
                                     serialisable_rating_service_actions,
                                     delete_second_file, sync_archive,
                                     delete_both_files, sync_urls_action)

            return (3, new_serialisable_info)

        if version == 3:

            (serialisable_tag_service_actions,
             serialisable_rating_service_actions, delete_second_file,
             sync_archive, delete_both_files,
             sync_urls_action) = old_serialisable_info

            new_serialisable_info = (serialisable_tag_service_actions,
                                     serialisable_rating_service_actions,
                                     sync_archive, sync_urls_action)

            return (4, new_serialisable_info)