mirror of https://github.com/zulip/zulip.git
Finish annotating zerver/views/messages.py.
These annotations aren't perfect because the sqlalchemy stubs in typeshed are broken (e.g. a `Select` doesn't have the ability to do `.where()`, but we've at least used some typevars to make it easy to address that when the sqlalchemy stubs are less broken).
This commit is contained in:
parent
e5b8ad6666
commit
e580ed579b
|
@ -9,7 +9,7 @@ from django.db import connection
|
|||
from django.db.models import Q
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from six import text_type
|
||||
from typing import Any, AnyStr, Iterable, Optional, Tuple
|
||||
from typing import Any, AnyStr, Callable, Iterable, Optional, Tuple, Union
|
||||
from zerver.lib.str_utils import force_bytes, force_text
|
||||
|
||||
from zerver.decorator import authenticated_api_view, authenticated_json_post_view, \
|
||||
|
@ -37,7 +37,7 @@ from zerver.models import Message, UserProfile, Stream, Subscription, \
|
|||
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.sql import select, join, column, literal_column, literal, and_, \
|
||||
or_, not_, union_all, alias, Selectable
|
||||
or_, not_, union_all, alias, Selectable, Select, ColumnElement
|
||||
|
||||
import re
|
||||
import ujson
|
||||
|
@ -48,12 +48,17 @@ import six
|
|||
|
||||
class BadNarrowOperator(JsonableError):
|
||||
def __init__(self, desc, status_code=400):
|
||||
# type: (str, int) -> None
|
||||
self.desc = desc
|
||||
self.status_code = status_code
|
||||
|
||||
def to_json_error_msg(self):
|
||||
# type: () -> str
|
||||
return _('Invalid narrow operator: {}').format(self.desc)
|
||||
|
||||
Query = Any # TODO: Should be Select, but sqlalchemy stubs are busted
|
||||
ConditionTransform = Any # TODO: should be Callable[[ColumnElement], ColumnElement], but sqlalchemy stubs are busted
|
||||
|
||||
# When you add a new operator to this, also update zerver/lib/narrow.py
|
||||
class NarrowBuilder(object):
|
||||
def __init__(self, user_profile, msg_id_column):
|
||||
|
@ -62,6 +67,8 @@ class NarrowBuilder(object):
|
|||
self.msg_id_column = msg_id_column
|
||||
|
||||
def add_term(self, query, term):
|
||||
# type: (Query, Dict[str, Any]) -> Query
|
||||
|
||||
# We have to be careful here because we're letting users call a method
|
||||
# by name! The prefix 'by_' prevents it from colliding with builtin
|
||||
# Python __magic__ stuff.
|
||||
|
@ -83,6 +90,7 @@ class NarrowBuilder(object):
|
|||
return method(query, operand, maybe_negate)
|
||||
|
||||
def by_has(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
if operand not in ['attachment', 'image', 'link']:
|
||||
raise BadNarrowOperator("unknown 'has' operand " + operand)
|
||||
col_name = 'has_' + operand
|
||||
|
@ -90,6 +98,7 @@ class NarrowBuilder(object):
|
|||
return query.where(maybe_negate(cond))
|
||||
|
||||
def by_in(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
if operand == 'home':
|
||||
conditions = exclude_muting_conditions(self.user_profile, [])
|
||||
return query.where(and_(*conditions))
|
||||
|
@ -99,6 +108,7 @@ class NarrowBuilder(object):
|
|||
raise BadNarrowOperator("unknown 'in' operand " + operand)
|
||||
|
||||
def by_is(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
if operand == 'private':
|
||||
query = query.select_from(join(query.froms[0], "zerver_recipient",
|
||||
column("recipient_id") ==
|
||||
|
@ -118,6 +128,7 @@ class NarrowBuilder(object):
|
|||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
|
||||
|
||||
def _pg_re_escape(self, pattern):
|
||||
# type: (text_type) -> text_type
|
||||
"""
|
||||
Escape user input to place in a regex
|
||||
|
||||
|
@ -139,6 +150,7 @@ class NarrowBuilder(object):
|
|||
return ''.join(s)
|
||||
|
||||
def by_stream(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
stream = get_stream(operand, self.user_profile.realm)
|
||||
if stream is None:
|
||||
raise BadNarrowOperator('unknown stream ' + operand)
|
||||
|
@ -164,6 +176,7 @@ class NarrowBuilder(object):
|
|||
return query.where(maybe_negate(cond))
|
||||
|
||||
def by_topic(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
if self.user_profile.realm.is_zephyr_mirror_realm:
|
||||
# MIT users expect narrowing to topic "foo" to also show messages to /^foo(.d)*$/
|
||||
# (foo, foo.d, foo.d.d, etc)
|
||||
|
@ -187,6 +200,7 @@ class NarrowBuilder(object):
|
|||
return query.where(maybe_negate(cond))
|
||||
|
||||
def by_sender(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
try:
|
||||
sender = get_user_profile_by_email(operand)
|
||||
except UserProfile.DoesNotExist:
|
||||
|
@ -196,13 +210,16 @@ class NarrowBuilder(object):
|
|||
return query.where(maybe_negate(cond))
|
||||
|
||||
def by_near(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
return query
|
||||
|
||||
def by_id(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
cond = self.msg_id_column == literal(operand)
|
||||
return query.where(maybe_negate(cond))
|
||||
|
||||
def by_pm_with(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
if ',' in operand:
|
||||
# Huddle
|
||||
try:
|
||||
|
@ -236,12 +253,14 @@ class NarrowBuilder(object):
|
|||
return query.where(maybe_negate(cond))
|
||||
|
||||
def by_search(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
if settings.USING_PGROONGA:
|
||||
return self._by_search_pgroonga(query, operand, maybe_negate)
|
||||
else:
|
||||
return self._by_search_tsearch(query, operand, maybe_negate)
|
||||
|
||||
def _by_search_pgroonga(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
match_positions_byte = func.pgroonga.match_positions_byte
|
||||
query_extract_keywords = func.pgroonga.query_extract_keywords
|
||||
keywords = query_extract_keywords(operand)
|
||||
|
@ -253,6 +272,7 @@ class NarrowBuilder(object):
|
|||
return query.where(maybe_negate(condition))
|
||||
|
||||
def _by_search_tsearch(self, query, operand, maybe_negate):
|
||||
# type: (Query, str, ConditionTransform) -> Query
|
||||
tsquery = func.plainto_tsquery(literal("zulip.english_us_search"), literal(operand))
|
||||
ts_locs_array = func.ts_match_locs_array
|
||||
query = query.column(ts_locs_array(literal("zulip.english_us_search"),
|
||||
|
@ -339,6 +359,8 @@ def narrow_parameter(json):
|
|||
raise ValueError("argument is not a list")
|
||||
|
||||
def convert_term(elem):
|
||||
# type: (Union[Dict, List]) -> Dict[str, Any]
|
||||
|
||||
# We have to support a legacy tuple format.
|
||||
if isinstance(elem, list):
|
||||
if (len(elem) != 2
|
||||
|
@ -455,6 +477,7 @@ def exclude_muting_conditions(user_profile, narrow):
|
|||
|
||||
if muted_topics:
|
||||
def mute_cond(muted):
|
||||
# type: (Tuple[str, str]) -> Selectable
|
||||
stream_cond = column("recipient_id") == recipient_map[muted[0].lower()]
|
||||
topic_cond = func.upper(column("subject")) == func.upper(muted[1])
|
||||
return and_(stream_cond, topic_cond)
|
||||
|
|
Loading…
Reference in New Issue