Version 211
This commit is contained in:
parent
eccf03aaa4
commit
aae8d9cc97
|
@ -8,6 +8,17 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 211</h3></li>
|
||||
<ul>
|
||||
<li>added options for disk cache init and maintenance to 'speed and memory' page of options</li>
|
||||
<li>abstracted the manage tags dialog into a general purpose 'apply' dialog and a scrollable manage tags panel</li>
|
||||
<li>the 'auto-replace entered siblings' checkbox now remembers how it was last set</li>
|
||||
<li>uncertain sibling tag counts will now appear like 'tag (1,234-1,456)', showing the range of possible results they represent</li>
|
||||
<li>fixed a bug where siblings would be collapsed inside the tag manager object if a specific tag search domain was set</li>
|
||||
<li>altering the search text on an autocomplete from 'tag' to '-tag' or vice versa should graphically update more reliably, and an exact-matching predicate will be promoted to the top of the list correctly either way</li>
|
||||
<li>calls to upnpc will now kill the process and raise an exception if it hangs for more than thirty seconds</li>
|
||||
<li>misc improvements and cleanup</li>
|
||||
</ul>
|
||||
<li><h3>version 210</h3></li>
|
||||
<ul>
|
||||
<li>manage tags dialog now launches much faster when thousands of thumbs are selected</li>
|
||||
|
|
|
@ -2309,7 +2309,7 @@ class TagSiblingsManager( object ):
|
|||
|
||||
( old_pred_type, old_value, old_inclusive ) = old_predicate.GetInfo()
|
||||
|
||||
new_predicate = ClientSearch.Predicate( old_pred_type, new_tag, inclusive = old_inclusive )
|
||||
new_predicate = ClientSearch.Predicate( old_pred_type, new_tag, old_inclusive )
|
||||
|
||||
tags_to_predicates[ new_tag ] = new_predicate
|
||||
|
||||
|
@ -2360,12 +2360,14 @@ class TagSiblingsManager( object ):
|
|||
|
||||
statuses = statuses_to_tags.keys()
|
||||
|
||||
new_statuses_to_tags = HydrusData.default_dict_set()
|
||||
|
||||
for status in statuses:
|
||||
|
||||
statuses_to_tags[ status ] = self._CollapseTags( statuses_to_tags[ status ] )
|
||||
new_statuses_to_tags[ status ] = self._CollapseTags( statuses_to_tags[ status ] )
|
||||
|
||||
|
||||
return statuses_to_tags
|
||||
return new_statuses_to_tags
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -616,7 +616,12 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
|
||||
|
||||
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = stop_time, caller_limit = 200 * 1024 * 1024 )
|
||||
disk_cache_maintenance_mb = self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' )
|
||||
|
||||
if disk_cache_maintenance_mb is not None:
|
||||
|
||||
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = stop_time, caller_limit = disk_cache_maintenance_mb * 1024 * 1024 )
|
||||
|
||||
|
||||
self.WriteInterruptable( 'vacuum', stop_time = stop_time )
|
||||
|
||||
|
|
|
@ -2754,7 +2754,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
return namespace_id_tag_ids
|
||||
|
||||
|
||||
def _GetAutocompletePredicates( self, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, file_service_key = CC.COMBINED_FILE_SERVICE_KEY, search_text = '', exact_match = False, include_current = True, include_pending = True, add_namespaceless = False ):
|
||||
def _GetAutocompletePredicates( self, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, file_service_key = CC.COMBINED_FILE_SERVICE_KEY, search_text = '', exact_match = False, inclusive = True, include_current = True, include_pending = True, add_namespaceless = False ):
|
||||
|
||||
namespace_id_tag_ids = self._GetAutocompleteNamespaceIdTagIds( search_text, exact_match )
|
||||
|
||||
|
@ -2782,7 +2782,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
filtered_tags = tag_censorship_manager.FilterTags( tag_service_key, tags_to_do )
|
||||
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, counts = { HC.CURRENT : current_count, HC.PENDING : pending_count } ) for ( tag, current_count, pending_count ) in tag_info if tag in filtered_tags ]
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive, current_count, pending_count ) for ( tag, current_count, pending_count ) in tag_info if tag in filtered_tags ]
|
||||
|
||||
return predicates
|
||||
|
||||
|
@ -2853,7 +2853,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
num_everything = service_info[ HC.SERVICE_INFO_NUM_FILES ]
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : num_everything } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = num_everything ) )
|
||||
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_HASH ] ] )
|
||||
|
||||
|
@ -2874,7 +2874,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
num_archive = num_local - num_inbox
|
||||
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : num_everything } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = num_everything ) )
|
||||
|
||||
show_inbox_and_archive = True
|
||||
|
||||
|
@ -2887,23 +2887,23 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if show_inbox_and_archive:
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, None, counts = { HC.CURRENT : num_inbox } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, None, counts = { HC.CURRENT : num_archive } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = num_inbox ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = num_archive ) )
|
||||
|
||||
|
||||
if service_type == HC.FILE_REPOSITORY:
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, None, counts = { HC.CURRENT : num_local } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, None, counts = { HC.CURRENT : num_not_local } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, min_current_count = num_local ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, min_current_count = num_not_local ) )
|
||||
|
||||
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME ] ] )
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME ] ] )
|
||||
|
||||
ratings_service_ids = self._GetServiceIds( HC.RATINGS_SERVICES )
|
||||
|
||||
if len( ratings_service_ids ) > 0: predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, None ) )
|
||||
if len( ratings_service_ids ) > 0: predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING ) )
|
||||
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
|
||||
|
||||
|
||||
return predicates
|
||||
|
@ -5066,11 +5066,18 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
def _InitCaches( self ):
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
|
||||
new_options = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS )
|
||||
|
||||
stop_time = HydrusData.GetNow() + 10
|
||||
disk_cache_init_period = new_options.GetNoneableInteger( 'disk_cache_init_period' )
|
||||
|
||||
self._LoadIntoDiskCache( stop_time = stop_time )
|
||||
if disk_cache_init_period is not None:
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
|
||||
|
||||
stop_time = HydrusData.GetNow() + disk_cache_init_period
|
||||
|
||||
self._LoadIntoDiskCache( stop_time = stop_time )
|
||||
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing db caches' )
|
||||
|
||||
|
|
|
@ -453,10 +453,15 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._dictionary[ 'booleans' ][ 'disable_cv_for_gifs' ] = False
|
||||
|
||||
self._dictionary[ 'booleans' ][ 'replace_siblings_on_manage_tags' ] = True
|
||||
|
||||
self._dictionary[ 'noneable_integers' ] = {}
|
||||
|
||||
self._dictionary[ 'noneable_integers' ][ 'forced_search_limit' ] = None
|
||||
|
||||
self._dictionary[ 'noneable_integers' ][ 'disk_cache_maintenance_mb' ] = 256
|
||||
self._dictionary[ 'noneable_integers' ][ 'disk_cache_init_period' ] = 4
|
||||
|
||||
client_files_default = os.path.join( HC.DB_DIR, 'client_files' )
|
||||
|
||||
self._dictionary[ 'client_files_locations_ideal_weights' ] = [ ( HydrusPaths.ConvertAbsPathToPortablePath( client_files_default ), 1.0 ) ]
|
||||
|
@ -1578,7 +1583,7 @@ class ServiceRepository( ServiceRestricted ):
|
|||
|
||||
|
||||
|
||||
if self.CanProcessUpdate():
|
||||
if self._service_type == HC.TAG_REPOSITORY and self.CanProcessUpdate():
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
|
||||
job_key.SetVariable( 'popup_text_1', 'preparing disk cache' )
|
||||
|
|
|
@ -1163,7 +1163,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
|
|||
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'debug_garbage' ), p( 'Garbage' ) )
|
||||
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'clear_caches' ), p( '&Clear Preview/Fullscreen Caches' ) )
|
||||
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'delete_service_info' ), p( '&Clear DB Service Info Cache' ), p( 'Delete all cached service info, in case it has become desynchronised.' ) )
|
||||
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'load_into_disk_cache' ), p( 'Load db into disk cache' ) )
|
||||
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'load_into_disk_cache' ), p( 'Load whole db into disk cache' ) )
|
||||
|
||||
menu.AppendMenu( wx.ID_NONE, p( 'Debug' ), debug )
|
||||
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'help_shortcuts' ), p( '&Shortcuts' ) )
|
||||
|
|
|
@ -7,6 +7,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIDialogsManage
|
||||
import ClientGUIHoverFrames
|
||||
import ClientGUIPanels
|
||||
import ClientMedia
|
||||
import ClientRatings
|
||||
import collections
|
||||
|
@ -883,7 +884,14 @@ class Canvas( wx.Window ):
|
|||
|
||||
if self._current_display_media is not None:
|
||||
|
||||
with ClientGUIDialogsManage.DialogManageTags( self, self._file_service_key, ( self._current_display_media, ), canvas_key = self._canvas_key ) as dlg:
|
||||
title = 'manage tags'
|
||||
dialog_key = 'manage_tags'
|
||||
|
||||
with ClientGUIDialogs.DialogManageApply( self, title, dialog_key ) as dlg:
|
||||
|
||||
panel = ClientGUIPanels.ManageTagsPanel( dlg, self._file_service_key, ( self._current_display_media, ), canvas_key = self._canvas_key )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
|
|
@ -900,11 +900,11 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
if sibling is None:
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, search_text, inclusive = inclusive )
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, search_text, inclusive )
|
||||
|
||||
else:
|
||||
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, sibling, inclusive = inclusive )
|
||||
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, sibling, inclusive )
|
||||
|
||||
|
||||
return ( inclusive, search_text, entry_predicate )
|
||||
|
@ -941,7 +941,10 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
must_do_a_search = False
|
||||
|
||||
if '*' in search_text: must_do_a_search = True
|
||||
if '*' in search_text:
|
||||
|
||||
must_do_a_search = True
|
||||
|
||||
|
||||
if ':' in search_text:
|
||||
|
||||
|
@ -986,7 +989,10 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
can_fetch_from_media = media is not None and len( media ) > 0
|
||||
|
||||
if can_fetch_from_media and self._synchronised.IsOn(): fetch_from_db = False
|
||||
if can_fetch_from_media and self._synchronised.IsOn():
|
||||
|
||||
fetch_from_db = False
|
||||
|
||||
|
||||
|
||||
if fetch_from_db:
|
||||
|
@ -996,7 +1002,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
if len( half_complete_tag ) < num_autocomplete_chars and '*' not in search_text:
|
||||
|
||||
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
|
||||
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
|
||||
|
||||
predicates = siblings_manager.CollapsePredicates( predicates )
|
||||
|
||||
|
@ -1008,7 +1014,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
self._cache_text = search_text
|
||||
|
||||
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
|
||||
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
|
||||
|
||||
self._cached_results = siblings_manager.CollapsePredicates( self._cached_results )
|
||||
|
||||
|
@ -1062,7 +1068,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
tags_to_do.update( pending_tags_to_count.keys() )
|
||||
|
||||
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive = inclusive, counts = { HC.CURRENT : current_tags_to_count[ tag ], HC.PENDING : pending_tags_to_count[ tag ] } ) for tag in tags_to_do ]
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive, current_tags_to_count[ tag ], pending_tags_to_count[ tag ] ) for tag in tags_to_do ]
|
||||
|
||||
predicates = siblings_manager.CollapsePredicates( predicates )
|
||||
|
||||
|
@ -1080,23 +1086,28 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
if '*' not in self._current_namespace and half_complete_tag == '':
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, self._current_namespace, inclusive = inclusive ) )
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, self._current_namespace, inclusive ) )
|
||||
|
||||
|
||||
if half_complete_tag != '':
|
||||
|
||||
if '*' in self._current_namespace or ( '*' in half_complete_tag and half_complete_tag != '*' ):
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive = inclusive ) )
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive ) )
|
||||
|
||||
|
||||
|
||||
elif '*' in search_text:
|
||||
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive = inclusive ) )
|
||||
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive ) )
|
||||
|
||||
|
||||
|
||||
for match in matches:
|
||||
|
||||
if match.GetInclusive() != inclusive: match.SetInclusive( inclusive )
|
||||
|
||||
|
||||
try:
|
||||
|
||||
index = matches.index( entry_predicate )
|
||||
|
@ -1113,11 +1124,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
|
||||
|
||||
for match in matches:
|
||||
|
||||
if match.GetType() == HC.PREDICATE_TYPE_TAG: match.SetInclusive( inclusive )
|
||||
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
|
@ -2426,7 +2432,7 @@ class ListBox( wx.ScrolledWindow ):
|
|||
value = term.GetValue()
|
||||
|
||||
include_predicates.append( ClientSearch.Predicate( predicate_type, value ) )
|
||||
exclude_predicates.append( ClientSearch.Predicate( predicate_type, value, inclusive = False ) )
|
||||
exclude_predicates.append( ClientSearch.Predicate( predicate_type, value, False ) )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -2438,7 +2444,7 @@ class ListBox( wx.ScrolledWindow ):
|
|||
s = term
|
||||
|
||||
include_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term ) )
|
||||
exclude_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term, inclusive = False ) )
|
||||
exclude_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term, False ) )
|
||||
|
||||
|
||||
|
||||
|
@ -3307,11 +3313,15 @@ class ListBoxTagsAutocompleteDropdown( ListBoxTags ):
|
|||
|
||||
|
||||
|
||||
else: they_are_the_same = False
|
||||
else:
|
||||
|
||||
they_are_the_same = False
|
||||
|
||||
|
||||
if not they_are_the_same:
|
||||
|
||||
self._predicates = predicates
|
||||
# important to make own copy, as same object originals can be altered (e.g. set non-inclusive) in cache, and we need to notice that change just above
|
||||
self._predicates = [ predicate.GetCopy() for predicate in predicates ]
|
||||
|
||||
self._ordered_strings = []
|
||||
self._strings_to_terms = {}
|
||||
|
@ -4292,6 +4302,16 @@ class NoneableSpinCtrl( wx.Panel ):
|
|||
|
||||
|
||||
|
||||
def SetToolTipString( self, text ):
|
||||
|
||||
wx.Panel.SetToolTipString( self, text )
|
||||
|
||||
for c in self.GetChildren():
|
||||
|
||||
c.SetToolTipString( text )
|
||||
|
||||
|
||||
|
||||
def SetValue( self, value ):
|
||||
|
||||
if value is None:
|
||||
|
|
|
@ -269,7 +269,7 @@ class Dialog( wx.Dialog ):
|
|||
|
||||
( pos_x, pos_y ) = parent_tlp.GetPositionTuple()
|
||||
|
||||
pos = ( pos_x + 50, pos_y + 100 )
|
||||
pos = ( pos_x + 50, pos_y + 50 )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -316,6 +316,132 @@ class Dialog( wx.Dialog ):
|
|||
self.SetMinSize( ( min_width, min_height ) )
|
||||
|
||||
|
||||
class DialogManageApply( Dialog ):
|
||||
|
||||
def __init__( self, parent, title, dialog_key ):
|
||||
|
||||
self._dialog_key = dialog_key
|
||||
|
||||
# use the dialog key to figure out default position and size from options
|
||||
|
||||
( remember, position ) = HC.options[ 'tag_dialog_position' ]
|
||||
|
||||
if not remember:
|
||||
|
||||
position = 'topleft'
|
||||
|
||||
|
||||
Dialog.__init__( self, parent, title, position = position )
|
||||
|
||||
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
|
||||
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
|
||||
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
||||
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||||
|
||||
if action is not None:
|
||||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'ok':
|
||||
|
||||
self.EventOk( None )
|
||||
|
||||
else:
|
||||
|
||||
event.Skip()
|
||||
|
||||
|
||||
|
||||
|
||||
def EventOk( self, event ):
|
||||
|
||||
self._panel.CommitChanges()
|
||||
|
||||
# use dialog key to figure this out
|
||||
|
||||
( remember, size ) = HC.options[ 'tag_dialog_size' ]
|
||||
|
||||
current_size = self.GetSizeTuple()
|
||||
|
||||
if remember and size != current_size:
|
||||
|
||||
HC.options[ 'tag_dialog_size' ] = ( remember, current_size )
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
|
||||
|
||||
|
||||
( remember, position ) = HC.options[ 'tag_dialog_position' ]
|
||||
|
||||
current_position = self.GetPositionTuple()
|
||||
|
||||
if remember and position != current_position:
|
||||
|
||||
HC.options[ 'tag_dialog_position' ] = ( remember, current_position )
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
|
||||
|
||||
|
||||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
||||
def SetPanel( self, panel ):
|
||||
|
||||
self._panel = panel
|
||||
|
||||
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttonbox.AddF( self._apply, CC.FLAGS_MIXED )
|
||||
buttonbox.AddF( self._cancel, CC.FLAGS_MIXED )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( buttonbox, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
# use the dialog key to figure out default position and size from options
|
||||
|
||||
( remember, size ) = HC.options[ 'tag_dialog_size' ]
|
||||
|
||||
if size is not None:
|
||||
|
||||
( ideal_width, ideal_height ) = size
|
||||
|
||||
else:
|
||||
|
||||
( ideal_width, ideal_height ) = self.GetEffectiveMinSize()
|
||||
|
||||
ideal_width += 20
|
||||
ideal_height += 20
|
||||
|
||||
|
||||
( parent_window_width, parent_window_height ) = self.GetParent().GetTopLevelParent().GetSize()
|
||||
|
||||
width = min( ideal_width, parent_window_width - 100 )
|
||||
height = min( ideal_height, parent_window_height - 100 )
|
||||
|
||||
self.SetInitialSize( ( width, height ) )
|
||||
|
||||
( remember, position ) = HC.options[ 'tag_dialog_position' ]
|
||||
|
||||
if remember:
|
||||
|
||||
self.SetPosition( position )
|
||||
|
||||
|
||||
self._panel.SetupScrolling()
|
||||
|
||||
|
||||
class DialogAdvancedContentUpdate( Dialog ):
|
||||
|
||||
COPY = 0
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Crypto.PublicKey.RSA
|
||||
import ClientCaches
|
||||
import ClientConstants as CC
|
||||
import ClientData
|
||||
|
@ -4721,6 +4720,12 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
|
||||
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( self, 'max disk cache init period', none_phrase = 'do not run', min = 1, max = 120 )
|
||||
self._disk_cache_init_period.SetToolTipString( 'When the client boots, it can speed up operation by reading the front of the database into memory. This sets the max number of seconds it can spend doing that.' )
|
||||
|
||||
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( self, 'disk cache maintenance (MB)', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
|
||||
self._disk_cache_maintenance_mb.SetToolTipString( 'The client can regularly check the front of its database is cached in memory. This represents how many megabytes it will ensure are cached.' )
|
||||
|
||||
self._thumbnail_width = wx.SpinCtrl( self, min = 20, max = 200 )
|
||||
self._thumbnail_width.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
|
||||
|
||||
|
@ -4761,6 +4766,9 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
#
|
||||
|
||||
self._disk_cache_init_period.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_init_period' ) )
|
||||
self._disk_cache_maintenance_mb.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' ) )
|
||||
|
||||
( thumbnail_width, thumbnail_height ) = HC.options[ 'thumbnail_dimensions' ]
|
||||
|
||||
self._thumbnail_width.SetValue( thumbnail_width )
|
||||
|
@ -4806,6 +4814,9 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._disk_cache_init_period, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( self._disk_cache_maintenance_mb, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
|
@ -4833,10 +4844,6 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
text = 'If you want to disable automatic autocomplete results fetching, use Ctrl+Space to fetch results manually.'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
|
@ -4846,20 +4853,24 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
text = 'If you disable automatic autocomplete results fetching, use Ctrl+Space to fetch results manually.'
|
||||
|
||||
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
gridbox = wx.FlexGridSizer( 0, 2 )
|
||||
|
||||
gridbox.AddGrowableCol( 1, 1 )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete character threshold: ' ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( self._num_autocomplete_chars, CC.FLAGS_MIXED )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Automatically fetch autocomplete results after a short delay: ' ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( self._fetch_ac_results_automatically, CC.FLAGS_MIXED )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete long wait character threshold: ' ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( self._num_autocomplete_chars, CC.FLAGS_MIXED )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete long wait (ms): ' ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( self._autocomplete_long_wait, CC.FLAGS_MIXED )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait threshold: ' ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait character threshold: ' ), CC.FLAGS_MIXED )
|
||||
gridbox.AddF( self._autocomplete_short_wait_chars, CC.FLAGS_MIXED )
|
||||
|
||||
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait (ms): ' ), CC.FLAGS_MIXED )
|
||||
|
@ -4920,6 +4931,9 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
|
|||
|
||||
def UpdateOptions( self ):
|
||||
|
||||
self._new_options.SetNoneableInteger( 'disk_cache_init_period', self._disk_cache_init_period.GetValue() )
|
||||
self._new_options.SetNoneableInteger( 'disk_cache_maintenance_mb', self._disk_cache_maintenance_mb.GetValue() )
|
||||
|
||||
new_thumbnail_dimensions = [ self._thumbnail_width.GetValue(), self._thumbnail_height.GetValue() ]
|
||||
|
||||
HC.options[ 'thumbnail_dimensions' ] = new_thumbnail_dimensions
|
||||
|
@ -9004,804 +9018,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
|
||||
|
||||
class DialogManageTags( ClientGUIDialogs.Dialog ):
|
||||
|
||||
def __init__( self, parent, file_service_key, media, canvas_key = None ):
|
||||
|
||||
self._file_service_key = file_service_key
|
||||
|
||||
self._canvas_key = canvas_key
|
||||
|
||||
media = ClientMedia.FlattenMedia( media )
|
||||
|
||||
self._current_media = [ m.Duplicate() for m in media ]
|
||||
|
||||
self._hashes = set()
|
||||
|
||||
for m in self._current_media:
|
||||
|
||||
self._hashes.update( m.GetHashes() )
|
||||
|
||||
|
||||
( remember, position ) = HC.options[ 'tag_dialog_position' ]
|
||||
|
||||
if remember and position is not None:
|
||||
|
||||
my_position = 'custom'
|
||||
|
||||
wx.CallAfter( self.SetPosition, position )
|
||||
|
||||
else:
|
||||
|
||||
my_position = 'topleft'
|
||||
|
||||
|
||||
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage tags for ' + HydrusData.ConvertIntToPrettyString( len( self._hashes ) ) + ' files', position = my_position )
|
||||
|
||||
if canvas_key is not None:
|
||||
|
||||
self._next = wx.Button( self, label = '->' )
|
||||
self._next.Bind( wx.EVT_BUTTON, self.EventNext )
|
||||
|
||||
self._delete = wx.Button( self, label = 'delete' )
|
||||
self._delete.Bind( wx.EVT_BUTTON, self.EventDelete )
|
||||
|
||||
self._previous = wx.Button( self, label = '<-' )
|
||||
self._previous.Bind( wx.EVT_BUTTON, self.EventPrevious )
|
||||
|
||||
|
||||
self._tag_repositories = ClientGUICommon.ListBook( self )
|
||||
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
|
||||
|
||||
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
|
||||
self._apply.Bind( wx.EVT_BUTTON, self.EventOK )
|
||||
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
|
||||
|
||||
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
|
||||
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
|
||||
|
||||
#
|
||||
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( HC.TAG_SERVICES )
|
||||
|
||||
for service in services:
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
service_type = service.GetServiceType()
|
||||
name = service.GetName()
|
||||
|
||||
page = self._Panel( self._tag_repositories, self._file_service_key, service.GetServiceKey(), self._current_media )
|
||||
|
||||
self._tag_repositories.AddPage( name, service_key, page )
|
||||
|
||||
|
||||
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
|
||||
|
||||
self._tag_repositories.Select( default_tag_repository_key )
|
||||
|
||||
#
|
||||
|
||||
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
buttonbox.AddF( self._apply, CC.FLAGS_MIXED )
|
||||
buttonbox.AddF( self._cancel, CC.FLAGS_MIXED )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
if canvas_key is not None:
|
||||
|
||||
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
hbox.AddF( self._previous, CC.FLAGS_MIXED )
|
||||
hbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
hbox.AddF( self._delete, CC.FLAGS_MIXED )
|
||||
hbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
hbox.AddF( self._next, CC.FLAGS_MIXED )
|
||||
|
||||
vbox.AddF( hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
vbox.AddF( self._tag_repositories, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( buttonbox, CC.FLAGS_BUTTON_SIZER )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
( remember, size ) = HC.options[ 'tag_dialog_size' ]
|
||||
|
||||
if remember and size is not None:
|
||||
|
||||
self.SetInitialSize( size )
|
||||
|
||||
else:
|
||||
|
||||
( x, y ) = self.GetEffectiveMinSize()
|
||||
|
||||
( parent_window_width, parent_window_height ) = parent.GetTopLevelParent().GetSize()
|
||||
|
||||
self.SetInitialSize( ( x + 200, max( 500, parent_window_height - 200 ) ) )
|
||||
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
|
||||
self.RefreshAcceleratorTable()
|
||||
|
||||
if self._canvas_key is not None:
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
|
||||
|
||||
|
||||
|
||||
def _ClearPanels( self ):
|
||||
|
||||
for page in self._tag_repositories.GetActivePages(): page.SetMedia( set() )
|
||||
|
||||
|
||||
def _CommitCurrentChanges( self ):
|
||||
|
||||
service_keys_to_content_updates = {}
|
||||
|
||||
for page in self._tag_repositories.GetActivePages():
|
||||
|
||||
( service_key, content_updates ) = page.GetContentUpdates()
|
||||
|
||||
if len( content_updates ) > 0: service_keys_to_content_updates[ service_key ] = content_updates
|
||||
|
||||
|
||||
if len( service_keys_to_content_updates ) > 0:
|
||||
|
||||
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
|
||||
def _SetSearchFocus( self ):
|
||||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
|
||||
|
||||
def CanvasHasNewMedia( self, canvas_key, new_media ):
|
||||
|
||||
if canvas_key == self._canvas_key:
|
||||
|
||||
self._current_media = new_media
|
||||
|
||||
for page in self._tag_repositories.GetActivePages():
|
||||
|
||||
page.SetMedia( ( new_media, ) )
|
||||
|
||||
|
||||
|
||||
|
||||
def EventDelete( self, event ):
|
||||
|
||||
do_it = False
|
||||
|
||||
locations_manager = self._current_media.GetLocationsManager()
|
||||
|
||||
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
if not HC.options[ 'confirm_trash' ]:
|
||||
|
||||
do_it = True
|
||||
|
||||
|
||||
text = 'Send this file to the trash?'
|
||||
|
||||
service_key = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
|
||||
|
||||
text = 'Permanently delete this file?'
|
||||
|
||||
service_key = CC.TRASH_SERVICE_KEY
|
||||
|
||||
else:
|
||||
|
||||
return
|
||||
|
||||
|
||||
if not do_it:
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
do_it = True
|
||||
|
||||
|
||||
|
||||
|
||||
if do_it:
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'content_updates', { service_key : [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, ( self._current_media.GetHash(), ) ) ] } )
|
||||
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
||||
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||||
|
||||
if action is not None:
|
||||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'canvas_show_next': self.EventNext( event )
|
||||
elif command == 'canvas_show_previous': self.EventPrevious( event )
|
||||
elif command == 'manage_tags': self.EventOK( event )
|
||||
elif command == 'set_search_focus': self._SetSearchFocus()
|
||||
elif command == 'ok': self.EventOK( event )
|
||||
else: event.Skip()
|
||||
|
||||
|
||||
|
||||
def EventNext( self, event ):
|
||||
|
||||
if self._canvas_key is not None:
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
self._ClearPanels()
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'canvas_show_next', self._canvas_key )
|
||||
|
||||
|
||||
|
||||
def EventOK( self, event ):
|
||||
|
||||
try:
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
( remember, size ) = HC.options[ 'tag_dialog_size' ]
|
||||
|
||||
current_size = self.GetSizeTuple()
|
||||
|
||||
if remember and size != current_size:
|
||||
|
||||
HC.options[ 'tag_dialog_size' ] = ( remember, current_size )
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
|
||||
|
||||
|
||||
( remember, position ) = HC.options[ 'tag_dialog_position' ]
|
||||
|
||||
current_position = self.GetPositionTuple()
|
||||
|
||||
if remember and position != current_position:
|
||||
|
||||
HC.options[ 'tag_dialog_position' ] = ( remember, current_position )
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
|
||||
|
||||
|
||||
finally:
|
||||
|
||||
self.EndModal( wx.ID_OK )
|
||||
|
||||
|
||||
|
||||
def EventPrevious( self, event ):
|
||||
|
||||
if self._canvas_key is not None:
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
self._ClearPanels()
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'canvas_show_previous', self._canvas_key )
|
||||
|
||||
|
||||
|
||||
def EventServiceChanged( self, event ):
|
||||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
|
||||
|
||||
def RefreshAcceleratorTable( self ):
|
||||
|
||||
interested_actions = [ 'manage_tags', 'set_search_focus' ]
|
||||
|
||||
entries = []
|
||||
|
||||
for ( modifier, key_dict ) in HC.options[ 'shortcuts' ].items(): entries.extend( [ ( modifier, key, ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
|
||||
|
||||
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
|
||||
|
||||
|
||||
class _Panel( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, file_service_key, tag_service_key, media ):
|
||||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._file_service_key = file_service_key
|
||||
self._tag_service_key = tag_service_key
|
||||
|
||||
self._i_am_local_tag_service = self._tag_service_key == CC.LOCAL_TAG_SERVICE_KEY
|
||||
|
||||
if not self._i_am_local_tag_service:
|
||||
|
||||
service = HydrusGlobals.client_controller.GetServicesManager().GetService( tag_service_key )
|
||||
|
||||
try: self._account = service.GetInfo( 'account' )
|
||||
except: self._account = HydrusData.GetUnknownAccount()
|
||||
|
||||
|
||||
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
|
||||
|
||||
self._tags_box = ClientGUICommon.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTags )
|
||||
|
||||
self._tags_box_sorter.SetTagsBox( self._tags_box )
|
||||
|
||||
self._collapse_siblings_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'auto-replace entered siblings' )
|
||||
self._collapse_siblings_checkbox.SetValue( True )
|
||||
|
||||
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
|
||||
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
|
||||
|
||||
self._tags_box_sorter.AddF( self._collapse_siblings_checkbox, CC.FLAGS_LONE_BUTTON )
|
||||
self._tags_box_sorter.AddF( self._show_deleted_checkbox, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTags, expand_parents, self._file_service_key, self._tag_service_key, null_entry_callable = self.Ok )
|
||||
|
||||
self._advanced_content_update_button = wx.Button( self, label = 'advanced operation' )
|
||||
self._advanced_content_update_button.Bind( wx.EVT_BUTTON, self.EventAdvancedContentUpdate )
|
||||
|
||||
self._modify_mappers = wx.Button( self, label = 'modify mappers' )
|
||||
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
|
||||
|
||||
self._copy_tags = wx.Button( self, label = 'copy tags' )
|
||||
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
|
||||
|
||||
self._paste_tags = wx.Button( self, label = 'paste tags' )
|
||||
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
text = 'remove all tags'
|
||||
|
||||
else:
|
||||
|
||||
text = 'petition all tags'
|
||||
|
||||
|
||||
self._remove_tags = wx.Button( self, label = text )
|
||||
self._remove_tags.Bind( wx.EVT_BUTTON, self.EventRemoveTags )
|
||||
|
||||
self._tags_box.ChangeTagService( self._tag_service_key )
|
||||
|
||||
self.SetMedia( media )
|
||||
|
||||
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
self._modify_mappers.Hide()
|
||||
|
||||
else:
|
||||
|
||||
if not self._account.HasPermission( HC.MANAGE_USERS ):
|
||||
|
||||
self._modify_mappers.Hide()
|
||||
|
||||
|
||||
|
||||
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
copy_paste_hbox.AddF( self._copy_tags, CC.FLAGS_MIXED )
|
||||
copy_paste_hbox.AddF( self._paste_tags, CC.FLAGS_MIXED )
|
||||
copy_paste_hbox.AddF( self._remove_tags, CC.FLAGS_MIXED )
|
||||
copy_paste_hbox.AddF( self._advanced_content_update_button, CC.FLAGS_MIXED )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._tags_box_sorter, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._add_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( copy_paste_hbox, CC.FLAGS_BUTTON_SIZER )
|
||||
vbox.AddF( self._modify_mappers, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _AddTags( self, tags, only_add = False, only_remove = False, forced_reason = None ):
|
||||
|
||||
if not self._i_am_local_tag_service and self._account.HasPermission( HC.RESOLVE_PETITIONS ):
|
||||
|
||||
forced_reason = 'admin'
|
||||
|
||||
|
||||
collapse_siblings = self._collapse_siblings_checkbox.GetValue()
|
||||
|
||||
tag_managers = [ m.GetTagsManager() for m in self._media ]
|
||||
|
||||
num_files = len( self._media )
|
||||
|
||||
sets_of_choices = []
|
||||
|
||||
potential_num_reasons_needed = 0
|
||||
|
||||
for tag in tags:
|
||||
|
||||
if collapse_siblings:
|
||||
|
||||
sibling_tag = HydrusGlobals.client_controller.GetManager( 'tag_siblings' ).GetSibling( tag )
|
||||
|
||||
if sibling_tag is not None:
|
||||
|
||||
tag = sibling_tag
|
||||
|
||||
|
||||
|
||||
num_current = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetCurrent( self._tag_service_key ) ] )
|
||||
|
||||
choices = []
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
if not only_remove:
|
||||
|
||||
if num_current < num_files:
|
||||
|
||||
choices.append( ( 'add ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - num_current ) + ' files', ( HC.CONTENT_UPDATE_ADD, tag ) ) )
|
||||
|
||||
|
||||
|
||||
if not only_add:
|
||||
|
||||
if num_current > 0:
|
||||
|
||||
choices.append( ( 'delete ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current ) + ' files', ( HC.CONTENT_UPDATE_DELETE, tag ) ) )
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
num_pending = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPending( self._tag_service_key ) ] )
|
||||
num_petitioned = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPetitioned( self._tag_service_key ) ] )
|
||||
|
||||
if not only_remove:
|
||||
|
||||
if num_current + num_pending < num_files: choices.append( ( 'pend ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - ( num_current + num_pending ) ) + ' files', ( HC.CONTENT_UPDATE_PEND, tag ) ) )
|
||||
|
||||
|
||||
if not only_add:
|
||||
|
||||
if num_current > num_petitioned and not only_add:
|
||||
|
||||
choices.append( ( 'petition ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current - num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_PETITION, tag ) ) )
|
||||
|
||||
potential_num_reasons_needed += 1
|
||||
|
||||
|
||||
if num_pending > 0 and not only_add:
|
||||
|
||||
choices.append( ( 'rescind pending ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_pending ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PEND, tag ) ) )
|
||||
|
||||
|
||||
|
||||
if not only_remove:
|
||||
|
||||
if num_petitioned > 0:
|
||||
|
||||
choices.append( ( 'rescind petitioned ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PETITION, tag ) ) )
|
||||
|
||||
|
||||
|
||||
|
||||
if len( choices ) == 0:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
sets_of_choices.append( choices )
|
||||
|
||||
|
||||
if forced_reason is None and potential_num_reasons_needed > 1:
|
||||
|
||||
no_user_choices = True not in ( len( choices ) > 1 for choices in sets_of_choices )
|
||||
|
||||
if no_user_choices:
|
||||
|
||||
message = 'You are about to petition more than one tag.'
|
||||
|
||||
else:
|
||||
|
||||
message = 'You might be about to petition more than one tag.'
|
||||
|
||||
|
||||
message += os.linesep * 2
|
||||
message += 'To save you time, would you like to use the same reason for all the petitions?'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Many petitions found' ) as yn_dlg:
|
||||
|
||||
if yn_dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
message = 'Please enter your common petition reason here:'
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as text_dlg:
|
||||
|
||||
if text_dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
forced_reason = text_dlg.GetValue()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
forced_choice_actions = []
|
||||
|
||||
for choices in sets_of_choices:
|
||||
|
||||
always_do = False
|
||||
|
||||
if len( choices ) == 1:
|
||||
|
||||
[ ( text_gumpf, choice ) ] = choices
|
||||
|
||||
else:
|
||||
|
||||
choice = None
|
||||
|
||||
for forced_choice_action in forced_choice_actions:
|
||||
|
||||
for possible_choice in choices:
|
||||
|
||||
( text_gumpf, ( choice_action, choice_tag ) ) = possible_choice
|
||||
|
||||
if choice_action == forced_choice_action:
|
||||
|
||||
choice = ( choice_action, choice_tag )
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
if choice is not None:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
if choice is None:
|
||||
|
||||
intro = 'What would you like to do?'
|
||||
|
||||
show_always_checkbox = len( sets_of_choices ) > 1
|
||||
|
||||
with ClientGUIDialogs.DialogButtonChoice( self, intro, choices, show_always_checkbox = show_always_checkbox ) as dlg:
|
||||
|
||||
result = dlg.ShowModal()
|
||||
|
||||
if result == wx.ID_OK:
|
||||
|
||||
( always_do, choice ) = dlg.GetData()
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if choice is None:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
( choice_action, choice_tag ) = choice
|
||||
|
||||
if always_do:
|
||||
|
||||
forced_choice_actions.append( choice_action )
|
||||
|
||||
|
||||
if choice_action == HC.CONTENT_UPDATE_ADD: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_DELETE: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_PEND: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPending( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PEND: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPending( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
|
||||
|
||||
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media_to_affect ) ) )
|
||||
|
||||
content_updates = []
|
||||
|
||||
if choice_action == HC.CONTENT_UPDATE_PETITION:
|
||||
|
||||
if forced_reason is None:
|
||||
|
||||
message = 'Enter a reason for ' + choice_tag + ' to be removed. A janitor will review your petition.'
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
reason = dlg.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
reason = forced_reason
|
||||
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes, reason ) ) )
|
||||
|
||||
else:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes ) ) )
|
||||
|
||||
|
||||
if choice_action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_PEND ):
|
||||
|
||||
tag_parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
|
||||
|
||||
parents = tag_parents_manager.GetParents( self._tag_service_key, choice_tag )
|
||||
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( parent, hashes ) ) for parent in parents ) )
|
||||
|
||||
|
||||
for m in self._media:
|
||||
|
||||
for content_update in content_updates:
|
||||
|
||||
m.GetMediaResult().ProcessContentUpdate( self._tag_service_key, content_update )
|
||||
|
||||
|
||||
|
||||
self._content_updates.extend( content_updates )
|
||||
|
||||
|
||||
self._tags_box.SetTagsByMedia( self._media, force_reload = True )
|
||||
|
||||
|
||||
def AddTags( self, tags ):
|
||||
|
||||
if len( tags ) > 0:
|
||||
|
||||
self._AddTags( tags )
|
||||
|
||||
|
||||
|
||||
def EventAdvancedContentUpdate( self, event ):
|
||||
|
||||
hashes = set()
|
||||
|
||||
for m in self._media:
|
||||
|
||||
hashes.update( m.GetHashes() )
|
||||
|
||||
|
||||
self.GetParent().GetParent().EventOK( event )
|
||||
|
||||
with ClientGUIDialogs.DialogAdvancedContentUpdate( self, self._tag_service_key, hashes ) as dlg:
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
|
||||
def EventCopyTags( self, event ):
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( self._media, tag_service_key = self._tag_service_key, collapse_siblings = False )
|
||||
|
||||
tags = set( current_tags_to_count.keys() ).union( pending_tags_to_count.keys() )
|
||||
|
||||
text = os.linesep.join( tags )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
def EventModify( self, event ):
|
||||
|
||||
contents = []
|
||||
|
||||
tags = self._tags_box.GetSelectedTags()
|
||||
|
||||
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in self._media ) ) )
|
||||
|
||||
for tag in tags:
|
||||
|
||||
contents.extend( [ HydrusData.Content( HC.CONTENT_TYPE_MAPPING, ( tag, hash ) ) for hash in hashes ] )
|
||||
|
||||
|
||||
if len( contents ) > 0:
|
||||
|
||||
subject_identifiers = [ HydrusData.AccountIdentifier( content = content ) for content in contents ]
|
||||
|
||||
with ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_key, subject_identifiers ) as dlg: dlg.ShowModal()
|
||||
|
||||
|
||||
|
||||
def EventPasteTags( self, event ):
|
||||
|
||||
if wx.TheClipboard.Open():
|
||||
|
||||
data = wx.TextDataObject()
|
||||
|
||||
wx.TheClipboard.GetData( data )
|
||||
|
||||
wx.TheClipboard.Close()
|
||||
|
||||
text = data.GetText()
|
||||
|
||||
try:
|
||||
|
||||
tags = HydrusData.DeserialisePrettyTags( text )
|
||||
|
||||
tags = HydrusTags.CleanTags( tags )
|
||||
|
||||
self._AddTags( tags, only_add = True )
|
||||
|
||||
except: wx.MessageBox( 'I could not understand what was in the clipboard' )
|
||||
|
||||
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||||
|
||||
|
||||
def EventRemoveTags( self, event ):
|
||||
|
||||
tag_managers = [ m.GetTagsManager() for m in self._media ]
|
||||
|
||||
removable_tags = set()
|
||||
|
||||
for tag_manager in tag_managers:
|
||||
|
||||
removable_tags.update( tag_manager.GetCurrent( self._tag_service_key ) )
|
||||
removable_tags.update( tag_manager.GetPending( self._tag_service_key ) )
|
||||
|
||||
|
||||
self._AddTags( removable_tags, only_remove = True )
|
||||
|
||||
|
||||
def EventShowDeleted( self, event ):
|
||||
|
||||
self._tags_box.SetShow( 'deleted', self._show_deleted_checkbox.GetValue() )
|
||||
|
||||
|
||||
def GetContentUpdates( self ): return ( self._tag_service_key, self._content_updates )
|
||||
|
||||
def GetServiceKey( self ): return self._tag_service_key
|
||||
|
||||
def HasChanges( self ): return len( self._content_updates ) > 0
|
||||
|
||||
def Ok( self ):
|
||||
|
||||
wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
|
||||
|
||||
|
||||
def SetMedia( self, media ):
|
||||
|
||||
self._content_updates = []
|
||||
|
||||
if media is None:
|
||||
|
||||
media = []
|
||||
|
||||
|
||||
self._media = media
|
||||
|
||||
self._tags_box.SetTagsByMedia( self._media )
|
||||
|
||||
|
||||
def SetTagBoxFocus( self ):
|
||||
|
||||
self._add_tag_box.SetFocus()
|
||||
|
||||
|
||||
|
||||
class DialogManageUPnP( ClientGUIDialogs.Dialog ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
|
|
|
@ -7,6 +7,7 @@ import ClientGUICommon
|
|||
import ClientGUIDialogs
|
||||
import ClientGUIDialogsManage
|
||||
import ClientGUICanvas
|
||||
import ClientGUIPanels
|
||||
import ClientMedia
|
||||
import collections
|
||||
import HydrusExceptions
|
||||
|
@ -779,7 +780,19 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
|
|||
|
||||
if len( self._selected_media ) > 0:
|
||||
|
||||
with ClientGUIDialogsManage.DialogManageTags( self.GetTopLevelParent(), self._file_service_key, self._selected_media ) as dlg: dlg.ShowModal()
|
||||
num_files = self._GetNumSelected()
|
||||
|
||||
title = 'manage tags for ' + HydrusData.ConvertIntToPrettyString( num_files ) + ' files'
|
||||
dialog_key = 'manage_tags'
|
||||
|
||||
with ClientGUIDialogs.DialogManageApply( self, title, dialog_key ) as dlg:
|
||||
|
||||
panel = ClientGUIPanels.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
self.SetFocus()
|
||||
|
||||
|
|
|
@ -0,0 +1,667 @@
|
|||
import ClientCaches
|
||||
import ClientConstants as CC
|
||||
import ClientData
|
||||
import ClientGUICommon
|
||||
import ClientGUIDialogs
|
||||
import ClientMedia
|
||||
import collections
|
||||
import HydrusConstants as HC
|
||||
import HydrusData
|
||||
import HydrusExceptions
|
||||
import HydrusGlobals
|
||||
import HydrusSerialisable
|
||||
import HydrusTags
|
||||
import itertools
|
||||
import os
|
||||
import string
|
||||
import time
|
||||
import traceback
|
||||
import wx
|
||||
import wx.lib.scrolledpanel
|
||||
|
||||
class ManageTagsPanel( wx.lib.scrolledpanel.ScrolledPanel ):
|
||||
|
||||
def __init__( self, parent, file_service_key, media, canvas_key = None ):
|
||||
|
||||
wx.lib.scrolledpanel.ScrolledPanel.__init__( self, parent )
|
||||
|
||||
self._file_service_key = file_service_key
|
||||
|
||||
self._canvas_key = canvas_key
|
||||
|
||||
media = ClientMedia.FlattenMedia( media )
|
||||
|
||||
self._current_media = [ m.Duplicate() for m in media ]
|
||||
|
||||
self._hashes = set()
|
||||
|
||||
for m in self._current_media:
|
||||
|
||||
self._hashes.update( m.GetHashes() )
|
||||
|
||||
|
||||
self._tag_repositories = ClientGUICommon.ListBook( self )
|
||||
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
|
||||
|
||||
#
|
||||
|
||||
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( HC.TAG_SERVICES )
|
||||
|
||||
for service in services:
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
name = service.GetName()
|
||||
|
||||
page = self._Panel( self._tag_repositories, self._file_service_key, service.GetServiceKey(), self._current_media )
|
||||
|
||||
self._tag_repositories.AddPage( name, service_key, page )
|
||||
|
||||
|
||||
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
|
||||
|
||||
self._tag_repositories.Select( default_tag_repository_key )
|
||||
|
||||
#
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._tag_repositories, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
self.Bind( wx.EVT_MENU, self.EventMenu )
|
||||
|
||||
self.RefreshAcceleratorTable()
|
||||
|
||||
if self._canvas_key is not None:
|
||||
|
||||
HydrusGlobals.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
|
||||
|
||||
|
||||
|
||||
def _CommitCurrentChanges( self ):
|
||||
|
||||
service_keys_to_content_updates = {}
|
||||
|
||||
for page in self._tag_repositories.GetActivePages():
|
||||
|
||||
( service_key, content_updates ) = page.GetContentUpdates()
|
||||
|
||||
if len( content_updates ) > 0: service_keys_to_content_updates[ service_key ] = content_updates
|
||||
|
||||
|
||||
if len( service_keys_to_content_updates ) > 0:
|
||||
|
||||
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
|
||||
|
||||
|
||||
|
||||
def _SetSearchFocus( self ):
|
||||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
page.SetTagBoxFocus()
|
||||
|
||||
|
||||
def CanvasHasNewMedia( self, canvas_key, new_media_singleton ):
|
||||
|
||||
if canvas_key == self._canvas_key:
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
self._current_media = ( new_media_singleton.Duplicate(), )
|
||||
|
||||
for page in self._tag_repositories.GetActivePages():
|
||||
|
||||
page.SetMedia( self._current_media )
|
||||
|
||||
|
||||
|
||||
|
||||
def CommitChanges( self ):
|
||||
|
||||
self._CommitCurrentChanges()
|
||||
|
||||
|
||||
def EventMenu( self, event ):
|
||||
|
||||
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
||||
|
||||
if action is not None:
|
||||
|
||||
( command, data ) = action
|
||||
|
||||
if command == 'manage_tags':
|
||||
|
||||
wx.PostEvent( self.GetParent(), wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
|
||||
|
||||
elif command == 'set_search_focus': self._SetSearchFocus()
|
||||
else: event.Skip()
|
||||
|
||||
|
||||
|
||||
def EventServiceChanged( self, event ):
|
||||
|
||||
page = self._tag_repositories.GetCurrentPage()
|
||||
|
||||
wx.CallAfter( page.SetTagBoxFocus )
|
||||
|
||||
|
||||
def RefreshAcceleratorTable( self ):
|
||||
|
||||
interested_actions = [ 'manage_tags', 'set_search_focus' ]
|
||||
|
||||
entries = []
|
||||
|
||||
for ( modifier, key_dict ) in HC.options[ 'shortcuts' ].items():
|
||||
|
||||
entries.extend( [ ( modifier, key, ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
|
||||
|
||||
|
||||
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
|
||||
|
||||
|
||||
class _Panel( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, file_service_key, tag_service_key, media ):
|
||||
|
||||
wx.Panel.__init__( self, parent )
|
||||
|
||||
self._file_service_key = file_service_key
|
||||
self._tag_service_key = tag_service_key
|
||||
|
||||
self._i_am_local_tag_service = self._tag_service_key == CC.LOCAL_TAG_SERVICE_KEY
|
||||
|
||||
if not self._i_am_local_tag_service:
|
||||
|
||||
service = HydrusGlobals.client_controller.GetServicesManager().GetService( tag_service_key )
|
||||
|
||||
try: self._account = service.GetInfo( 'account' )
|
||||
except: self._account = HydrusData.GetUnknownAccount()
|
||||
|
||||
|
||||
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
|
||||
|
||||
self._tags_box = ClientGUICommon.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTags )
|
||||
|
||||
self._tags_box_sorter.SetTagsBox( self._tags_box )
|
||||
|
||||
self._new_options = HydrusGlobals.client_controller.GetNewOptions()
|
||||
|
||||
self._collapse_siblings_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'auto-replace entered siblings' )
|
||||
self._collapse_siblings_checkbox.SetValue( self._new_options.GetBoolean( 'replace_siblings_on_manage_tags' ) )
|
||||
self._collapse_siblings_checkbox.Bind( wx.EVT_CHECKBOX, self.EventCheckCollapseSiblings )
|
||||
|
||||
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
|
||||
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
|
||||
|
||||
self._tags_box_sorter.AddF( self._collapse_siblings_checkbox, CC.FLAGS_LONE_BUTTON )
|
||||
self._tags_box_sorter.AddF( self._show_deleted_checkbox, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
expand_parents = True
|
||||
|
||||
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTags, expand_parents, self._file_service_key, self._tag_service_key, null_entry_callable = self.Ok )
|
||||
|
||||
self._advanced_content_update_button = wx.Button( self, label = 'advanced operation' )
|
||||
self._advanced_content_update_button.Bind( wx.EVT_BUTTON, self.EventAdvancedContentUpdate )
|
||||
|
||||
self._modify_mappers = wx.Button( self, label = 'modify mappers' )
|
||||
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
|
||||
|
||||
self._copy_tags = wx.Button( self, label = 'copy tags' )
|
||||
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
|
||||
|
||||
self._paste_tags = wx.Button( self, label = 'paste tags' )
|
||||
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
text = 'remove all tags'
|
||||
|
||||
else:
|
||||
|
||||
text = 'petition all tags'
|
||||
|
||||
|
||||
self._remove_tags = wx.Button( self, label = text )
|
||||
self._remove_tags.Bind( wx.EVT_BUTTON, self.EventRemoveTags )
|
||||
|
||||
self._tags_box.ChangeTagService( self._tag_service_key )
|
||||
|
||||
self.SetMedia( media )
|
||||
|
||||
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
self._modify_mappers.Hide()
|
||||
|
||||
else:
|
||||
|
||||
if not self._account.HasPermission( HC.MANAGE_USERS ):
|
||||
|
||||
self._modify_mappers.Hide()
|
||||
|
||||
|
||||
|
||||
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
copy_paste_hbox.AddF( self._copy_tags, CC.FLAGS_MIXED )
|
||||
copy_paste_hbox.AddF( self._paste_tags, CC.FLAGS_MIXED )
|
||||
copy_paste_hbox.AddF( self._remove_tags, CC.FLAGS_MIXED )
|
||||
copy_paste_hbox.AddF( self._advanced_content_update_button, CC.FLAGS_MIXED )
|
||||
|
||||
vbox = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
vbox.AddF( self._tags_box_sorter, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.AddF( self._add_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
vbox.AddF( copy_paste_hbox, CC.FLAGS_BUTTON_SIZER )
|
||||
vbox.AddF( self._modify_mappers, CC.FLAGS_LONE_BUTTON )
|
||||
|
||||
self.SetSizer( vbox )
|
||||
|
||||
|
||||
def _AddTags( self, tags, only_add = False, only_remove = False, forced_reason = None ):
|
||||
|
||||
if not self._i_am_local_tag_service and self._account.HasPermission( HC.RESOLVE_PETITIONS ):
|
||||
|
||||
forced_reason = 'admin'
|
||||
|
||||
|
||||
collapse_siblings = self._collapse_siblings_checkbox.GetValue()
|
||||
|
||||
tag_managers = [ m.GetTagsManager() for m in self._media ]
|
||||
|
||||
num_files = len( self._media )
|
||||
|
||||
sets_of_choices = []
|
||||
|
||||
potential_num_reasons_needed = 0
|
||||
|
||||
for tag in tags:
|
||||
|
||||
if collapse_siblings:
|
||||
|
||||
sibling_tag = HydrusGlobals.client_controller.GetManager( 'tag_siblings' ).GetSibling( tag )
|
||||
|
||||
if sibling_tag is not None:
|
||||
|
||||
tag = sibling_tag
|
||||
|
||||
|
||||
|
||||
num_current = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetCurrent( self._tag_service_key ) ] )
|
||||
|
||||
choices = []
|
||||
|
||||
if self._i_am_local_tag_service:
|
||||
|
||||
if not only_remove:
|
||||
|
||||
if num_current < num_files:
|
||||
|
||||
choices.append( ( 'add ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - num_current ) + ' files', ( HC.CONTENT_UPDATE_ADD, tag ) ) )
|
||||
|
||||
|
||||
|
||||
if not only_add:
|
||||
|
||||
if num_current > 0:
|
||||
|
||||
choices.append( ( 'delete ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current ) + ' files', ( HC.CONTENT_UPDATE_DELETE, tag ) ) )
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
num_pending = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPending( self._tag_service_key ) ] )
|
||||
num_petitioned = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPetitioned( self._tag_service_key ) ] )
|
||||
|
||||
if not only_remove:
|
||||
|
||||
if num_current + num_pending < num_files: choices.append( ( 'pend ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - ( num_current + num_pending ) ) + ' files', ( HC.CONTENT_UPDATE_PEND, tag ) ) )
|
||||
|
||||
|
||||
if not only_add:
|
||||
|
||||
if num_current > num_petitioned and not only_add:
|
||||
|
||||
choices.append( ( 'petition ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current - num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_PETITION, tag ) ) )
|
||||
|
||||
potential_num_reasons_needed += 1
|
||||
|
||||
|
||||
if num_pending > 0 and not only_add:
|
||||
|
||||
choices.append( ( 'rescind pending ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_pending ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PEND, tag ) ) )
|
||||
|
||||
|
||||
|
||||
if not only_remove:
|
||||
|
||||
if num_petitioned > 0:
|
||||
|
||||
choices.append( ( 'rescind petitioned ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PETITION, tag ) ) )
|
||||
|
||||
|
||||
|
||||
|
||||
if len( choices ) == 0:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
sets_of_choices.append( choices )
|
||||
|
||||
|
||||
if forced_reason is None and potential_num_reasons_needed > 1:
|
||||
|
||||
no_user_choices = True not in ( len( choices ) > 1 for choices in sets_of_choices )
|
||||
|
||||
if no_user_choices:
|
||||
|
||||
message = 'You are about to petition more than one tag.'
|
||||
|
||||
else:
|
||||
|
||||
message = 'You might be about to petition more than one tag.'
|
||||
|
||||
|
||||
message += os.linesep * 2
|
||||
message += 'To save you time, would you like to use the same reason for all the petitions?'
|
||||
|
||||
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Many petitions found' ) as yn_dlg:
|
||||
|
||||
if yn_dlg.ShowModal() == wx.ID_YES:
|
||||
|
||||
message = 'Please enter your common petition reason here:'
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as text_dlg:
|
||||
|
||||
if text_dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
forced_reason = text_dlg.GetValue()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
forced_choice_actions = []
|
||||
|
||||
for choices in sets_of_choices:
|
||||
|
||||
always_do = False
|
||||
|
||||
if len( choices ) == 1:
|
||||
|
||||
[ ( text_gumpf, choice ) ] = choices
|
||||
|
||||
else:
|
||||
|
||||
choice = None
|
||||
|
||||
for forced_choice_action in forced_choice_actions:
|
||||
|
||||
for possible_choice in choices:
|
||||
|
||||
( text_gumpf, ( choice_action, choice_tag ) ) = possible_choice
|
||||
|
||||
if choice_action == forced_choice_action:
|
||||
|
||||
choice = ( choice_action, choice_tag )
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
if choice is not None:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
if choice is None:
|
||||
|
||||
intro = 'What would you like to do?'
|
||||
|
||||
show_always_checkbox = len( sets_of_choices ) > 1
|
||||
|
||||
with ClientGUIDialogs.DialogButtonChoice( self, intro, choices, show_always_checkbox = show_always_checkbox ) as dlg:
|
||||
|
||||
result = dlg.ShowModal()
|
||||
|
||||
if result == wx.ID_OK:
|
||||
|
||||
( always_do, choice ) = dlg.GetData()
|
||||
|
||||
else:
|
||||
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if choice is None:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
( choice_action, choice_tag ) = choice
|
||||
|
||||
if always_do:
|
||||
|
||||
forced_choice_actions.append( choice_action )
|
||||
|
||||
|
||||
if choice_action == HC.CONTENT_UPDATE_ADD: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_DELETE: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_PEND: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPending( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PEND: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPending( self._tag_service_key ) )
|
||||
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
|
||||
|
||||
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media_to_affect ) ) )
|
||||
|
||||
content_updates = []
|
||||
|
||||
if choice_action == HC.CONTENT_UPDATE_PETITION:
|
||||
|
||||
if forced_reason is None:
|
||||
|
||||
message = 'Enter a reason for ' + choice_tag + ' to be removed. A janitor will review your petition.'
|
||||
|
||||
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
|
||||
reason = dlg.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
|
||||
|
||||
else:
|
||||
|
||||
reason = forced_reason
|
||||
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes, reason ) ) )
|
||||
|
||||
else:
|
||||
|
||||
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes ) ) )
|
||||
|
||||
|
||||
if choice_action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_PEND ):
|
||||
|
||||
tag_parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
|
||||
|
||||
parents = tag_parents_manager.GetParents( self._tag_service_key, choice_tag )
|
||||
|
||||
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( parent, hashes ) ) for parent in parents ) )
|
||||
|
||||
|
||||
for m in self._media:
|
||||
|
||||
for content_update in content_updates:
|
||||
|
||||
m.GetMediaResult().ProcessContentUpdate( self._tag_service_key, content_update )
|
||||
|
||||
|
||||
|
||||
self._content_updates.extend( content_updates )
|
||||
|
||||
|
||||
self._tags_box.SetTagsByMedia( self._media, force_reload = True )
|
||||
|
||||
|
||||
def AddTags( self, tags ):
|
||||
|
||||
if len( tags ) > 0:
|
||||
|
||||
self._AddTags( tags )
|
||||
|
||||
|
||||
|
||||
def EventAdvancedContentUpdate( self, event ):
|
||||
|
||||
hashes = set()
|
||||
|
||||
for m in self._media:
|
||||
|
||||
hashes.update( m.GetHashes() )
|
||||
|
||||
|
||||
self.Ok()
|
||||
|
||||
def do_it():
|
||||
|
||||
with ClientGUIDialogs.DialogAdvancedContentUpdate( self, self._tag_service_key, hashes ) as dlg:
|
||||
|
||||
dlg.ShowModal()
|
||||
|
||||
|
||||
|
||||
wx.CallAfter( do_it )
|
||||
|
||||
|
||||
def EventCheckCollapseSiblings( self, event ):
|
||||
|
||||
self._new_options.SetBoolean( 'replace_siblings_on_manage_tags', self._collapse_siblings_checkbox.GetValue() )
|
||||
|
||||
HydrusGlobals.client_controller.Write( 'serialisable', self._new_options )
|
||||
|
||||
|
||||
def EventCopyTags( self, event ):
|
||||
|
||||
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( self._media, tag_service_key = self._tag_service_key, collapse_siblings = False )
|
||||
|
||||
tags = set( current_tags_to_count.keys() ).union( pending_tags_to_count.keys() )
|
||||
|
||||
text = os.linesep.join( tags )
|
||||
|
||||
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
|
||||
|
||||
|
||||
def EventModify( self, event ):
|
||||
|
||||
contents = []
|
||||
|
||||
tags = self._tags_box.GetSelectedTags()
|
||||
|
||||
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in self._media ) ) )
|
||||
|
||||
for tag in tags:
|
||||
|
||||
contents.extend( [ HydrusData.Content( HC.CONTENT_TYPE_MAPPING, ( tag, hash ) ) for hash in hashes ] )
|
||||
|
||||
|
||||
if len( contents ) > 0:
|
||||
|
||||
subject_identifiers = [ HydrusData.AccountIdentifier( content = content ) for content in contents ]
|
||||
|
||||
with ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_key, subject_identifiers ) as dlg: dlg.ShowModal()
|
||||
|
||||
|
||||
|
||||
def EventPasteTags( self, event ):
|
||||
|
||||
if wx.TheClipboard.Open():
|
||||
|
||||
data = wx.TextDataObject()
|
||||
|
||||
wx.TheClipboard.GetData( data )
|
||||
|
||||
wx.TheClipboard.Close()
|
||||
|
||||
text = data.GetText()
|
||||
|
||||
try:
|
||||
|
||||
tags = HydrusData.DeserialisePrettyTags( text )
|
||||
|
||||
tags = HydrusTags.CleanTags( tags )
|
||||
|
||||
self._AddTags( tags, only_add = True )
|
||||
|
||||
except: wx.MessageBox( 'I could not understand what was in the clipboard' )
|
||||
|
||||
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
||||
|
||||
|
||||
def EventRemoveTags( self, event ):
|
||||
|
||||
tag_managers = [ m.GetTagsManager() for m in self._media ]
|
||||
|
||||
removable_tags = set()
|
||||
|
||||
for tag_manager in tag_managers:
|
||||
|
||||
removable_tags.update( tag_manager.GetCurrent( self._tag_service_key ) )
|
||||
removable_tags.update( tag_manager.GetPending( self._tag_service_key ) )
|
||||
|
||||
|
||||
self._AddTags( removable_tags, only_remove = True )
|
||||
|
||||
|
||||
def EventShowDeleted( self, event ):
|
||||
|
||||
self._tags_box.SetShow( 'deleted', self._show_deleted_checkbox.GetValue() )
|
||||
|
||||
|
||||
def GetContentUpdates( self ): return ( self._tag_service_key, self._content_updates )
|
||||
|
||||
def GetServiceKey( self ): return self._tag_service_key
|
||||
|
||||
def HasChanges( self ): return len( self._content_updates ) > 0
|
||||
|
||||
def Ok( self ):
|
||||
|
||||
wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
|
||||
|
||||
|
||||
def SetMedia( self, media ):
|
||||
|
||||
self._content_updates = []
|
||||
|
||||
if media is None:
|
||||
|
||||
media = []
|
||||
|
||||
|
||||
self._media = media
|
||||
|
||||
self._tags_box.SetTagsByMedia( self._media )
|
||||
|
||||
|
||||
def SetTagBoxFocus( self ):
|
||||
|
||||
self._add_tag_box.SetFocus()
|
||||
|
||||
|
||||
|
|
@ -9,7 +9,6 @@ import HydrusGlobals
|
|||
import HydrusThreading
|
||||
import HydrusVideoHandling
|
||||
import lz4
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import wx
|
||||
|
|
|
@ -628,9 +628,7 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
|
|||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PREDICATE
|
||||
SERIALISABLE_VERSION = 1
|
||||
|
||||
def __init__( self, predicate_type = None, value = None, inclusive = True, counts = None ):
|
||||
|
||||
if counts is None: counts = {}
|
||||
def __init__( self, predicate_type = None, value = None, inclusive = True, min_current_count = 0, min_pending_count = 0, max_current_count = None, max_pending_count = None ):
|
||||
|
||||
if isinstance( value, list ):
|
||||
|
||||
|
@ -641,12 +639,11 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
|
|||
self._value = value
|
||||
|
||||
self._inclusive = inclusive
|
||||
self._counts = {}
|
||||
|
||||
self._counts[ HC.CURRENT ] = 0
|
||||
self._counts[ HC.PENDING ] = 0
|
||||
|
||||
for ( current_or_pending, count ) in counts.items(): self.AddToCount( current_or_pending, count )
|
||||
self._min_current_count = min_current_count
|
||||
self._min_pending_count = min_pending_count
|
||||
self._max_current_count = max_current_count
|
||||
self._max_pending_count = max_pending_count
|
||||
|
||||
|
||||
def __eq__( self, other ):
|
||||
|
@ -666,7 +663,7 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
def __repr__( self ):
|
||||
|
||||
return 'Predicate: ' + HydrusData.ToUnicode( ( self._predicate_type, self._value, self._inclusive, self._counts ) )
|
||||
return 'Predicate: ' + HydrusData.ToUnicode( ( self._predicate_type, self._value, self._inclusive, self.GetCount() ) )
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
@ -730,16 +727,75 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
|
||||
def AddToCount( self, current_or_pending, count ): self._counts[ current_or_pending ] += count
|
||||
def AddToCount( self, current_or_pending, count ):
|
||||
|
||||
if count == 0:
|
||||
|
||||
return
|
||||
|
||||
|
||||
if current_or_pending == HC.CURRENT:
|
||||
|
||||
if self._min_current_count == 0:
|
||||
|
||||
self._min_current_count = count
|
||||
|
||||
else:
|
||||
|
||||
if self._max_current_count is None:
|
||||
|
||||
self._max_current_count = self._min_current_count
|
||||
|
||||
|
||||
self._max_current_count += count
|
||||
|
||||
if count > self._min_current_count:
|
||||
|
||||
self._min_current_count = count
|
||||
|
||||
|
||||
|
||||
elif current_or_pending == HC.PENDING:
|
||||
|
||||
if self._min_pending_count == 0:
|
||||
|
||||
self._min_pending_count = count
|
||||
|
||||
else:
|
||||
|
||||
if self._max_pending_count is None:
|
||||
|
||||
self._max_pending_count = self._min_pending_count
|
||||
|
||||
|
||||
self._max_pending_count += count
|
||||
|
||||
if count > self._min_pending_count:
|
||||
|
||||
self._min_pending_count = count
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def GetCopy( self ): return Predicate( self._predicate_type, self._value, self._inclusive, self._counts )
|
||||
def GetCopy( self ): return Predicate( self._predicate_type, self._value, self._inclusive, self._min_current_count, self._min_pending_count, self._max_current_count, self._max_pending_count )
|
||||
|
||||
def GetCountlessCopy( self ): return Predicate( self._predicate_type, self._value, self._inclusive )
|
||||
|
||||
def GetCount( self, current_or_pending = None ):
|
||||
|
||||
if current_or_pending is None: return sum( self._counts.values() )
|
||||
else: return self._counts[ current_or_pending ]
|
||||
if current_or_pending is None:
|
||||
|
||||
return self._min_current_count + self._min_pending_count
|
||||
|
||||
elif current_or_pending == HC.CURRENT:
|
||||
|
||||
return self._min_current_count
|
||||
|
||||
elif current_or_pending == HC.PENDING:
|
||||
|
||||
return self._min_pending_count
|
||||
|
||||
|
||||
|
||||
def GetInclusive( self ):
|
||||
|
@ -802,8 +858,29 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
if with_count:
|
||||
|
||||
if self._counts[ HC.CURRENT ] > 0: count_text += u' (' + HydrusData.ConvertIntToPrettyString( self._counts[ HC.CURRENT ] ) + u')'
|
||||
if self._counts[ HC.PENDING ] > 0: count_text += u' (+' + HydrusData.ConvertIntToPrettyString( self._counts[ HC.PENDING ] ) + u')'
|
||||
if self._min_current_count > 0:
|
||||
|
||||
number_text = HydrusData.ConvertIntToPrettyString( self._min_current_count )
|
||||
|
||||
if self._max_current_count is not None:
|
||||
|
||||
number_text += u'-' + HydrusData.ConvertIntToPrettyString( self._max_current_count )
|
||||
|
||||
|
||||
count_text += u' (' + number_text + u')'
|
||||
|
||||
|
||||
if self._min_pending_count > 0:
|
||||
|
||||
number_text = HydrusData.ConvertIntToPrettyString( self._min_pending_count )
|
||||
|
||||
if self._max_pending_count is not None:
|
||||
|
||||
number_text += u'-' + HydrusData.ConvertIntToPrettyString( self._max_pending_count )
|
||||
|
||||
|
||||
count_text += u' (+' + number_text + u')'
|
||||
|
||||
|
||||
|
||||
if self._predicate_type in HC.SYSTEM_PREDICATES:
|
||||
|
|
|
@ -48,7 +48,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 17
|
||||
SOFTWARE_VERSION = 210
|
||||
SOFTWARE_VERSION = 211
|
||||
|
||||
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
||||
|
|
|
@ -1036,6 +1036,22 @@ def ToUnicode( text_producing_object ):
|
|||
|
||||
return text
|
||||
|
||||
def WaitForProcessToFinish( p, timeout ):
|
||||
|
||||
started = GetNow()
|
||||
|
||||
while p.poll() is None:
|
||||
|
||||
if TimeHasPassed( started + timeout ):
|
||||
|
||||
p.kill()
|
||||
|
||||
raise Exception( 'Process did not finish within ' + ConvertIntToPrettyString( timeout ) + ' seconds!' )
|
||||
|
||||
|
||||
time.sleep( 2 )
|
||||
|
||||
|
||||
class HydrusYAMLBase( yaml.YAMLObject ):
|
||||
|
||||
yaml_loader = yaml.SafeLoader
|
||||
|
|
|
@ -18,7 +18,6 @@ import tempfile
|
|||
import threading
|
||||
import traceback
|
||||
import cStringIO
|
||||
import subprocess
|
||||
import HydrusData
|
||||
|
||||
# Mime
|
||||
|
|
|
@ -33,7 +33,7 @@ def GetExternalIP():
|
|||
|
||||
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
||||
|
||||
p.wait()
|
||||
HydrusData.WaitForProcessToFinish( p, 30 )
|
||||
|
||||
( output, error ) = p.communicate()
|
||||
|
||||
|
@ -75,7 +75,7 @@ def AddUPnPMapping( internal_client, internal_port, external_port, protocol, des
|
|||
|
||||
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
||||
|
||||
p.wait()
|
||||
HydrusData.WaitForProcessToFinish( p, 30 )
|
||||
|
||||
( output, error ) = p.communicate()
|
||||
|
||||
|
@ -97,11 +97,14 @@ def GetUPnPMappings():
|
|||
|
||||
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
||||
|
||||
p.wait()
|
||||
HydrusData.WaitForProcessToFinish( p, 30 )
|
||||
|
||||
( output, error ) = p.communicate()
|
||||
|
||||
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to fetch UPnP mappings:' + os.linesep * 2 + HydrusData.ToUnicode( error ) )
|
||||
if error is not None and len( error ) > 0:
|
||||
|
||||
raise Exception( 'Problem while trying to fetch UPnP mappings:' + os.linesep * 2 + HydrusData.ToUnicode( error ) )
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
|
@ -165,7 +168,7 @@ def RemoveUPnPMapping( external_port, protocol ):
|
|||
|
||||
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
|
||||
|
||||
p.wait()
|
||||
HydrusData.WaitForProcessToFinish( p, 30 )
|
||||
|
||||
( output, error ) = p.communicate()
|
||||
|
||||
|
|
|
@ -121,8 +121,8 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
preds = set()
|
||||
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', counts = { HC.CURRENT : 1 } ) )
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', counts = { HC.CURRENT : 1 } ) )
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', min_current_count = 1 ) )
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 ) )
|
||||
|
||||
for p in result: self.assertEqual( p.GetCount( HC.CURRENT ), 1 )
|
||||
|
||||
|
@ -134,8 +134,8 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
preds = set()
|
||||
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', counts = { HC.CURRENT : 1 } ) )
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', counts = { HC.CURRENT : 1 } ) )
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 ) )
|
||||
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', min_current_count = 1 ) )
|
||||
|
||||
for p in result: self.assertEqual( p.GetCount( HC.CURRENT ), 1 )
|
||||
|
||||
|
@ -151,7 +151,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'series:c' )
|
||||
|
||||
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', counts = { HC.CURRENT : 1 } )
|
||||
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 )
|
||||
|
||||
( read_pred, ) = result
|
||||
|
||||
|
@ -163,7 +163,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'car', exact_match = True )
|
||||
|
||||
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', counts = { HC.CURRENT : 1 } )
|
||||
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', min_current_count = 1 )
|
||||
|
||||
( read_pred, ) = result
|
||||
|
||||
|
@ -226,7 +226,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
def test_export_folders( self ):
|
||||
|
||||
file_search_context = ClientSearch.FileSearchContext(file_service_key = HydrusData.GenerateKey(), tag_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( predicate_type = HC.PREDICATE_TYPE_TAG, value = 'test' ) ] )
|
||||
file_search_context = ClientSearch.FileSearchContext(file_service_key = HydrusData.GenerateKey(), tag_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test' ) ] )
|
||||
|
||||
export_folder = ClientFiles.ExportFolder( 'test path', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, file_search_context = file_search_context, period = 3600, phrase = '{hash}' )
|
||||
|
||||
|
@ -245,7 +245,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
for ( inclusive, namespace, result ) in tests:
|
||||
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, namespace, inclusive = inclusive ) ]
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, namespace, inclusive ) ]
|
||||
|
||||
search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = predicates )
|
||||
|
||||
|
@ -273,7 +273,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
for ( inclusive, tag, result ) in tests:
|
||||
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive = inclusive ) ]
|
||||
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive ) ]
|
||||
|
||||
search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = predicates )
|
||||
|
||||
|
@ -541,10 +541,10 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
predicates = []
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : 1 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, None, counts = { HC.CURRENT : 1 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, None, counts = { HC.CURRENT : 0 } ) )
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME, HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = 1 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = 1 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = 0 ) )
|
||||
predicates.extend( [ ClientSearch.Predicate( predicate_type ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME, HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
|
||||
|
||||
self.assertEqual( result, predicates )
|
||||
|
||||
|
@ -597,7 +597,7 @@ class TestClientDB( unittest.TestCase ):
|
|||
|
||||
session.AddPage( 'files', management_controller, [] )
|
||||
|
||||
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', counts = { HC.CURRENT : 1, HC.PENDING : 3 } ) ] )
|
||||
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 3 ) ] )
|
||||
|
||||
management_controller = ClientGUIManagement.CreateManagementControllerQuery( HydrusData.GenerateKey(), fsc, True )
|
||||
|
||||
|
|
|
@ -403,16 +403,16 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'tag' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', counts = { HC.CURRENT : 1, HC.PENDING : 2 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 2 )
|
||||
|
||||
self.assertEqual( p.GetUnicode( with_count = False ), u'tag' )
|
||||
self.assertEqual( p.GetUnicode( with_count = True ), u'tag (1) (+2)' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', inclusive = False )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', False )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'-tag' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', inclusive = False, counts = { HC.CURRENT : 1, HC.PENDING : 2 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', False, 1, 2 )
|
||||
|
||||
self.assertEqual( p.GetUnicode( with_count = False ), u'-tag' )
|
||||
self.assertEqual( p.GetUnicode( with_count = True ), u'-tag (1) (+2)' )
|
||||
|
@ -431,7 +431,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'system:age > 1y2m3d4h' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, None, counts = { HC.CURRENT : 1000 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = 1000 )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'system:archive (1,000)' )
|
||||
|
||||
|
@ -439,7 +439,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'system:duration < 200 milliseconds' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : 2000 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = 2000 )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'system:everything (2,000)' )
|
||||
|
||||
|
@ -459,7 +459,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'system:height < 2,000' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, None, counts = { HC.CURRENT : 1000 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = 1000 )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'system:inbox (1,000)' )
|
||||
|
||||
|
@ -467,7 +467,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'system:limit is 2,000' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, None, counts = { HC.CURRENT : 100 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, min_current_count = 100 )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'system:local (100)' )
|
||||
|
||||
|
@ -483,7 +483,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'system:mime is video/webm, image/gif' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, None, counts = { HC.CURRENT : 100 } )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, min_current_count = 100 )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'system:not local (100)' )
|
||||
|
||||
|
@ -525,7 +525,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'series:*anything*' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series', inclusive = False )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series', False )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'-series' )
|
||||
|
||||
|
@ -535,7 +535,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
|
||||
self.assertEqual( p.GetUnicode(), u'a*i:o*' )
|
||||
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'a*i:o*', inclusive = False )
|
||||
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'a*i:o*', False )
|
||||
|
||||
self.assertEqual( p.GetUnicode(), u'-a*i:o*' )
|
||||
|
||||
|
@ -587,19 +587,19 @@ class TestTagParents( unittest.TestCase ):
|
|||
|
||||
predicates = []
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandmother', counts = { HC.CURRENT : 10 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandfather', counts = { HC.CURRENT : 15 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'not_exist', counts = { HC.CURRENT : 20 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandmother', min_current_count = 10 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandfather', min_current_count = 15 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'not_exist', min_current_count = 20 ) )
|
||||
|
||||
self.assertEqual( self._tag_parents_manager.ExpandPredicates( CC.COMBINED_TAG_SERVICE_KEY, predicates ), predicates )
|
||||
|
||||
predicates = []
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
|
||||
|
||||
results = []
|
||||
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'mother' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'father' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
|
||||
|
@ -610,18 +610,18 @@ class TestTagParents( unittest.TestCase ):
|
|||
predicates = []
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', counts = { HC.CURRENT : 5 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', min_current_count = 5 ) )
|
||||
|
||||
results = []
|
||||
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'mother' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'father' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandfather' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', counts = { HC.CURRENT : 5 } ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', min_current_count = 5 ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'aunt' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'uncle' ) )
|
||||
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
|
||||
|
@ -748,23 +748,24 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
|
||||
predicates = []
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', counts = { HC.CURRENT : 10 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', counts = { HC.CURRENT : 5 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', counts = { HC.CURRENT : 20 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', min_current_count = 10 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', min_current_count = 5 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20 ) )
|
||||
|
||||
results = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', counts = { HC.CURRENT : 35 } ) ]
|
||||
results = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20, max_current_count = 35 ) ]
|
||||
|
||||
self.assertEqual( self._tag_siblings_manager.CollapsePredicates( predicates ), results )
|
||||
|
||||
predicates = []
|
||||
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', counts = { HC.CURRENT : 10 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', counts = { HC.CURRENT : 5 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', counts = { HC.CURRENT : 20 } ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', min_current_count = 10 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', min_current_count = 5 ) )
|
||||
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20 ) )
|
||||
|
||||
( result, ) = self._tag_siblings_manager.CollapsePredicates( predicates )
|
||||
|
||||
self.assertEqual( result.GetCount(), 35 )
|
||||
self.assertEqual( result.GetCount(), 20 )
|
||||
self.assertEqual( result.GetUnicode(), u'chain_c (20-35)' )
|
||||
|
||||
|
||||
def test_chain( self ):
|
||||
|
@ -782,8 +783,6 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
self.assertEqual( set( self._tag_siblings_manager.CollapseTags( [ 'chain_a', 'chain_b' ] ) ), set( [ 'chain_c' ] ) )
|
||||
self.assertEqual( set( self._tag_siblings_manager.CollapseTags( [ 'chain_a', 'chain_b', 'chain_c' ] ) ), set( [ 'chain_c' ] ) )
|
||||
|
||||
# collapsetagstocount
|
||||
|
||||
|
||||
def test_current_overwrite( self ):
|
||||
|
||||
|
|
Loading…
Reference in New Issue