665 lines
30 KiB
Python
665 lines
30 KiB
Python
import collections
|
|
import itertools
|
|
import sqlite3
|
|
import time
|
|
import typing
|
|
|
|
from hydrus.core import HydrusConstants as HC
|
|
from hydrus.core import HydrusData
|
|
from hydrus.core import HydrusDBBase
|
|
from hydrus.core import HydrusExceptions
|
|
from hydrus.core import HydrusGlobals as HG
|
|
|
|
from hydrus.client import ClientConstants as CC
|
|
from hydrus.client import ClientSearch
|
|
from hydrus.client.db import ClientDBDefinitionsCache
|
|
from hydrus.client.db import ClientDBMaster
|
|
from hydrus.client.db import ClientDBModule
|
|
from hydrus.client.db import ClientDBServices
|
|
from hydrus.client.db import ClientDBTagParents
|
|
from hydrus.client.db import ClientDBTagSiblings
|
|
from hydrus.client.metadata import ClientTags
|
|
|
|
class ClientDBTagDisplay( ClientDBModule.ClientDBModule ):
|
|
|
|
def __init__(
|
|
self,
|
|
cursor: sqlite3.Cursor,
|
|
cursor_transaction_wrapper: HydrusDBBase.DBCursorTransactionWrapper,
|
|
modules_services: ClientDBServices.ClientDBMasterServices,
|
|
modules_tags: ClientDBMaster.ClientDBMasterTags,
|
|
modules_tags_local_cache: ClientDBDefinitionsCache.ClientDBCacheLocalTags,
|
|
modules_tag_siblings: ClientDBTagSiblings.ClientDBTagSiblings,
|
|
modules_tag_parents: ClientDBTagParents.ClientDBTagParents
|
|
):
|
|
|
|
self._cursor_transaction_wrapper = cursor_transaction_wrapper
|
|
self.modules_services = modules_services
|
|
self.modules_tags = modules_tags
|
|
self.modules_tags_local_cache = modules_tags_local_cache
|
|
self.modules_tag_parents = modules_tag_parents
|
|
self.modules_tag_siblings = modules_tag_siblings
|
|
|
|
ClientDBModule.ClientDBModule.__init__( self, 'client tag display', cursor )
|
|
|
|
|
|
def FilterChained( self, display_type, tag_service_id, tag_ids ) -> typing.Set[ int ]:
|
|
|
|
# we are not passing ideal_tag_ids here, but that's ok, we are testing sibling chains in one second
|
|
parents_chained_tag_ids = self.modules_tag_parents.FilterChained( display_type, tag_service_id, tag_ids )
|
|
|
|
unknown_tag_ids = set( tag_ids ).difference( parents_chained_tag_ids )
|
|
|
|
sibling_chained_tag_ids = self.modules_tag_siblings.FilterChained( display_type, tag_service_id, unknown_tag_ids )
|
|
|
|
chained_tag_ids = set( parents_chained_tag_ids ).union( sibling_chained_tag_ids )
|
|
|
|
return chained_tag_ids
|
|
|
|
|
|
def GeneratePredicatesFromTagIdsAndCounts( self, tag_display_type: int, display_tag_service_id: int, tag_ids_to_full_counts, inclusive, job_key = None ):
|
|
|
|
tag_ids = set( tag_ids_to_full_counts.keys() )
|
|
|
|
predicates = []
|
|
|
|
if tag_display_type == ClientTags.TAG_DISPLAY_STORAGE:
|
|
|
|
if display_tag_service_id != self.modules_services.combined_tag_service_id:
|
|
|
|
tag_ids_to_ideal_tag_ids = self.modules_tag_siblings.GetTagIdsToIdealTagIds( ClientTags.TAG_DISPLAY_ACTUAL, display_tag_service_id, tag_ids )
|
|
|
|
tag_ids_that_are_sibling_chained = self.modules_tag_siblings.FilterChained( ClientTags.TAG_DISPLAY_ACTUAL, display_tag_service_id, tag_ids )
|
|
|
|
tag_ids_to_ideal_tag_ids_for_siblings = { tag_id : ideal_tag_id for ( tag_id, ideal_tag_id ) in tag_ids_to_ideal_tag_ids.items() if tag_id in tag_ids_that_are_sibling_chained }
|
|
|
|
ideal_tag_ids_to_sibling_chain_tag_ids = self.modules_tag_siblings.GetIdealTagIdsToChains( ClientTags.TAG_DISPLAY_ACTUAL, display_tag_service_id, set( tag_ids_to_ideal_tag_ids_for_siblings.values() ) )
|
|
|
|
#
|
|
|
|
ideal_tag_ids = set( tag_ids_to_ideal_tag_ids.values() )
|
|
|
|
ideal_tag_ids_that_are_parent_chained = self.modules_tag_parents.FilterChained( ClientTags.TAG_DISPLAY_ACTUAL, display_tag_service_id, ideal_tag_ids )
|
|
|
|
tag_ids_to_ideal_tag_ids_for_parents = { tag_id : ideal_tag_id for ( tag_id, ideal_tag_id ) in tag_ids_to_ideal_tag_ids.items() if ideal_tag_id in ideal_tag_ids_that_are_parent_chained }
|
|
|
|
ideal_tag_ids_to_ancestor_tag_ids = self.modules_tag_parents.GetTagsToAncestors( ClientTags.TAG_DISPLAY_ACTUAL, display_tag_service_id, set( tag_ids_to_ideal_tag_ids_for_parents.values() ) )
|
|
|
|
else:
|
|
|
|
# shouldn't ever happen with storage display
|
|
|
|
tag_ids_to_ideal_tag_ids_for_siblings = {}
|
|
tag_ids_to_ideal_tag_ids_for_parents = {}
|
|
|
|
ideal_tag_ids_to_sibling_chain_tag_ids = {}
|
|
|
|
ideal_tag_ids_to_ancestor_tag_ids = {}
|
|
|
|
|
|
tag_ids_we_want_to_look_up = set( tag_ids )
|
|
tag_ids_we_want_to_look_up.update( itertools.chain.from_iterable( ideal_tag_ids_to_sibling_chain_tag_ids.values() ) )
|
|
tag_ids_we_want_to_look_up.update( itertools.chain.from_iterable( ideal_tag_ids_to_ancestor_tag_ids.values() ) )
|
|
|
|
if job_key is not None and job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
tag_ids_to_tags = self.modules_tags_local_cache.GetTagIdsToTags( tag_ids = tag_ids_we_want_to_look_up )
|
|
|
|
if job_key is not None and job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
ideal_tag_ids_to_chain_tags = { ideal_tag_id : { tag_ids_to_tags[ chain_tag_id ] for chain_tag_id in chain_tag_ids } for ( ideal_tag_id, chain_tag_ids ) in ideal_tag_ids_to_sibling_chain_tag_ids.items() }
|
|
|
|
ideal_tag_ids_to_ancestor_tags = { ideal_tag_id : { tag_ids_to_tags[ ancestor_tag_id ] for ancestor_tag_id in ancestor_tag_ids } for ( ideal_tag_id, ancestor_tag_ids ) in ideal_tag_ids_to_ancestor_tag_ids.items() }
|
|
|
|
for ( tag_id, ( min_current_count, max_current_count, min_pending_count, max_pending_count ) ) in tag_ids_to_full_counts.items():
|
|
|
|
tag = tag_ids_to_tags[ tag_id ]
|
|
|
|
predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, value = tag, inclusive = inclusive, count = ClientSearch.PredicateCount( min_current_count, min_pending_count, max_current_count, max_pending_count ) )
|
|
|
|
if tag_id in tag_ids_to_ideal_tag_ids_for_siblings:
|
|
|
|
ideal_tag_id = tag_ids_to_ideal_tag_ids_for_siblings[ tag_id ]
|
|
|
|
if ideal_tag_id != tag_id:
|
|
|
|
predicate.SetIdealSibling( tag_ids_to_tags[ ideal_tag_id ] )
|
|
|
|
|
|
predicate.SetKnownSiblings( ideal_tag_ids_to_chain_tags[ ideal_tag_id ] )
|
|
|
|
|
|
if tag_id in tag_ids_to_ideal_tag_ids_for_parents:
|
|
|
|
ideal_tag_id = tag_ids_to_ideal_tag_ids_for_parents[ tag_id ]
|
|
|
|
parents = ideal_tag_ids_to_ancestor_tags[ ideal_tag_id ]
|
|
|
|
if len( parents ) > 0:
|
|
|
|
predicate.SetKnownParents( parents )
|
|
|
|
|
|
|
|
predicates.append( predicate )
|
|
|
|
|
|
elif tag_display_type == ClientTags.TAG_DISPLAY_ACTUAL:
|
|
|
|
tag_ids_to_known_chain_tag_ids = collections.defaultdict( set )
|
|
|
|
if display_tag_service_id == self.modules_services.combined_tag_service_id:
|
|
|
|
search_tag_service_ids = self.modules_services.GetServiceIds( HC.REAL_TAG_SERVICES )
|
|
|
|
else:
|
|
|
|
search_tag_service_ids = ( display_tag_service_id, )
|
|
|
|
|
|
for search_tag_service_id in search_tag_service_ids:
|
|
|
|
tag_ids_that_are_sibling_chained = self.modules_tag_siblings.FilterChained( ClientTags.TAG_DISPLAY_ACTUAL, search_tag_service_id, tag_ids )
|
|
|
|
tag_ids_to_ideal_tag_ids_for_siblings = self.modules_tag_siblings.GetTagIdsToIdealTagIds( ClientTags.TAG_DISPLAY_ACTUAL, search_tag_service_id, tag_ids_that_are_sibling_chained )
|
|
|
|
ideal_tag_ids = set( tag_ids_to_ideal_tag_ids_for_siblings.values() )
|
|
|
|
ideal_tag_ids_to_sibling_chain_tag_ids = self.modules_tag_siblings.GetIdealTagIdsToChains( ClientTags.TAG_DISPLAY_ACTUAL, search_tag_service_id, ideal_tag_ids )
|
|
|
|
for ( tag_id, ideal_tag_id ) in tag_ids_to_ideal_tag_ids_for_siblings.items():
|
|
|
|
tag_ids_to_known_chain_tag_ids[ tag_id ].update( ideal_tag_ids_to_sibling_chain_tag_ids[ ideal_tag_id ] )
|
|
|
|
|
|
|
|
tag_ids_we_want_to_look_up = set( tag_ids ).union( itertools.chain.from_iterable( tag_ids_to_known_chain_tag_ids.values() ) )
|
|
|
|
if job_key is not None and job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
tag_ids_to_tags = self.modules_tags_local_cache.GetTagIdsToTags( tag_ids = tag_ids_we_want_to_look_up )
|
|
|
|
if job_key is not None and job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
for ( tag_id, ( min_current_count, max_current_count, min_pending_count, max_pending_count ) ) in tag_ids_to_full_counts.items():
|
|
|
|
tag = tag_ids_to_tags[ tag_id ]
|
|
|
|
predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, value = tag, inclusive = inclusive, count = ClientSearch.PredicateCount( min_current_count, min_pending_count, max_current_count, max_pending_count ) )
|
|
|
|
if tag_id in tag_ids_to_known_chain_tag_ids:
|
|
|
|
chain_tags = { tag_ids_to_tags[ chain_tag_id ] for chain_tag_id in tag_ids_to_known_chain_tag_ids[ tag_id ] }
|
|
|
|
predicate.SetKnownSiblings( chain_tags )
|
|
|
|
|
|
predicates.append( predicate )
|
|
|
|
|
|
|
|
return predicates
|
|
|
|
|
|
def GetApplication( self ):
|
|
|
|
service_keys_to_sibling_applicable_service_keys = self.modules_tag_siblings.GetApplication()
|
|
service_keys_to_parent_applicable_service_keys = self.modules_tag_parents.GetApplication()
|
|
|
|
return ( service_keys_to_sibling_applicable_service_keys, service_keys_to_parent_applicable_service_keys )
|
|
|
|
|
|
def GetApplicationStatus( self, service_id ):
|
|
|
|
( sibling_rows_to_add, sibling_rows_to_remove, num_actual_sibling_rows, num_ideal_sibling_rows ) = self.modules_tag_siblings.GetApplicationStatus( service_id )
|
|
( parent_rows_to_add, parent_rows_to_remove, num_actual_parent_rows, num_ideal_parent_rows ) = self.modules_tag_parents.GetApplicationStatus( service_id )
|
|
|
|
num_actual_rows = num_actual_sibling_rows + num_actual_parent_rows
|
|
num_ideal_rows = num_ideal_sibling_rows + num_ideal_parent_rows
|
|
|
|
return ( sibling_rows_to_add, sibling_rows_to_remove, parent_rows_to_add, parent_rows_to_remove, num_actual_rows, num_ideal_rows )
|
|
|
|
|
|
def GetApplicableServiceIds( self, tag_service_id ) -> typing.Set[ int ]:
|
|
|
|
return set( self.modules_tag_siblings.GetApplicableServiceIds( tag_service_id ) ).union( self.modules_tag_parents.GetApplicableServiceIds( tag_service_id ) )
|
|
|
|
|
|
def GetChainsMembers( self, display_type, tag_service_id, tag_ids ) -> typing.Set[ int ]:
|
|
|
|
# all parent definitions are sibling collapsed, so are terminus of their sibling chains
|
|
# so get all of the parent chain, then get all chains that point to those
|
|
|
|
ideal_tag_ids = self.modules_tag_siblings.GetIdealTagIds( display_type, tag_service_id, tag_ids )
|
|
|
|
parent_chain_members = self.modules_tag_parents.GetChainsMembers( display_type, tag_service_id, ideal_tag_ids )
|
|
|
|
sibling_chain_members = self.modules_tag_siblings.GetChainsMembersFromIdeals( display_type, tag_service_id, parent_chain_members )
|
|
|
|
return sibling_chain_members.union( parent_chain_members )
|
|
|
|
# ok revisit this sometime I guess but it needs work
|
|
'''
|
|
with self._MakeTemporaryIntegerTable( tag_ids, 'tag_id' ) as temp_tag_ids_table_name:
|
|
|
|
with self._MakeTemporaryIntegerTable( [], 'ideal_tag_id' ) as temp_ideal_tag_ids_table_name:
|
|
|
|
self.modules_tag_siblings.GetIdealTagIdsIntoTable( display_type, tag_service_id, temp_tag_ids_table_name, temp_ideal_tag_ids_table_name )
|
|
|
|
with self._MakeTemporaryIntegerTable( [], 'ideal_tag_id' ) as temp_parent_chain_members_table_name:
|
|
|
|
# this is actually not implemented LMAO
|
|
self.modules_tag_parents.GetChainsMembersTables( display_type, tag_service_id, temp_ideal_tag_ids_table_name, temp_parent_chain_members_table_name, 'ideal_tag_id' )
|
|
|
|
with self._MakeTemporaryIntegerTable( [], 'tag_id' ) as temp_chain_members_table_name:
|
|
|
|
self.modules_tag_siblings.GetChainsMembersFromIdealsTables( display_type, tag_service_id, temp_parent_chain_members_table_name, temp_chain_members_table_name )
|
|
|
|
return self._STS( self._Execute( 'SELECT tag_id FROM {};'.format( temp_chain_members_table_name ) ) )
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
def GetImpliedBy( self, display_type, tag_service_id, tag_id ) -> typing.Set[ int ]:
|
|
|
|
ideal_tag_id = self.modules_tag_siblings.GetIdealTagId( display_type, tag_service_id, tag_id )
|
|
|
|
if ideal_tag_id == tag_id:
|
|
|
|
# this tag exists in display
|
|
# it is also implied by any descendant
|
|
# and any of its or those descendants' siblings
|
|
|
|
# these are all ideal siblings
|
|
self_and_descendant_ids = { tag_id }.union( self.modules_tag_parents.GetDescendants( display_type, tag_service_id, ideal_tag_id ) )
|
|
|
|
implication_ids = self.modules_tag_siblings.GetChainsMembersFromIdeals( display_type, tag_service_id, self_and_descendant_ids )
|
|
|
|
else:
|
|
|
|
# this tag does not exist in display
|
|
|
|
implication_ids = set()
|
|
|
|
|
|
return implication_ids
|
|
|
|
|
|
def GetImplies( self, display_type, tag_service_id, tag_id ) -> typing.Set[ int ]:
|
|
|
|
# a tag implies its ideal sibling and any ancestors
|
|
|
|
ideal_tag_id = self.modules_tag_siblings.GetIdealTagId( display_type, tag_service_id, tag_id )
|
|
|
|
implies = { ideal_tag_id }
|
|
|
|
implies.update( self.modules_tag_parents.GetAncestors( display_type, tag_service_id, ideal_tag_id ) )
|
|
|
|
return implies
|
|
|
|
|
|
def GetInterestedServiceIds( self, tag_service_id ) -> typing.Set[ int ]:
|
|
|
|
return set( self.modules_tag_siblings.GetInterestedServiceIds( tag_service_id ) ).union( self.modules_tag_parents.GetInterestedServiceIds( tag_service_id ) )
|
|
|
|
|
|
def GetMediaPredicates( self, tag_context: ClientSearch.TagContext, tags_to_counts, inclusive, job_key = None ):
|
|
|
|
if HG.autocomplete_delay_mode:
|
|
|
|
time_to_stop = HydrusData.GetNowFloat() + 3.0
|
|
|
|
while not HydrusData.TimeHasPassedFloat( time_to_stop ):
|
|
|
|
time.sleep( 0.1 )
|
|
|
|
if job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
display_tag_service_id = self.modules_services.GetServiceId( tag_context.display_service_key )
|
|
|
|
max_current_count = None
|
|
max_pending_count = None
|
|
|
|
tag_ids_to_full_counts = {}
|
|
|
|
showed_bad_tag_error = False
|
|
|
|
for ( i, ( tag, ( current_count, pending_count ) ) ) in enumerate( tags_to_counts.items() ):
|
|
|
|
try:
|
|
|
|
tag_id = self.modules_tags.GetTagId( tag )
|
|
|
|
except HydrusExceptions.TagSizeException:
|
|
|
|
if not showed_bad_tag_error:
|
|
|
|
showed_bad_tag_error = True
|
|
|
|
HydrusData.ShowText( 'Hey, you seem to have an invalid tag in view right now! Please run the \'repair invalid tags\' routine under the \'database\' menu asap!' )
|
|
|
|
|
|
continue
|
|
|
|
|
|
tag_ids_to_full_counts[ tag_id ] = ( current_count, max_current_count, pending_count, max_pending_count )
|
|
|
|
if i % 100 == 0:
|
|
|
|
if job_key is not None and job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
if job_key is not None and job_key.IsCancelled():
|
|
|
|
return []
|
|
|
|
|
|
predicates = self.GeneratePredicatesFromTagIdsAndCounts( ClientTags.TAG_DISPLAY_ACTUAL, display_tag_service_id, tag_ids_to_full_counts, inclusive, job_key = job_key )
|
|
|
|
return predicates
|
|
|
|
|
|
def GetSiblingsAndParentsForTags( self, tags ):
|
|
|
|
tag_services = self.modules_services.GetServices( HC.REAL_TAG_SERVICES )
|
|
|
|
service_keys = [ tag_service.GetServiceKey() for tag_service in tag_services ]
|
|
|
|
tags_to_service_keys_to_siblings_and_parents = {}
|
|
|
|
for tag in tags:
|
|
|
|
sibling_chain_members = { tag }
|
|
ideal_tag = tag
|
|
descendants = set()
|
|
ancestors = set()
|
|
|
|
tags_to_service_keys_to_siblings_and_parents[ tag ] = { service_key : ( sibling_chain_members, ideal_tag, descendants, ancestors ) for service_key in service_keys }
|
|
|
|
|
|
for service_key in service_keys:
|
|
|
|
tag_service_id = self.modules_services.GetServiceId( service_key )
|
|
|
|
existing_tags = { tag for tag in tags if self.modules_tags.TagExists( tag ) }
|
|
|
|
existing_tag_ids = { self.modules_tags.GetTagId( tag ) for tag in existing_tags }
|
|
|
|
tag_ids_to_ideal_tag_ids = self.modules_tag_siblings.GetTagIdsToIdealTagIds( ClientTags.TAG_DISPLAY_ACTUAL, tag_service_id, existing_tag_ids )
|
|
|
|
ideal_tag_ids = set( tag_ids_to_ideal_tag_ids.values() )
|
|
|
|
ideal_tag_ids_to_sibling_chain_ids = self.modules_tag_siblings.GetIdealTagIdsToChains( ClientTags.TAG_DISPLAY_ACTUAL, tag_service_id, ideal_tag_ids )
|
|
|
|
ideal_tag_ids_to_descendant_tag_ids = self.modules_tag_parents.GetTagsToDescendants( ClientTags.TAG_DISPLAY_ACTUAL, tag_service_id, ideal_tag_ids )
|
|
ideal_tag_ids_to_ancestor_tag_ids = self.modules_tag_parents.GetTagsToAncestors( ClientTags.TAG_DISPLAY_ACTUAL, tag_service_id, ideal_tag_ids )
|
|
|
|
all_tag_ids = set()
|
|
|
|
all_tag_ids.update( ideal_tag_ids_to_sibling_chain_ids.keys() )
|
|
all_tag_ids.update( itertools.chain.from_iterable( ideal_tag_ids_to_sibling_chain_ids.values() ) )
|
|
all_tag_ids.update( itertools.chain.from_iterable( ideal_tag_ids_to_descendant_tag_ids.values() ) )
|
|
all_tag_ids.update( itertools.chain.from_iterable( ideal_tag_ids_to_ancestor_tag_ids.values() ) )
|
|
|
|
tag_ids_to_tags = self.modules_tags_local_cache.GetTagIdsToTags( tag_ids = all_tag_ids )
|
|
|
|
for tag_id in existing_tag_ids:
|
|
|
|
ideal_tag_id = tag_ids_to_ideal_tag_ids[ tag_id ]
|
|
sibling_chain_ids = ideal_tag_ids_to_sibling_chain_ids[ ideal_tag_id ]
|
|
descendant_tag_ids = ideal_tag_ids_to_descendant_tag_ids[ ideal_tag_id ]
|
|
ancestor_tag_ids = ideal_tag_ids_to_ancestor_tag_ids[ ideal_tag_id ]
|
|
|
|
tag = tag_ids_to_tags[ tag_id ]
|
|
ideal_tag = tag_ids_to_tags[ ideal_tag_id ]
|
|
sibling_chain_members = { tag_ids_to_tags[ sibling_chain_id ] for sibling_chain_id in sibling_chain_ids }
|
|
descendants = { tag_ids_to_tags[ descendant_tag_id ] for descendant_tag_id in descendant_tag_ids }
|
|
ancestors = { tag_ids_to_tags[ ancestor_tag_id ] for ancestor_tag_id in ancestor_tag_ids }
|
|
|
|
tags_to_service_keys_to_siblings_and_parents[ tag ][ service_key ] = ( sibling_chain_members, ideal_tag, descendants, ancestors )
|
|
|
|
|
|
|
|
return tags_to_service_keys_to_siblings_and_parents
|
|
|
|
|
|
def GetTablesAndColumnsThatUseDefinitions( self, content_type: int ) -> typing.List[ typing.Tuple[ str, str ] ]:
|
|
|
|
return []
|
|
|
|
|
|
def GetTagsToImpliedBy( self, display_type, tag_service_id, tag_ids, tags_are_ideal = False ):
|
|
|
|
tag_ids_to_implied_by = collections.defaultdict( set )
|
|
|
|
if tags_are_ideal:
|
|
|
|
tag_ids_that_exist_in_display = set( tag_ids )
|
|
|
|
else:
|
|
|
|
tag_ids_to_ideals = self.modules_tag_siblings.GetTagIdsToIdealTagIds( display_type, tag_service_id, tag_ids )
|
|
|
|
tag_ids_that_exist_in_display = set()
|
|
|
|
for ( tag_id, ideal_tag_id ) in tag_ids_to_ideals.items():
|
|
|
|
if tag_id == ideal_tag_id:
|
|
|
|
tag_ids_that_exist_in_display.add( ideal_tag_id )
|
|
|
|
else:
|
|
|
|
# this tag does not exist in display
|
|
|
|
tag_ids_to_implied_by[ tag_id ] = set()
|
|
|
|
|
|
|
|
|
|
# tags are implied by descendants, and their siblings
|
|
|
|
tag_ids_to_descendants = self.modules_tag_parents.GetTagsToDescendants( display_type, tag_service_id, tag_ids_that_exist_in_display )
|
|
|
|
all_tags_and_descendants = set( tag_ids_that_exist_in_display )
|
|
all_tags_and_descendants.update( itertools.chain.from_iterable( tag_ids_to_descendants.values() ) )
|
|
|
|
# these are all ideal_tag_ids
|
|
all_tags_and_descendants_to_chains = self.modules_tag_siblings.GetIdealTagIdsToChains( display_type, tag_service_id, all_tags_and_descendants )
|
|
|
|
for ( tag_id, descendants ) in tag_ids_to_descendants.items():
|
|
|
|
implications = set( itertools.chain.from_iterable( ( all_tags_and_descendants_to_chains[ descendant ] for descendant in descendants ) ) )
|
|
implications.update( all_tags_and_descendants_to_chains[ tag_id ] )
|
|
|
|
tag_ids_to_implied_by[ tag_id ] = implications
|
|
|
|
|
|
return tag_ids_to_implied_by
|
|
|
|
|
|
def GetTagsToImplies( self, display_type, tag_service_id, tag_ids ):
|
|
|
|
# a tag implies its ideal sibling and any ancestors
|
|
|
|
tag_ids_to_implies = collections.defaultdict( set )
|
|
|
|
tag_ids_to_ideals = self.modules_tag_siblings.GetTagIdsToIdealTagIds( display_type, tag_service_id, tag_ids )
|
|
|
|
ideal_tag_ids = set( tag_ids_to_ideals.values() )
|
|
|
|
ideal_tag_ids_to_ancestors = self.modules_tag_parents.GetTagsToAncestors( display_type, tag_service_id, ideal_tag_ids )
|
|
|
|
for ( tag_id, ideal_tag_id ) in tag_ids_to_ideals.items():
|
|
|
|
implies = { ideal_tag_id }
|
|
implies.update( ideal_tag_ids_to_ancestors[ ideal_tag_id ] )
|
|
|
|
tag_ids_to_implies[ tag_id ] = implies
|
|
|
|
|
|
return tag_ids_to_implies
|
|
|
|
|
|
def GetUIDecorators( self, service_key, tags ):
|
|
|
|
tags_to_display_decorators = { tag : None for tag in tags }
|
|
|
|
if service_key == CC.COMBINED_TAG_SERVICE_KEY:
|
|
|
|
return tags_to_display_decorators
|
|
|
|
|
|
tag_service_id = self.modules_services.GetServiceId( service_key )
|
|
|
|
existing_tags = { tag for tag in tags if self.modules_tags.TagExists( tag ) }
|
|
|
|
existing_tag_ids = { self.modules_tags.GetTagId( tag ) for tag in existing_tags }
|
|
|
|
existing_tag_ids_to_ideal_tag_ids = self.modules_tag_siblings.GetTagIdsToIdealTagIds( ClientTags.TAG_DISPLAY_ACTUAL, tag_service_id, existing_tag_ids )
|
|
|
|
ideal_tag_ids = set( existing_tag_ids_to_ideal_tag_ids.values() )
|
|
|
|
interesting_tag_ids_to_ideal_tag_ids = { tag_id : ideal_tag_id for ( tag_id, ideal_tag_id ) in existing_tag_ids_to_ideal_tag_ids.items() if tag_id != ideal_tag_id }
|
|
|
|
ideal_tag_ids_to_ancestor_tag_ids = self.modules_tag_parents.GetTagsToAncestors( ClientTags.TAG_DISPLAY_ACTUAL, tag_service_id, ideal_tag_ids )
|
|
|
|
existing_tag_ids_to_ancestor_tag_ids = { existing_tag_id : ideal_tag_ids_to_ancestor_tag_ids[ existing_tag_ids_to_ideal_tag_ids[ existing_tag_id ] ] for existing_tag_id in existing_tag_ids }
|
|
|
|
interesting_tag_ids_to_ancestor_tag_ids = { tag_id : ancestor_tag_ids for ( tag_id, ancestor_tag_ids ) in existing_tag_ids_to_ancestor_tag_ids.items() if len( ancestor_tag_ids ) > 0 }
|
|
|
|
all_interesting_tag_ids = set()
|
|
|
|
all_interesting_tag_ids.update( interesting_tag_ids_to_ideal_tag_ids.keys() )
|
|
all_interesting_tag_ids.update( interesting_tag_ids_to_ideal_tag_ids.values() )
|
|
all_interesting_tag_ids.update( interesting_tag_ids_to_ancestor_tag_ids.keys() )
|
|
all_interesting_tag_ids.update( itertools.chain.from_iterable( interesting_tag_ids_to_ancestor_tag_ids.values() ) )
|
|
|
|
tag_ids_to_tags = self.modules_tags_local_cache.GetTagIdsToTags( tag_ids = all_interesting_tag_ids )
|
|
|
|
for tag_id in existing_tag_ids:
|
|
|
|
if tag_id in interesting_tag_ids_to_ideal_tag_ids:
|
|
|
|
ideal = tag_ids_to_tags[ interesting_tag_ids_to_ideal_tag_ids[ tag_id ] ]
|
|
|
|
else:
|
|
|
|
ideal = None
|
|
|
|
|
|
if tag_id in interesting_tag_ids_to_ancestor_tag_ids:
|
|
|
|
parents = { tag_ids_to_tags[ ancestor_tag_id ] for ancestor_tag_id in interesting_tag_ids_to_ancestor_tag_ids[ tag_id ] }
|
|
|
|
else:
|
|
|
|
parents = None
|
|
|
|
|
|
if ideal is not None or parents is not None:
|
|
|
|
tag = tag_ids_to_tags[ tag_id ]
|
|
|
|
tags_to_display_decorators[ tag ] = ( ideal, parents )
|
|
|
|
|
|
|
|
return tags_to_display_decorators
|
|
|
|
|
|
def IsChained( self, display_type, tag_service_id, tag_id ) -> bool:
|
|
|
|
return self.modules_tag_parents.IsChained( display_type, tag_service_id, tag_id ) or self.modules_tag_siblings.IsChained( display_type, tag_service_id, tag_id )
|
|
|
|
|
|
def NotifyParentsChanged( self, tag_service_id_that_changed, tag_ids_that_changed ):
|
|
|
|
if len( tag_ids_that_changed ) == 0:
|
|
|
|
return
|
|
|
|
|
|
# the parents for tag_ids have changed for tag_service_id
|
|
# therefore any service that is interested in tag_service_ids's parents needs to regen the respective chains for these tags
|
|
|
|
interested_tag_service_ids = self.modules_tag_parents.GetInterestedServiceIds( tag_service_id_that_changed )
|
|
|
|
self.modules_tag_parents.RegenChains( interested_tag_service_ids, tag_ids_that_changed )
|
|
|
|
|
|
def NotifySiblingsChanged( self, tag_service_id_that_changed, tag_ids_that_changed ):
|
|
|
|
if len( tag_ids_that_changed ) == 0:
|
|
|
|
return
|
|
|
|
|
|
# the siblings for tag_ids have changed for tag_service_id
|
|
# therefore any service that is interested in tag_service_ids's siblings needs to regen the respective chains for these tags
|
|
|
|
interested_tag_service_ids = self.modules_tag_siblings.GetInterestedServiceIds( tag_service_id_that_changed )
|
|
|
|
self.modules_tag_siblings.RegenChains( interested_tag_service_ids, tag_ids_that_changed )
|
|
|
|
# since siblings just changed, parents can as well! any interested service that has any of these tag_ids in its parent structure may need new ids, so let's regen
|
|
|
|
self.modules_tag_parents.RegenChains( interested_tag_service_ids, tag_ids_that_changed )
|
|
|
|
|
|
def RegenerateTagSiblingsAndParentsCache( self, only_these_service_ids = None ):
|
|
|
|
if only_these_service_ids is None:
|
|
|
|
tag_service_ids = self.modules_services.GetServiceIds( HC.REAL_TAG_SERVICES )
|
|
|
|
else:
|
|
|
|
tag_service_ids = only_these_service_ids
|
|
|
|
|
|
self.modules_tag_siblings.Regen( tag_service_ids )
|
|
|
|
# as siblings may have changed, parents may have as well
|
|
self.modules_tag_parents.Regen( tag_service_ids )
|
|
|
|
self._cursor_transaction_wrapper.pub_after_job( 'notify_new_tag_display_application' )
|
|
|
|
|
|
def SetApplication( self, service_keys_to_sibling_applicable_service_keys, service_keys_to_parent_applicable_service_keys ):
|
|
|
|
sibling_service_ids_to_sync = self.modules_tag_siblings.SetApplication( service_keys_to_sibling_applicable_service_keys )
|
|
parent_service_ids_to_sync = self.modules_tag_parents.SetApplication( service_keys_to_parent_applicable_service_keys )
|
|
|
|
service_ids_to_sync = sibling_service_ids_to_sync.union( parent_service_ids_to_sync )
|
|
|
|
#
|
|
|
|
self.RegenerateTagSiblingsAndParentsCache( only_these_service_ids = service_ids_to_sync )
|
|
|
|
self._cursor_transaction_wrapper.pub_after_job( 'notify_new_tag_display_application' )
|
|
|