Пример #1
0
def get_items(
    item_type: enums.ItemType = enums.ItemType.Gallery,
    limit: int = 100,
    offset: int = None,
):
    """
    Get a list of items

    Args:
        item_type: type of item to get
        limit: limit the amount of items returned
        offset: offset the results by n items

    Returns:
        .. code-block:: guess

            [
                item message object,
                ...
            ]
    """

    item_type = enums.ItemType.get(item_type)

    db_msg, db_model = item_type._msg_and_model()

    items = database_cmd.GetModelItems().run(db_model,
                                             limit=limit,
                                             offset=offset)

    item_list = message.List(db.model_name(db_model), db_msg)
    [item_list.append(db_msg(i)) for i in items]
    return item_list
Пример #2
0
def library_view(item_type: enums.ItemType = enums.ItemType.Gallery,
                 item_id: int = None,
                 related_type: enums.ItemType = None,
                 page: int = 0,
                 limit: int = 100,
                 sort_by: enums.ItemSort = None,
                 sort_desc: bool=False,
                 search_query: str = "",
                 search_options: dict = {},
                 filter_id: int = None,
                 view_filter: enums.ViewType = enums.ViewType.Library):
    """
    Fetch items from the database.
    Provides pagination.

    Args:
        item_type: possible items are :py:attr:`.ItemType.Gallery`, :py:attr:`.ItemType.Collection`, :py:attr:`.ItemType.Grouping`
        page: current page (zero-indexed)
        sort_by: either a :py:class:`.ItemSort` or a sort index
        sort_desc: order descending (default is ascending)
        limit: amount of items per page
        search_query: filter item by search terms
        search_options: options to apply when filtering, see :ref:`Settings` for available search options
        filter_id: current :py:attr:`.ItemType.GalleryFilter` item id
        view_filter: type of view, set ``None`` to not apply any filter
        related_type: child item
        item_id: id of parent item

    Returns:
        .. code-block:: guess

            [
                item message object,
                ...
            ]

    .. seealso::

        :func:`.get_sort_indexes`
    """
    view_filter, item_type, db_msg, db_model, model_ids, filter_op, join_exp, metatag_name = _view_helper(
        item_type, search_query, filter_id, view_filter, item_id, related_type, search_options)

    items = message.List(db_model.__name__.lower(), db_msg)

    order_exp, group_exp, sort_joins = helpers._sort_helper(sort_by, sort_desc, db_model)

    if sort_joins:
        join_exp.extend(sort_joins)
        # need unique but ordered results, cannot use set so we make use with this
        join_exp = tuple(OrderedDict([(x, None) for x in join_exp]).keys())

    [items.append(db_msg(x)) for x in database_cmd.GetModelItems().run(
        db_model, model_ids, limit=limit, offset=page * limit,
        filter=filter_op, join=join_exp, order_by=order_exp, group_by=group_exp)]

    return items
Пример #3
0
    def advance(self, buffer):
        """
        Advance the loop for this client
        Params:
            buffer -- data buffer to be parsed
        """
        try:
            if constants.server_ready:
                function_list = message.List("function", message.Function)
                functions = self.parse(buffer)
                if functions is None:
                    return
                if isinstance(functions, enums.ServerCommand):
                    if functions == enums.ServerCommand.RequestAuth:
                        self.handshake()
                    return functions

                for func, func_args, ctx in functions:
                    log.d("Calling function", func, "with args", func_args)
                    func_msg = message.Function(func.__name__)
                    try:
                        if ctx:
                            func_args['ctx'] = self.context
                            msg = func(**func_args)
                        else:
                            msg = func(**func_args)
                        assert isinstance(msg, message.CoreMessage) or None
                        func_msg.set_data(msg)
                    except exceptions.CoreError as e:
                        func_msg.set_error(message.Error(e.code, e.msg))
                    function_list.append(func_msg)

                # bad functions
                for fname, e in self.errors:
                    function_list.append(
                        message.Function(fname,
                                         error=message.Error(e.code, e.msg)))
                self.errors.clear()

                self.send(function_list.serialize(session_id=self.session.id))
            else:
                self.on_wait()
        except exceptions.CoreError as e:
            log.d("Sending exception to client:", e)
            self.on_error(e)

        except BaseException:
            if not constants.dev:
                log.exception("An unknown critical error has occurred")
                self.on_error(
                    exceptions.HappypandaError(
                        "An unknown critical error has occurred")
                )  # TODO: include traceback
            else:
                raise
Пример #4
0
def get_related_items(
    item_type: enums.ItemType = enums.ItemType.Gallery,
    item_id: int = 0,
    related_type: enums.ItemType = enums.ItemType.Page,
    limit: int = 100,
    offset: int = None,
):
    """
    Get item related to given item

    Args:
        item_type: parent item
        item_id: id of parent item
        related_type: child item
        limit: limit the amount of items returned
        offset: offset the results by n items

    Returns:
        .. code-block:: guess

            [
                related item message object,
                ...
            ]
    """
    if not item_id:
        raise exceptions.APIError(utils.this_function(),
                                  "item_id must be a valid item id")
    item_type = enums.ItemType.get(item_type)
    related_type = enums.ItemType.get(related_type)

    _, parent_model = item_type._msg_and_model()
    child_msg, child_model = related_type._msg_and_model()

    col = db.relationship_column(parent_model, child_model)
    if not col:
        raise exceptions.APIError(
            utils.this_function(),
            "{} has no relationship with {}".format(related_type, item_type))

    s = constants.db_session()
    q = s.query(child_model.id).join(col).filter(parent_model.id == item_id)
    if offset:
        q = q.offset(offset)
    item_ids = q.limit(limit).all()
    items = database_cmd.GetModelItems().run(child_model,
                                             {x[0]
                                              for x in item_ids})

    item_list = message.List(db.model_name(child_model), child_msg)
    [item_list.append(child_msg(x)) for x in items]
    return item_list
Пример #5
0
def _get_similar(kwargs, similar_items):
    item_list = message.List(db.model_name(kwargs['db_model']),
                             kwargs['db_msg'])
    items = []
    if similar_items:
        similar_items = similar_items[:kwargs['limit']]
        db_items = {}  # needed to sort them the way they came in
        for g in database_cmd.GetModelItems().run(kwargs['db_model'],
                                                  set(similar_items)):
            db_items[g.id] = g
        [items.append(db_items[x]) for x in similar_items if x in db_items]

    [item_list.append(kwargs['db_msg'](x)) for x in items]
    return item_list
Пример #6
0
def get_sort_indexes(item_type: enums.ItemType = None,
                     translate: bool = True,
                     locale: str = None):
    """
    Get a list of available sort item indexes and names

    Args:
        item_type: return applicable indexes for a specific item type
        translate: translate the sort expression name
        locale: locale to get translations from (will override default locale)

    Returns:
        .. code-block:: guess

            [
                {
                    'index' : int,
                    'name': str,
                    'item_type': int value of :py:class:`.ItemType`
                },
                ...
            ]
    """
    db_sort = database_cmd.GetDatabaseSort()
    if item_type:
        item_type = enums.ItemType.get(item_type)
        items = [item_type]
    else:
        items = list(enums.ItemType)

    sort_indexes = message.List("indexes", dict)
    for i in items:
        _, db_model = i._msg_and_model()
        for idx, name in db_sort.run(db_model, name=True).items():
            if translate:
                try:
                    name = i18n.t("general.sort-idx-{}".format(idx),
                                  default=name,
                                  locale=helpers._get_locale(locale))
                except Exception:
                    pass
            sort_indexes.append({
                'index': int(idx),
                'name': name,
                'item_type': i.value
            })
    return sort_indexes
Пример #7
0
def list_plugins(state: enums.PluginState=None):
    """
    Get a list plugins

    Args:
        state: filter list by plugin state

    Returns:
        .. code-block:: guess

            {
            }
    """
    l = message.List('plugins', message.Plugin)
    for n in constants.plugin_manager._nodes:
        l.append(message.Plugin(n))
    return l
Пример #8
0
def library_view(item_type: enums.ItemType = enums.ItemType.Gallery,
                 page: int = 0,
                 limit: int = 100,
                 sort_by: str = "",
                 search_query: str = "",
                 filter_id: int = None,
                 view_filter: enums.ViewType = enums.ViewType.Library,
                 ctx=None):
    """
    Fetch items from the database.
    Provides pagination.

    Args:
        item_type: possible items are :py:attr:`.ItemType.Gallery`, :py:attr:`.ItemType.Collection`, :py:attr:`.ItemType.Grouping`
        page: current page (zero-indexed)
        sort_by: name of column to order by ...
        limit: amount of items per page
        search_query: filter item by search terms
        filter_id: current filter list id
        view_filter: ...

    Returns:
        list of item message objects
    """
    utils.require_context(ctx)
    view_filter = enums.ViewType.get(view_filter)
    item_type = enums.ItemType.get(item_type)

    db_msg, db_model = item_type._msg_and_model(
        (enums.ItemType.Gallery, enums.ItemType.Collection,
         enums.ItemType.Grouping))

    items = message.List(db_model.__name__.lower(), db_msg)

    model_ids = search_cmd.ModelFilter().run(db_model, search_query)

    [
        items.append(db_msg(x)) for x in database_cmd.GetModelItemByID().run(
            db_model, model_ids, limit=limit, offset=page * limit)
    ]

    return items
Пример #9
0
def get_items(item_type: enums.ItemType = enums.ItemType.Gallery,
              limit: int = 100):
    """
    Get a list of items

    Args:
        item_type: type of item to get
        limit: limit the amount of items returned

    Returns:
        list of item message objects
    """

    item_type = enums.ItemType.get(item_type)

    db_msg, db_model = item_type._msg_and_model()

    items = database_cmd.GetModelItems().run(db_model, limit=limit)

    item_list = message.List(db.model_name(db_model), db_msg)
    [item_list.append(db_msg(i)) for i in items]
    return item_list
Пример #10
0
def search_item(
    item_type: enums.ItemType = enums.ItemType.Gallery,
    search_query: str = "",
    search_options: dict = {},
    sort_by: enums.ItemSort = None,
    sort_desc: bool = False,
    full_search: bool = True,
    limit: int = 100,
    offset: int = None,
):
    """
    Search for item

    Args:
        item_type: all of :py:attr:`.ItemType` except :py:attr:`.ItemType.Page` and :py:attr:`.ItemType.GalleryFilter`
        search_query: filter item by search terms
        search_options: options to apply when filtering, see :ref:`Settings` for available search options
        sort_by: either a :py:class:`.ItemSort` or a sort index
        sort_desc: order descending (default is ascending)
        limit: amount of items
        offset: offset the results by n items

    Returns:
        .. code-block:: guess

            [
                item message object,
                ...
            ]

    .. seealso::

        :func:`.get_sort_indexes`
    """
    item_type = enums.ItemType.get(item_type)

    if search_options:
        search_option_names = [
            x.name for x in search_cmd._get_search_options()
        ]
        for n in search_options:
            if n not in search_option_names:
                raise exceptions.APIError(
                    utils.this_function(),
                    "Invalid search option name '{}'".format(n))

    if item_type in (enums.ItemType.Page, enums.ItemType.GalleryFilter):
        raise exceptions.APIError(utils.this_function(),
                                  "Unsupported itemtype {}".format(item_type))
    db_msg, db_model = item_type._msg_and_model()

    model_ids = set()
    if full_search:
        model_ids = search_cmd.ModelFilter().run(db_model, search_query,
                                                 search_options)

    items = message.List("items", db_msg)

    order_exp, group_exp, join_exp = helpers._sort_helper(
        sort_by, sort_desc, db_model)

    [
        items.append(db_msg(x))
        for x in database_cmd.GetModelItems().run(db_model,
                                                  model_ids,
                                                  limit=limit,
                                                  offset=offset,
                                                  join=join_exp,
                                                  order_by=order_exp,
                                                  group_by=group_exp)
    ]

    return items
Пример #11
0
    def advance(self, buffer):
        """
        Advance the loop for this client
        Params:
            buffer -- data buffer to be parsed
        """
        with db.cleanup_session():
            try:
                if constants.server_ready:
                    try:
                        buffer = gzip.decompress(buffer)
                    except (zlib.error, OSError) as e:
                        raise exceptions.ParsingError(utils.this_function(),
                                                      str(e))

                    function_list = message.List("function", message.Function)
                    functions = self.parse(buffer)
                    if functions is None:
                        return
                    if isinstance(functions, enums.ServerCommand):
                        return functions

                    for func, func_args in functions:
                        log.d("Calling function", func, "with args", func_args)
                        func_msg = message.Function(func.__name__)
                        try:
                            msg = func(**func_args)
                            assert isinstance(
                                msg, message.CoreMessage) or msg is None
                            func_msg.set_data(msg)
                        except exceptions.CoreError as e:
                            log.w(f"Error on func '{func.__name__}':", e)
                            func_msg.set_error(message.Error(e.code, e.msg))
                        function_list.append(func_msg)

                    # bad functions
                    for fname, e in self.errors:
                        function_list.append(
                            message.Function(fname,
                                             error=message.Error(
                                                 e.code, e.msg)))
                    self.errors.clear()

                    self.send(
                        function_list.serialize(session_id=self.session.id))
                else:
                    self.on_wait()
            except exceptions.CoreError as e:
                log.w("Sending exception to client:", e)
                self.on_error(e)

            except Exception as e:
                if not constants.dev:
                    log.exception("An unhandled critical error has occurred")
                    if isinstance(e, (PermissionError, FileNotFoundError,
                                      NotImplementedError)):
                        self.on_error(exceptions.HappypandaError(str(e)))
                    else:
                        self.on_error(
                            exceptions.HappypandaError(
                                f"An unhandled critical error has occurred: {e.__class__.__name__}"
                            ))
                else:
                    log.exception("An unhandled critical error has occurred")
                    raise