class CSFBaseSliderLoader(CommonService, HasLog):
    """ Loads Sliders. """
    # noinspection PyMissingOrEmptyDocstring
    @property
    def mod_identity(self) -> CommonModIdentity:
        return ModInfo.get_identity()

    # noinspection PyMissingOrEmptyDocstring
    @property
    def log_identifier(self) -> str:
        return 'csf_slider_loader'

    @property
    def snippet_names(self) -> Tuple[str]:
        """ The names of snippets containing sliders. """
        raise NotImplementedError()

    @CommonExceptionHandler.catch_exceptions(ModInfo.get_identity(),
                                             fallback_return=tuple())
    def load(self) -> Iterator[CSFSlider]:
        """load()

        Loads all Sliders.

        :return: An iterable of Sliders.
        :rtype: Iterator[CSFSlider]
        """
        snippet_names: Tuple[str] = self.snippet_names

        for snippet_package in CommonResourceUtils.load_instances_with_any_tags(
                Types.SNIPPET, snippet_names):
            try:
                sliders: Tuple[CSFSlider] = tuple(self._load(snippet_package))

                for slider in sliders:
                    slider: CSFSlider = slider
                    if slider is None:
                        continue
                    (is_valid_result, is_valid_reason) = slider.is_valid()
                    if is_valid_result:
                        yield slider
            except Exception as ex:
                self.log.format_error(
                    'Error while parsing sliders from \'{}\''.format(
                        snippet_package),
                    exception=ex)

    def _load(self, package_slider: Any) -> Tuple[CSFSlider]:
        raise NotImplementedError()
示例#2
0
    def open(self, sim_info: SimInfo, page: int = 1) -> None:
        """ Open the dialog. """
        self.log.format_with_message(
            'Opening customize sliders dialog for Sim.', sim=sim_info)

        def _on_close() -> None:
            self.log.debug('Customize Slider dialog closed.')
            if self._on_close is not None:
                self._on_close()

        option_dialog = CommonChooseObjectOptionDialog(
            CSFStringId.CUSTOMIZE_SLIDERS,
            CSFStringId.CHOOSE_SLIDERS_TO_MODIFY,
            mod_identity=self.mod_identity,
            on_close=_on_close,
            per_page=400)

        def _reopen_dialog() -> None:
            self.log.debug('Reopening customize sliders dialog.')
            self.open(sim_info, page=option_dialog.current_page)

        option_dialog.add_option(
            CommonDialogActionOption(
                CommonDialogOptionContext(
                    CSFStringId.SLIDER_TEMPLATES_NAME,
                    CSFStringId.SLIDER_TEMPLATES_DESCRIPTION,
                    tag_list=[
                        slider_category.name
                        for slider_category in CSFSliderCategory.values
                    ]),
                on_chosen=lambda *_, **__: CSFSliderTemplateDialog(
                    on_close=_reopen_dialog).open(sim_info),
                always_visible=True))

        def _on_reset_all_sliders() -> None:
            self.log.debug('Confirming all sliders reset.')

            def _on_confirm(_) -> None:
                self.log.debug('Resetting all sliders.')
                self.slider_application_service.reset_all_sliders(sim_info)
                _reopen_dialog()

            def _on_cancel(_) -> None:
                self.log.debug('Cancelled resetting of all sliders.')
                _reopen_dialog()

            CommonOkCancelDialog(
                CSFStringId.CONFIRMATION,
                CSFStringId.ARE_YOU_SURE_YOU_WANT_TO_RESET_ALL_SLIDERS,
                mod_identity=self.mod_identity).show(
                    on_ok_selected=_on_confirm, on_cancel_selected=_on_cancel)

        def _on_slider_changed(slider_unique_identifier: str, amount: float,
                               outcome: CommonChoiceOutcome):
            if slider_unique_identifier is None or amount is None or CommonChoiceOutcome.is_error_or_cancel(
                    outcome):
                self.log.debug(
                    'No slider chosen, dialog closed, or no amount specified.')
                _reopen_dialog()
                return
            self.log.debug('Slider changed, attempting to apply.')
            self.slider_application_service.apply_slider_by_identifier(
                sim_info, slider_unique_identifier, amount)
            _reopen_dialog()

        self.log.debug('Opening Customize Slider dialog.')

        option_dialog.add_option(
            CommonDialogActionOption(
                CommonDialogOptionContext(
                    CSFStringId.RESET_ALL_SLIDERS_NAME,
                    CSFStringId.RESET_ALL_SLIDERS_DESCRIPTION,
                    tag_list=[
                        slider_category.name
                        for slider_category in CSFSliderCategory.values
                    ]),
                on_chosen=lambda *_, **__: _on_reset_all_sliders(),
                always_visible=True))

        sliders: Tuple[
            CSFSlider] = self._slider_query_utils.get_sliders_for_sim(sim_info)

        if not sliders:
            from cncustomsliderframework.sliders.slider_query_registry import CSFSliderQueryRegistry
            if CSFSliderQueryRegistry()._collecting:
                CommonOkDialog(
                    CSFStringId.SLIDERS_ARE_STILL_LOADING,
                    CSFStringId.SLIDERS_ARE_STILL_LOADING_DESCRIPTION,
                    description_tokens=(
                        CSFStringId.FINISHED_LOADING_SLIDERS, ),
                    mod_identity=ModInfo.get_identity()).show()
            else:
                CommonOkDialog(CSFStringId.NO_SLIDERS_FOUND,
                               CSFStringId.NO_SLIDERS_FOUND,
                               mod_identity=ModInfo.get_identity()).show()
            _on_close()
            return

        self.log.debug('Adding slider count {}'.format(len(sliders)))
        sorted_sliders = sorted(sliders, key=lambda s: s.name)
        slider_categories: List[CSFSliderCategory] = list()
        object_categories: List[str] = list()
        for custom_slider in sorted_sliders:
            for slider_category in custom_slider.categories:
                if slider_category.name in object_categories:
                    continue
                slider_categories.append(slider_category)
                object_categories.append(slider_category.name)

        def _on_randomize_slider_category(category_name: str,
                                          category: CSFSliderCategory):
            self.log.debug(
                'Confirming reset of sliders in category {}.'.format(
                    category_name))

            def _on_confirm(_) -> None:
                self.log.debug(
                    'Randomizing all sliders in category {}.'.format(
                        category_name))
                for slider in sorted_sliders:
                    if category not in slider.categories:
                        continue
                    self.slider_application_service.apply_random(
                        sim_info, slider)
                _reopen_dialog()

            def _on_cancel(_) -> None:
                self.log.debug(
                    'Cancelled randomization of sliders in category {}.'.
                    format(category_name))
                _reopen_dialog()

            CommonOkCancelDialog(CSFStringId.CONFIRMATION,
                                 CSFStringId.RANDOMIZE_SLIDER_CONFIRMATION,
                                 mod_identity=self.mod_identity).show(
                                     on_ok_selected=_on_confirm,
                                     on_cancel_selected=_on_cancel)

        for slider_category in slider_categories:
            option_dialog.add_option(
                CommonDialogSelectOption(
                    slider_category.name,
                    slider_category,
                    CommonDialogOptionContext(
                        CSFStringId.RANDOMIZE_SLIDER_NAME,
                        CSFStringId.RANDOMIZE_SLIDER_DESCRIPTION,
                        title_tokens=(slider_category.name, ),
                        description_tokens=(slider_category.name, ),
                        tag_list=[
                            slider_category.name
                            for slider_category in CSFSliderCategory.values
                        ]),
                    on_chosen=_on_randomize_slider_category,
                    always_visible=True))

        for custom_slider in sorted_sliders:
            if custom_slider.description is not None:
                # noinspection PyTypeChecker
                option_description = CommonLocalizationUtils.create_localized_string(
                    custom_slider.description,
                    tokens=(str(0.0), str(custom_slider.minimum_value),
                            str(custom_slider.maximum_value)))
            else:
                # noinspection PyTypeChecker
                option_description = CommonLocalizationUtils.create_localized_string(
                    CSFStringId.CHANGE_THE_SLIDER,
                    tokens=(custom_slider.display_name, ))

            option_dialog.add_option(
                CommonDialogInputFloatOption(
                    custom_slider.unique_identifier,
                    self.slider_application_service.get_current_slider_value(
                        sim_info, custom_slider),
                    CommonDialogOptionContext(
                        custom_slider.display_name,
                        option_description,
                        icon=custom_slider.icon_id or None,
                        tag_list=tuple([
                            category.name
                            for category in custom_slider.categories
                        ])),
                    min_value=custom_slider.minimum_value,
                    max_value=custom_slider.maximum_value,
                    on_chosen=_on_slider_changed,
                    dialog_description_identifier=CSFStringId.DEFAULT_MIN_MAX,
                    dialog_description_tokens=(
                        option_description, str(0.0),
                        str(custom_slider.minimum_value),
                        str(custom_slider.maximum_value))))

        categories: List[CommonDialogObjectOptionCategory] = list()

        for category in object_categories:
            # noinspection PyTypeChecker
            categories.append(
                CommonDialogObjectOptionCategory(
                    category, icon=CommonIconId.S4CLIB_UNFILLED_CIRCLE_ICON))

        self.log.debug('Showing slider options.')

        option_dialog.show(sim_info=sim_info, page=page, categories=categories)
示例#3
0
 def mod_identity(self) -> CommonModIdentity:
     return ModInfo.get_identity()
 def get_mod_identity(cls) -> CommonModIdentity:
     return ModInfo.get_identity()
示例#5
0
This file is part of the Custom Slider Framework licensed under the Creative Commons Attribution-NoDerivatives 4.0 International public license (CC BY-ND 4.0).

https://creativecommons.org/licenses/by-nd/4.0/
https://creativecommons.org/licenses/by-nd/4.0/legalcode

Copyright (c) COLONOLNUTTY
"""
from typing import Any, Dict, Set

from cncustomsliderframework.dtos.sliders.slider import CSFSlider
from cncustomsliderframework.modinfo import ModInfo
from cncustomsliderframework.sliders.slider_query_registry import CSFSliderQueryRegistry
from sims4.commands import Command, CommandType, CheatOutput
from sims4communitylib.utils.common_log_registry import CommonLogRegistry

log = CommonLogRegistry().register_log(ModInfo.get_identity(),
                                       'csf_slider_query')


@Command('csf.log_slider_tags', command_type=CommandType.Live)
def _csf_command_log_slider_tags(_connection: int = None):
    output = CheatOutput(_connection)
    output(
        'Logging Slider tags, this will take awhile and your game may freeze. Be Patient!'
    )
    try:
        log.enable()
        slider_results = []
        for slider_tag_value in CSFSliderQueryRegistry()._slider_library.keys(
        ):
            sliders = CSFSliderQueryRegistry(
class CSFSliderQueryRegistry(CommonService, HasLog):
    """ Registry handling slider queries. """

    # noinspection PyMissingOrEmptyDocstring
    @property
    def mod_identity(self) -> CommonModIdentity:
        return ModInfo.get_identity()

    # noinspection PyMissingOrEmptyDocstring
    @property
    def log_identifier(self) -> str:
        return 'csf_slider_query_registry'

    @property
    def _tag_handlers(self) -> List[CSFSliderTagHandler]:
        return self.__tag_handlers

    @property
    def slider_library(self) -> Dict[Tuple[CSFSliderTagType, Any], Set[str]]:
        """ A library of sliders organized by filter keys. """
        return self._slider_library

    @slider_library.setter
    def slider_library(self, value: Dict[Tuple[CSFSliderTagType, Any],
                                         Set[str]]):
        self._slider_library = value

    def __init__(self) -> None:
        from cncustomsliderframework.sliders.slider_registry import CSFSliderRegistry
        super().__init__()
        self._collecting = False
        self.slider_library = collections.defaultdict(set)
        self.__tag_handlers: List[CSFSliderTagHandler] = list()
        self._all: List[CSFSlider] = list()
        self._registry = CSFSliderRegistry()

    def add_tag_handler(self, tag_handler_init: Callable[[CSFSliderTagType],
                                                         CSFSliderTagHandler],
                        tag: CSFSliderTagType):
        """ Add a tag handler. """
        self.__tag_handlers.append(tag_handler_init(tag))

    def create_query(
        self,
        slider_filters: Tuple[CSFSliderTagFilter],
        query_type: CSFQueryType = CSFQueryType.ALL_INTERSECT_ANY_MUST_HAVE_ONE
    ) -> CSFSliderQuery:
        """ Create a query for sliders. """
        return CSFSliderQuery(slider_filters, query_type=query_type)

    def has_sliders(self, queries: Tuple[CSFSliderQuery]) -> bool:
        """ Determine if sliders are available for tags. """
        self.log.format_with_message('Checking if has sliders',
                                     queries=queries)
        for _ in self.get_sliders(queries):
            return True
        return False

    def get_all_sliders(self) -> Tuple[CSFSlider]:
        """ Get all sliders. """
        if self._collecting:
            return tuple()
        return tuple(self._all)

    def get_sliders(self, queries: Tuple[CSFSliderQuery]) -> Set[CSFSlider]:
        """ Retrieve sliders matching the queries. """
        self.log.format_with_message('Getting sliders', queries=queries)
        if self._collecting:
            return set()
        sliders = set()
        for query in queries:
            found_sliders = self._query_sliders(query)
            if found_sliders:
                sliders = sliders | found_sliders
        if verbose_log.enabled:
            verbose_log.debug('Finished locating sliders [{}]'.format(
                ',\n'.join([
                    '{}:{}'.format(str(slider.raw_display_name), slider.author)
                    for slider in sliders
                ])))
        return sliders

    def _query_sliders(self, query: CSFSliderQuery) -> Set[CSFSlider]:
        self.log.format_with_message(
            'Querying for sliders using query: {}'.format(query))
        all_tags = query.include_all_tags
        any_tags = query.include_any_tags
        exclude_tags = query.exclude_tags
        found_slider_identifiers = None
        for include_all_tag in all_tags:
            if include_all_tag is None:
                continue
            if include_all_tag.key not in self.slider_library:
                # One of the All keys is not within the slider library! This means no sliders match ALL tags.
                return set()
            new_found_sliders = self.slider_library[include_all_tag.key]
            if found_slider_identifiers is not None:
                self.log.debug('Looking for tag {}'.format(include_all_tag))
                self.log.debug('Before intersect for all_tags {}'.format(
                    len(found_slider_identifiers)))
                new_found_sliders = found_slider_identifiers & new_found_sliders
                self.log.debug('After intersect for all_tags {}'.format(
                    len(new_found_sliders)))
            else:
                self.log.debug('Found with all_tags {}'.format(
                    len(new_found_sliders)))
            found_slider_identifiers = new_found_sliders

        if found_slider_identifiers is None and all_tags:
            self.log.debug('No sliders found for all_tags {}'.format(all_tags))
            return set()

        self.log.debug('After all_tags {}'.format(
            len(found_slider_identifiers)))
        if verbose_log.enabled:
            found_all_sliders: List[CSFSlider] = list()
            for slider_identifier in found_slider_identifiers:
                slider = self._registry.locate_by_identifier(slider_identifier)
                if slider is None:
                    continue
                found_all_sliders.append(slider)
            verbose_log.format_with_message(
                'Found sliders via all tags',
                sliders=',\n'.join([
                    '{}:{}'.format(str(found_slider.raw_display_name),
                                   found_slider.author)
                    for found_slider in found_all_sliders
                ]))

        found_sliders_via_any_tags = set()
        for include_any_tag in any_tags:
            if include_any_tag is None:
                continue
            if include_any_tag.key not in self.slider_library:
                continue
            found_sliders_via_any_tags = found_sliders_via_any_tags | self.slider_library[
                include_any_tag.key]

        self.log.debug('Found sliders via any {}'.format(
            len(found_sliders_via_any_tags)))

        if verbose_log.enabled:
            found_any_sliders: List[CSFSlider] = list()
            for slider_identifier in found_sliders_via_any_tags:
                slider = self._registry.locate_by_identifier(slider_identifier)
                if slider is None:
                    continue
                found_any_sliders.append(slider)
            verbose_log.format_with_message(
                'Found sliders via any tags',
                sliders=',\n'.join([
                    '{}:{}'.format(str(found_slider.raw_display_name),
                                   found_slider.author)
                    for found_slider in found_any_sliders
                ]))

        if found_slider_identifiers is None:
            self.log.debug('No sliders found for all_tags.')
            if not all_tags:
                self.log.debug('Returning any tags.')
                return found_sliders_via_any_tags
        else:
            query_type = query.query_type
            if not any_tags and (
                    query_type == CSFQueryType.ALL_INTERSECT_ANY or query_type
                    == CSFQueryType.ALL_INTERSECT_ANY_MUST_HAVE_ONE):
                query_type = CSFQueryType.ALL_PLUS_ANY
            if query_type == CSFQueryType.ALL_PLUS_ANY:
                found_slider_identifiers = found_slider_identifiers | found_sliders_via_any_tags
            elif query_type == CSFQueryType.ALL_INTERSECT_ANY:
                found_slider_identifiers = found_slider_identifiers & found_sliders_via_any_tags

            if query_type == CSFQueryType.ALL_PLUS_ANY_MUST_HAVE_ONE:
                if not found_sliders_via_any_tags:
                    return set()
                found_slider_identifiers = found_slider_identifiers | found_sliders_via_any_tags
            elif query_type == CSFQueryType.ALL_INTERSECT_ANY_MUST_HAVE_ONE:
                if not found_sliders_via_any_tags:
                    return set()
                found_slider_identifiers = found_slider_identifiers & found_sliders_via_any_tags

        if found_slider_identifiers is None:
            self.log.debug(
                'No found sliders after combining any tags. All Tags: {} Any Tags: {}'
                .format(all_tags, any_tags))
            return set()

        self.log.debug('After any tags {}'.format(
            len(found_slider_identifiers)))

        excluded = set()
        for exclude_tag in exclude_tags:
            if exclude_tag is None:
                continue
            if exclude_tag.key not in self.slider_library:
                continue
            excluded = excluded | self.slider_library[exclude_tag.key]

        self.log.debug('Excluding sliders {}'.format(len(excluded)))

        if excluded:
            found_slider_identifiers = found_slider_identifiers - excluded

        self.log.debug('After exclude sliders {}'.format(
            len(found_slider_identifiers)))
        found_sliders: List[CSFSlider] = list()
        for slider_identifier in found_slider_identifiers:
            slider = self._registry.locate_by_identifier(slider_identifier)
            if slider is None:
                continue
            found_sliders.append(slider)

        if verbose_log.enabled:
            verbose_log.debug('Returning sliders [{}]'.format(',\n'.join([
                '{}:{}'.format(str(found_slider.raw_display_name),
                               found_slider.author)
                for found_slider in found_sliders
            ])))
        return set(found_sliders)

    def _organize(self, sliders: Tuple[CSFSlider]):
        self.log.debug('Collecting Sliders Query Data...')
        self.slider_library.clear()
        new_slider_library = dict()
        tag_handlers = tuple(self._tag_handlers)
        for slider in sliders:
            self.log.format_with_message('Handling tags for Slider',
                                         cas_part=slider.name)
            slider_identifier = slider.unique_identifier
            slider_tag_keys = list()
            for tag_handler in tag_handlers:
                if not tag_handler.applies(slider):
                    continue
                tag_type = tag_handler.tag_type
                for slider_tag in tag_handler.get_tags(slider):
                    tag_key = (tag_type, slider_tag)
                    slider_tag_keys.append(tag_key)
                    if tag_key not in new_slider_library:
                        new_slider_library[tag_key] = set()
                    if slider_identifier in new_slider_library[tag_key]:
                        continue
                    new_slider_library[tag_key].add(slider_identifier)
            self.log.format_with_message('Applied tags to slider.',
                                         display_name=slider.name,
                                         keys=slider_tag_keys)

        self.log.format_with_message(
            'Completed collecting Sliders Query Data.',
            slider_library=new_slider_library)
        self.slider_library = new_slider_library

    def trigger_collection(self,
                           show_loading_notification: bool = True) -> None:
        """trigger_collection(show_loading_notification=True)

        Trigger the action to collect all sliders and organize them by a number of tags.

        :param show_loading_notification: If set to True, the Loading Sliders notification will be shown. If set to False, the Loading Sliders notification will not be shown. Default is True.
        :type show_loading_notification: bool, optional
        """
        def _recollect_data() -> None:
            try:
                if show_loading_notification:
                    CommonBasicNotification(
                        CSFStringId.LOADING_SLIDERS,
                        CSFStringId.LOADING_SLIDERS_DESCRIPTION).show()
                number_of_sliders = self._collect()
                if number_of_sliders == -1:
                    return
                CommonBasicNotification(
                    CSFStringId.FINISHED_LOADING_SLIDERS,
                    CSFStringId.FINISHED_LOADING_SLIDERS_DESCRIPTION,
                    description_tokens=(str(number_of_sliders), )).show()
            except Exception as ex:
                self.log.error('Error occurred while collecting sliders.',
                               exception=ex)
                self._collecting = False

        _recollect_data()

    def _collect(self) -> int:
        if self._collecting:
            return -1
        self._collecting = True
        try:
            stop_watch = CommonStopWatch()
            stop_watch.start()
            self._all = tuple(self._registry.sliders.values())
            self.log.format_with_message(
                'Loaded Sliders',
                all_list=self._all,
            )
            enabled = self.log.enabled
            self.log.enable()
            self.log.debug('Took {}s to collect {} Sliders.'.format(
                '%.3f' % (stop_watch.stop()), len(self._all)))
            if not enabled:
                self.log.disable()
            stop_watch.start()
            self._organize(self._all)
            self._collecting = False
            self.log.enable()
            self.log.debug('Took {}s to organize Sliders'.format(
                '%.3f' % (stop_watch.stop())))
            if not enabled:
                self.log.disable()
            if self.log.enabled:
                self.log.debug('Loaded {} Sliders.'.format(len(self._all)))
            return len(self._all)
        except Exception as ex:
            self.log.error('Error occurred while collecting Sliders.',
                           exception=ex)
            return -1
        finally:
            self._collecting = False

    @classmethod
    def register_tag_handler(
            cls, tag_type: CSFSliderTagType) -> Callable[[Any], Any]:
        """ Register a tag handler. """
        def _method_wrapper(
            tag_handler_callback: Callable[[CSFSliderTagType],
                                           CSFSliderTagHandler]):
            cls().add_tag_handler(tag_handler_callback, tag_type)
            return tag_handler_callback

        return _method_wrapper

    @staticmethod
    @CommonEventRegistry.handle_events(ModInfo.get_identity())
    def _load_sliders_on_zone_load(event_data: S4CLZoneLateLoadEvent):
        if event_data.game_loaded:
            # If the game is already loaded, we've already loaded the data once.
            return False
        CSFSliderQueryRegistry().trigger_collection(
            show_loading_notification=False)
        return True

    @staticmethod
    @CommonIntervalEventRegistry.run_once(ModInfo.get_identity(),
                                          milliseconds=10)
    def _show_sliders_loading_notification_on_first_update() -> None:
        if CSFSliderQueryRegistry()._collecting:
            CommonBasicNotification(
                CSFStringId.LOADING_SLIDERS,
                CSFStringId.LOADING_SLIDERS_DESCRIPTION).show()
from cncustomsliderframework.sliders.query.tag_handlers.slider_tag_handler import CSFSliderTagHandler
from cncustomsliderframework.sliders.slider_tag_type import CSFSliderTagType
from cncustomsliderframework.sliders.tag_filters.slider_tag_filter import CSFSliderTagFilter
from sims4.commands import Command, CheatOutput, CommandType
from sims4communitylib.classes.time.common_stop_watch import CommonStopWatch
from sims4communitylib.events.event_handling.common_event_registry import CommonEventRegistry
from sims4communitylib.events.interval.common_interval_event_service import CommonIntervalEventRegistry
from sims4communitylib.events.zone_spin.events.zone_late_load import S4CLZoneLateLoadEvent
from sims4communitylib.logging.has_log import HasLog
from sims4communitylib.mod_support.mod_identity import CommonModIdentity
from sims4communitylib.notifications.common_basic_notification import CommonBasicNotification
from sims4communitylib.services.common_service import CommonService
from sims4communitylib.utils.common_log_registry import CommonLogRegistry

verbose_log = CommonLogRegistry().register_log(
    ModInfo.get_identity(), 'csf_slider_query_registry_verbose')


class CSFSliderQueryRegistry(CommonService, HasLog):
    """ Registry handling slider queries. """

    # noinspection PyMissingOrEmptyDocstring
    @property
    def mod_identity(self) -> CommonModIdentity:
        return ModInfo.get_identity()

    # noinspection PyMissingOrEmptyDocstring
    @property
    def log_identifier(self) -> str:
        return 'csf_slider_query_registry'