Пример #1
0
def _get_default_settings(request: Request, order: Any) -> DefaultSettings:
    if isinstance(request.context, Group):
        is_home_page = False
        user_settings = (request.query(UserGroupSettings).filter(
            UserGroupSettings.user == request.user,
            UserGroupSettings.group == request.context,
        ).one_or_none())
    else:
        is_home_page = True
        user_settings = None

    if user_settings and user_settings.default_order:
        default_order = user_settings.default_order
    elif request.user.home_default_order:
        default_order = request.user.home_default_order
    else:
        default_order = TopicSortOption.ACTIVITY

    # the default period depends on what the order is, so we need to see if
    # we're going to end up using the default order here as well
    if order is missing:
        order = default_order

    if user_settings and user_settings.default_period:
        user_default = user_settings.default_period
        default_period = ShortTimePeriod().deserialize(user_default)
    elif request.user.home_default_period:
        user_default = request.user.home_default_period
        default_period = ShortTimePeriod().deserialize(user_default)
    else:
        # Overall default periods, if the user doesn't have either a
        # group-specific or a home default set up:
        #   * "all time" if sorting by new
        #   * "all time" if sorting by activity and inside a group
        #   * "3 days" if sorting by activity and on home page
        #   * "1 day" otherwise (sorting by most votes or most comments)
        if order == TopicSortOption.NEW:
            default_period = None
        elif order == TopicSortOption.ACTIVITY and not is_home_page:
            default_period = None
        elif order == TopicSortOption.ACTIVITY:
            default_period = SimpleHoursPeriod(72)
        else:
            default_period = SimpleHoursPeriod(24)

    return DefaultSettings(order=default_order, period=default_period)
Пример #2
0
    def _deserialize(self, value: str, attr: str,
                     data: dict) -> Optional[SimpleHoursPeriod]:
        """Deserialize to a SimpleHoursPeriod object."""
        if value == "all":
            return None

        try:
            return SimpleHoursPeriod.from_short_form(value)
        except ValueError:
            raise ValidationError("Invalid time period")
Пример #3
0
def get_search(request: Request, order: Optional[TopicSortOption],
               after: Optional[str], before: Optional[str], per_page: int,
               search: str, **kwargs: Any) -> dict:
    """Get a list of search results."""
    # period needs special treatment so we can distinguish between missing and None
    period = kwargs.get("period", missing)

    group = None
    if isinstance(request.context, Group):
        group = request.context

    if not order:
        order = TopicSortOption.NEW

    if period is missing:
        period = None

    query = (request.query(Topic).join_all_relationships().search(
        search).apply_sort_option(order))

    # if searching from inside a group, restrict to that group alone
    if group:
        query = query.inside_groups([group])

    # restrict the time period, if not set to "all time"
    if period:
        query = query.inside_time_period(period)

    # apply before/after pagination restrictions if relevant
    if before:
        query = query.before_id36(before)

    if after:
        query = query.after_id36(after)

    topics = query.get_page(per_page)

    period_options = [SimpleHoursPeriod(hours) for hours in (1, 12, 24, 72)]

    # add the current period to the bottom of the dropdown if it's not one of the
    # "standard" ones
    if period and period not in period_options:
        period_options.append(period)

    return {
        "search": search,
        "topics": topics,
        "group": group,
        "order": order,
        "order_options": TopicSortOption,
        "period": period,
        "period_options": period_options,
    }
Пример #4
0
    def _deserialize(
        self,
        value: str,
        attr: Optional[str],
        data: DataType,
        **kwargs: Any,
    ) -> Optional[SimpleHoursPeriod]:
        """Deserialize to a SimpleHoursPeriod object."""
        if value == "all":
            return None

        try:
            return SimpleHoursPeriod.from_short_form(value)
        except ValueError as exc:
            raise ValidationError("Invalid time period") from exc
Пример #5
0
def get_search(
    request: Request,
    order: Any,
    period: Any,
    after: str,
    before: str,
    per_page: int,
    search: str,
) -> dict:
    """Get a list of search results."""
    # pylint: disable=too-many-arguments
    if order is missing:
        order = TopicSortOption.NEW

    if period is missing:
        period = None

    query = (request.query(Topic).join_all_relationships().search(
        search).apply_sort_option(order))

    # restrict the time period, if not set to "all time"
    if period:
        query = query.inside_time_period(period)

    # apply before/after pagination restrictions if relevant
    if before:
        query = query.before_id36(before)

    if after:
        query = query.after_id36(after)

    topics = query.get_page(per_page)

    period_options = [SimpleHoursPeriod(hours) for hours in (1, 12, 24, 72)]

    # add the current period to the bottom of the dropdown if it's not one of the
    # "standard" ones
    if period and period not in period_options:
        period_options.append(period)

    return {
        "search": search,
        "topics": topics,
        "order": order,
        "order_options": TopicSortOption,
        "period": period,
        "period_options": period_options,
    }
Пример #6
0
def _get_default_settings(
    request: Request, order: Optional[TopicSortOption]
) -> DefaultSettings:
    if isinstance(request.context, Group) and request.user:
        user_settings = (
            request.query(UserGroupSettings)
            .filter(
                UserGroupSettings.user == request.user,
                UserGroupSettings.group == request.context,
            )
            .one_or_none()
        )
    else:
        user_settings = None

    if user_settings and user_settings.default_order:
        default_order = user_settings.default_order
    elif request.user and request.user.home_default_order:
        default_order = request.user.home_default_order
    else:
        default_order = TopicSortOption.ACTIVITY

    # the default period depends on what the order is, so we need to see if we're going
    # to end up using the default order here as well
    if not order:
        order = default_order

    if user_settings and user_settings.default_period:
        user_default = user_settings.default_period
        default_period = ShortTimePeriod().deserialize(user_default)
    elif request.user and request.user.home_default_period:
        user_default = request.user.home_default_period
        default_period = ShortTimePeriod().deserialize(user_default)
    else:
        # Overall default periods, if the user doesn't have either a group-specific or a
        # home default set up:
        #   * "1 day" if sorting by most votes or most comments
        #   * "all time" otherwise
        if order in (TopicSortOption.VOTES, TopicSortOption.COMMENTS):
            default_period = SimpleHoursPeriod(24)
        else:
            default_period = None

    return DefaultSettings(order=default_order, period=default_period)
Пример #7
0
def get_group_topics(  # noqa
        request: Request, after: Optional[str], before: Optional[str],
        order: Optional[TopicSortOption], per_page: int,
        rank_start: Optional[int], tag: Optional[Ltree], unfiltered: bool,
        **kwargs: Any) -> dict:
    """Get a listing of topics in the group."""
    # period needs special treatment so we can distinguish between missing and None
    period = kwargs.get("period", missing)

    is_home_page = request.matched_route.name == "home"

    if is_home_page:
        # on the home page, include topics from the user's subscribed groups
        # (or all groups, if logged-out)
        if request.user:
            groups = [sub.group for sub in request.user.subscriptions]
        else:
            groups = [
                group for group in request.query(Group).all()
                if group.path != "test"
            ]
        subgroups = None
    else:
        # otherwise, just topics from the single group that we're looking at
        groups = [request.context]

        subgroups = (request.query(Group).filter(
            Group.path.descendant_of(request.context.path),
            Group.path != request.context.path,
        ).all())

    default_settings = _get_default_settings(request, order)

    if not order:
        order = default_settings.order

    if period is missing:
        period = default_settings.period

    # set up the basic query for topics
    query = (request.query(Topic).join_all_relationships().inside_groups(
        groups, include_subgroups=not is_home_page).exclude_ignored().
             apply_sort_option(order))

    # restrict the time period, if not set to "all time"
    if period:
        query = query.inside_time_period(period)

    # restrict to a specific tag, if we're viewing a single one
    if tag:
        query = query.has_tag(str(tag))

    # apply before/after pagination restrictions if relevant
    if before:
        query = query.before_id36(before)

    if after:
        query = query.after_id36(after)

    # apply topic tag filters unless they're disabled or viewing a single tag
    if request.user and request.user.filtered_topic_tags and not (tag or
                                                                  unfiltered):
        query = query.filter(~Topic.tags.descendant_of(  # type: ignore
            any_(cast(request.user.filtered_topic_tags, TagList))))

    topics = query.get_page(per_page)

    # don't show pinned topics on home page
    if request.matched_route.name == "home":
        pinned_topics = []
    else:
        # get pinned topics
        pinned_query = (request.query(Topic).join_all_relationships(
        ).inside_groups(groups).is_pinned(True).apply_sort_option(order))

        pinned_topics = pinned_query.all()

    period_options = [
        SimpleHoursPeriod(hours) for hours in (1, 12, 24, 72, 168)
    ]

    # add the current period to the bottom of the dropdown if it's not one of the
    # "standard" ones
    if period and period not in period_options:
        period_options.append(period)

    if isinstance(request.context, Group):
        wiki_pages = (request.query(GroupWikiPage).filter(
            GroupWikiPage.group == request.context).order_by(
                GroupWikiPage.path).all())

        # remove the index from the page list, we'll output it separately
        if any(page.path == "index" for page in wiki_pages):
            wiki_has_index = True
            wiki_pages = [page for page in wiki_pages if page.path != "index"]
        else:
            wiki_has_index = False
    else:
        wiki_pages = None
        wiki_has_index = False

    if isinstance(request.context, Group):
        # Get the most recent topic from each scheduled topic in this group
        # I'm not even going to attempt to write this query in pure SQLAlchemy
        topic_id_subquery = """
            SELECT topic_id FROM (SELECT topic_id, schedule_id, row_number() OVER
            (PARTITION BY schedule_id ORDER BY created_time DESC) AS rownum FROM topics)
            AS t WHERE schedule_id IS NOT NULL AND rownum = 1
        """
        most_recent_scheduled_topics = (
            request.query(Topic).join(TopicSchedule).filter(
                Topic.topic_id.in_(text(topic_id_subquery)),  # type: ignore
                TopicSchedule.group == request.context,
                TopicSchedule.next_post_time != None,  # noqa
            ).order_by(TopicSchedule.next_post_time).all())
    else:
        most_recent_scheduled_topics = None

    if is_home_page:
        financial_data = get_financial_data(request.db_session)
    else:
        financial_data = None

    return {
        "group":
        request.context,
        "groups":
        groups,
        "topics":
        topics,
        "pinned_topics":
        pinned_topics,
        "order":
        order,
        "order_options":
        TopicSortOption,
        "period":
        period,
        "period_options":
        period_options,
        "is_default_period":
        period == default_settings.period,
        "is_default_view": (period == default_settings.period
                            and order == default_settings.order),
        "rank_start":
        rank_start,
        "tag":
        tag,
        "unfiltered":
        unfiltered,
        "wiki_pages":
        wiki_pages,
        "wiki_has_index":
        wiki_has_index,
        "subgroups":
        subgroups,
        "most_recent_scheduled_topics":
        most_recent_scheduled_topics,
        "financial_data":
        financial_data,
        "current_time":
        utc_now(),
    }
Пример #8
0
def get_group_topics(
    request: Request,
    order: Any,  # more specific would be better, but missing isn't typed
    period: Any,  # more specific would be better, but missing isn't typed
    after: str,
    before: str,
    per_page: int,
    rank_start: Optional[int],
    tag: Optional[Ltree],
    unfiltered: bool,
) -> dict:
    """Get a listing of topics in the group."""
    # pylint: disable=too-many-arguments, too-many-branches, too-many-locals
    if request.matched_route.name == "home":
        # on the home page, include topics from the user's subscribed groups
        # (or all groups, if logged-out)
        if request.user:
            groups = [sub.group for sub in request.user.subscriptions]
        else:
            groups = [
                group for group in request.query(Group).all()
                if group.path != "test"
            ]
    else:
        # otherwise, just topics from the single group that we're looking at
        groups = [request.context]

    default_settings = _get_default_settings(request, order)

    if order is missing:
        order = default_settings.order

    if period is missing:
        period = default_settings.period

    # set up the basic query for topics
    query = (request.query(Topic).join_all_relationships().inside_groups(
        groups).apply_sort_option(order))

    # restrict the time period, if not set to "all time"
    if period:
        query = query.inside_time_period(period)

    # restrict to a specific tag, if we're viewing a single one
    if tag:
        query = query.has_tag(tag)

    # apply before/after pagination restrictions if relevant
    if before:
        query = query.before_id36(before)

    if after:
        query = query.after_id36(after)

    # apply topic tag filters unless they're disabled or viewing a single tag
    if request.user and not (tag or unfiltered):
        # pylint: disable=protected-access
        query = query.filter(~Topic._tags.descendant_of(  # type: ignore
            any_(cast(request.user._filtered_topic_tags, ArrayOfLtree))))

    topics = query.get_page(per_page)

    period_options = [
        SimpleHoursPeriod(hours) for hours in (1, 12, 24, 72, 168)
    ]

    # add the current period to the bottom of the dropdown if it's not one of the
    # "standard" ones
    if period and period not in period_options:
        period_options.append(period)

    if isinstance(request.context, Group):
        wiki_pages = (request.query(GroupWikiPage).filter(
            GroupWikiPage.group == request.context).order_by(
                GroupWikiPage.slug).all())

        # remove the index from the page list, we'll output it separately
        if any(page.slug == "index" for page in wiki_pages):
            wiki_has_index = True
            wiki_pages = [page for page in wiki_pages if page.slug != "index"]
        else:
            wiki_has_index = False
    else:
        wiki_pages = None
        wiki_has_index = False

    return {
        "group":
        request.context,
        "groups":
        groups,
        "topics":
        topics,
        "order":
        order,
        "order_options":
        TopicSortOption,
        "period":
        period,
        "period_options":
        period_options,
        "is_default_period":
        period == default_settings.period,
        "is_default_view": (period == default_settings.period
                            and order == default_settings.order),
        "rank_start":
        rank_start,
        "tag":
        tag,
        "unfiltered":
        unfiltered,
        "wiki_pages":
        wiki_pages,
        "wiki_has_index":
        wiki_has_index,
    }
Пример #9
0
def get_group_topics(  # noqa
        request: Request, after: Optional[str], before: Optional[str],
        order: Optional[TopicSortOption], per_page: int,
        rank_start: Optional[int], tag: Optional[Ltree], unfiltered: bool,
        **kwargs: Any) -> dict:
    """Get a listing of topics in the group."""
    # period needs special treatment so we can distinguish between missing and None
    period = kwargs.get("period", missing)

    is_home_page = request.matched_route.name in [
        "home", "home_atom", "home_rss"
    ]
    is_atom = request.matched_route.name in ["home_atom", "group_topics_atom"]
    is_rss = request.matched_route.name in ["home_rss", "group_topics_rss"]

    if is_home_page:
        # on the home page, include topics from the user's subscribed groups
        # (or all groups, if logged-out)
        if request.user:
            groups = [sub.group for sub in request.user.subscriptions]
        else:
            groups = [
                group for group in request.query(Group).all()
                if group.path != "test"
            ]
        subgroups = None
    else:
        # otherwise, just topics from the single group that we're looking at
        groups = [request.context]

        subgroups = (request.query(Group).filter(
            Group.path.descendant_of(request.context.path),
            Group.path != request.context.path,
        ).all())

    default_settings = _get_default_settings(request, order)

    if not order:
        order = default_settings.order

    if period is missing:
        period = default_settings.period

    # force Newest sort order, and All Time period, for RSS feeds
    if is_atom or is_rss:
        order = TopicSortOption.NEW
        period = None

    # set up the basic query for topics
    query = (request.query(Topic).join_all_relationships().inside_groups(
        groups, include_subgroups=not is_home_page).exclude_ignored().
             apply_sort_option(order))

    # restrict the time period, if not set to "all time"
    if period:
        query = query.inside_time_period(period)

    # restrict to a specific tag, if we're viewing a single one
    if tag:
        query = query.has_tag(str(tag))

    # apply before/after pagination restrictions if relevant
    if before:
        query = query.before_id36(before)

    if after:
        query = query.after_id36(after)

    # apply topic tag filters unless they're disabled
    if request.user and request.user.filtered_topic_tags and not unfiltered:
        filtered_topic_tags = request.user.filtered_topic_tags

        # if viewing single tag, don't filter that tag and its ancestors
        # for example, if viewing "ask.survey", don't filter "ask.survey" or "ask"
        if tag:
            filtered_topic_tags = [
                ft for ft in filtered_topic_tags
                if not tag.descendant_of(ft.replace(" ", "_"))
            ]

        query = query.filter(~Topic.tags.descendant_of(  # type: ignore
            any_(cast(filtered_topic_tags, TagList))))

    topics = query.get_page(per_page)

    period_options = [
        SimpleHoursPeriod(hours) for hours in (1, 12, 24, 72, 168)
    ]

    # add the current period to the bottom of the dropdown if it's not one of the
    # "standard" ones
    if period and period not in period_options:
        period_options.append(period)

    if isinstance(request.context, Group):
        wiki_pages = (request.query(GroupWikiPage).filter(
            GroupWikiPage.group == request.context).order_by(
                GroupWikiPage.path).all())

        # remove the index from the page list, we'll output it separately
        if any(page.path == "index" for page in wiki_pages):
            wiki_has_index = True
            wiki_pages = [page for page in wiki_pages if page.path != "index"]
        else:
            wiki_has_index = False
    else:
        wiki_pages = None
        wiki_has_index = False

    if isinstance(request.context, Group):
        # Get the most recent topic from each scheduled topic in this group
        group_schedules = (
            request.query(TopicSchedule).options(
                joinedload(TopicSchedule.latest_topic)).filter(
                    TopicSchedule.group == request.context,
                    TopicSchedule.next_post_time != None,  # noqa
                ).order_by(TopicSchedule.next_post_time).all())
        most_recent_scheduled_topics = [
            schedule.latest_topic for schedule in group_schedules
        ]
    else:
        most_recent_scheduled_topics = []

    if is_home_page:
        financial_data = get_financial_data(request.db_session)
    else:
        financial_data = None

    if is_atom:
        request.response.content_type = "application/atom+xml"
    if is_rss:
        request.response.content_type = "application/rss+xml"

    return {
        "group":
        request.context,
        "groups":
        groups,
        "topics":
        topics,
        "order":
        order,
        "order_options":
        TopicSortOption,
        "period":
        period,
        "period_options":
        period_options,
        "is_default_period":
        period == default_settings.period,
        "is_default_view": (period == default_settings.period
                            and order == default_settings.order),
        "rank_start":
        rank_start,
        "tag":
        tag,
        "unfiltered":
        unfiltered,
        "wiki_pages":
        wiki_pages,
        "wiki_has_index":
        wiki_has_index,
        "subgroups":
        subgroups,
        "most_recent_scheduled_topics":
        most_recent_scheduled_topics,
        "financial_data":
        financial_data,
        "current_time":
        utc_now(),
    }
Пример #10
0
def get_group_topics(
    request: Request,
    order: Any,  # more specific would be better, but missing isn't typed
    period: Any,  # more specific would be better, but missing isn't typed
    after: str,
    before: str,
    per_page: int,
    rank_start: Optional[int],
    tag: Optional[Ltree],
    unfiltered: bool,
) -> dict:
    """Get a listing of topics in the group."""
    # pylint: disable=too-many-arguments
    if request.matched_route.name == 'home':
        # on the home page, include topics from the user's subscribed groups
        groups = [sub.group for sub in request.user.subscriptions]
    else:
        # otherwise, just topics from the single group that we're looking at
        groups = [request.context]

    default_settings = _get_default_settings(request, order)

    if order is missing:
        order = default_settings.order

    if period is missing:
        period = default_settings.period

    # set up the basic query for topics
    query = (request.query(Topic).join_all_relationships().inside_groups(
        groups).apply_sort_option(order))

    # restrict the time period, if not set to "all time"
    if period:
        query = query.inside_time_period(period)

    # restrict to a specific tag, if we're viewing a single one
    if tag:
        query = query.has_tag(tag)

    # apply before/after pagination restrictions if relevant
    if before:
        query = query.before_id36(before)

    if after:
        query = query.after_id36(after)

    # apply topic tag filters unless they're disabled or viewing a single tag
    if not (tag or unfiltered):
        # pylint: disable=protected-access
        query = query.filter(~Topic._tags.overlap(  # type: ignore
            request.user._filtered_topic_tags))

    topics = query.get_page(per_page)

    period_options = [SimpleHoursPeriod(hours) for hours in (1, 12, 24, 72)]

    # add the current period to the bottom of the dropdown if it's not one of
    # the "standard" ones
    if period and period not in period_options:
        period_options.append(period)

    return {
        'group':
        request.context,
        'topics':
        topics,
        'order':
        order,
        'order_options':
        TopicSortOption,
        'period':
        period,
        'period_options':
        period_options,
        'is_default_period':
        period == default_settings.period,
        'is_default_view': (period == default_settings.period
                            and order == default_settings.order),
        'rank_start':
        rank_start,
        'tag':
        tag,
        'unfiltered':
        unfiltered,
    }