- feat(bot): Add top-missing cmd

- feat(bot): Refactor referencing handler
  - fix(bot): Refactor DocumentListWidget
1 internal commit(s)

GitOrigin-RevId: 9f388e98c7039711927abf4a048b5c45ea7a2fc3
This commit is contained in:
the-superpirate 2021-04-13 17:23:31 +03:00
parent 422959914c
commit 6de3fb1250
14 changed files with 318 additions and 229 deletions

View File

@ -79,8 +79,6 @@ telegram:
- nexus.bot.handlers.download.DownloadHandler - nexus.bot.handlers.download.DownloadHandler
- nexus.bot.handlers.emoji.EmojiHandler - nexus.bot.handlers.emoji.EmojiHandler
- nexus.bot.handlers.help.HelpHandler - nexus.bot.handlers.help.HelpHandler
- nexus.bot.handlers.referencing_to.ReferencingToHandler
- nexus.bot.handlers.referencing_to.ReferencingToPagingHandler
- nexus.bot.handlers.roll.RollHandler - nexus.bot.handlers.roll.RollHandler
- nexus.bot.handlers.settings.SettingsButtonsHandler - nexus.bot.handlers.settings.SettingsButtonsHandler
- nexus.bot.handlers.settings.SettingsHandler - nexus.bot.handlers.settings.SettingsHandler
@ -88,6 +86,7 @@ telegram:
- nexus.bot.handlers.submit.SubmitHandler - nexus.bot.handlers.submit.SubmitHandler
- nexus.bot.handlers.start.StartHandler - nexus.bot.handlers.start.StartHandler
- nexus.bot.handlers.stop.StopHandler - nexus.bot.handlers.stop.StopHandler
- nexus.bot.handlers.top_missed.TopMissedHandler
- nexus.bot.handlers.view.ViewHandler - nexus.bot.handlers.view.ViewHandler
- nexus.bot.handlers.vote.VoteHandler - nexus.bot.handlers.vote.VoteHandler
- nexus.bot.handlers.noop.NoopHandler - nexus.bot.handlers.noop.NoopHandler

View File

@ -10,7 +10,6 @@ from . import (
help, help,
legacy, legacy,
noop, noop,
referencing_to,
roll, roll,
search, search,
settings, settings,
@ -18,10 +17,11 @@ from . import (
start, start,
stop, stop,
submit, submit,
top_missed,
view, view,
vote, vote,
) )
__all__ = ['admin', 'ban', 'contact', 'copyright', 'close', 'donate', 'download', 'emoji', 'help', __all__ = ['admin', 'ban', 'contact', 'copyright', 'close', 'donate', 'download', 'emoji', 'help',
'legacy', 'noop', 'referencing_to', 'roll', 'search', 'settings', 'legacy', 'noop', 'roll', 'search', 'settings',
'shortlink', 'start', 'stop', 'submit', 'view', 'vote'] 'shortlink', 'start', 'stop', 'submit', 'top_missed', 'view', 'vote']

View File

@ -1,95 +0,0 @@
import re
import time
from grpc.experimental.aio import AioRpcError
from library.telegram.base import RequestContext
from nexus.bot.widgets.document_list_widget import DocumentListWidget
from nexus.translations import t
from telethon import events
from .base import BaseCallbackQueryHandler
class ReferencingToHandler(BaseCallbackQueryHandler):
filter = events.CallbackQuery(pattern=re.compile('^/r_([A-Za-z0-9]+)_([0-9]+)', re.DOTALL))
should_reset_last_widget = False
async def do_request(self, request_context: RequestContext, session_id: str, message_id: int, document_id: int, page: int):
start_time = time.time()
try:
document_list_widget = await DocumentListWidget.create(
application=self.application,
chat=request_context.chat,
session_id=session_id,
message_id=message_id,
request_id=request_context.request_id,
referencing_to=document_id,
page=page,
)
except AioRpcError as e:
raise e
action = 'referencing_to_found'
if len(document_list_widget.scored_documents) == 0:
action = 'referencing_to_not_found'
request_context.statbox(
action=action,
duration=time.time() - start_time,
document_id=document_id,
schema='scimag',
)
serp, buttons = await document_list_widget.render()
return await self.application.telegram_client.edit_message(
request_context.chat.chat_id,
message_id,
serp,
buttons=buttons,
link_preview=False,
)
async def handler(self, event, request_context: RequestContext):
session_id = event.pattern_match.group(1).decode()
document_id = int(event.pattern_match.group(2).decode())
request_context.add_default_fields(
mode='referencing_to',
session_id=session_id,
)
prefetch_message = await event.respond(
t("SEARCHING", language=request_context.chat.language),
)
message_id = prefetch_message.id
return await self.do_request(
request_context=request_context,
session_id=session_id,
message_id=message_id,
document_id=document_id,
page=0,
)
class ReferencingToPagingHandler(ReferencingToHandler):
filter = events.CallbackQuery(pattern=re.compile('^/rp_([A-Za-z0-9]+)_([0-9]+)_([0-9]+)_([0-9]+)', re.DOTALL))
async def handler(self, event: events.ChatAction, request_context: RequestContext):
session_id = event.pattern_match.group(1).decode()
message_id = int(event.pattern_match.group(2).decode())
document_id = int(event.pattern_match.group(3).decode())
page = int(event.pattern_match.group(4).decode())
request_context.add_default_fields(
mode='referencing_to_paging',
session_id=session_id,
)
return await self.do_request(
request_context=request_context,
session_id=session_id,
message_id=message_id,
document_id=document_id,
page=page,
)

View File

@ -1,6 +1,7 @@
import asyncio import asyncio
import re import re
import time import time
from abc import ABC
from grpc import StatusCode from grpc import StatusCode
from grpc.experimental.aio import AioRpcError from grpc.experimental.aio import AioRpcError
@ -24,8 +25,9 @@ from .base import (
) )
class BaseSearchHandler(BaseHandler): class BaseSearchHandler(BaseHandler, ABC):
should_reset_last_widget = False def preprocess_query(self, query):
return query.replace(f'@{self.application.config["telegram"]["bot_external_name"]}', '').strip()
async def do_search( async def do_search(
self, self,
@ -38,10 +40,9 @@ class BaseSearchHandler(BaseHandler):
): ):
session_id = self.generate_session_id() session_id = self.generate_session_id()
message_id = prefetch_message.id message_id = prefetch_message.id
request_context.add_default_fields(is_group_mode=is_group_mode, mode='search', session_id=session_id) request_context.add_default_fields(is_group_mode=is_group_mode, mode='search', session_id=session_id)
start_time = time.time() start_time = time.time()
try: try:
search_widget = await SearchWidget.create( search_widget = await SearchWidget.create(
application=self.application, application=self.application,
@ -152,7 +153,7 @@ class SearchHandler(BaseSearchHandler):
def parse_pattern(self, event: events.ChatAction): def parse_pattern(self, event: events.ChatAction):
search_prefix = event.pattern_match.group(1) search_prefix = event.pattern_match.group(1)
query = event.pattern_match.group(2) query = self.preprocess_query(event.pattern_match.group(2))
is_group_mode = event.is_group or event.is_channel is_group_mode = event.is_group or event.is_channel
return search_prefix, query, is_group_mode return search_prefix, query, is_group_mode
@ -185,7 +186,9 @@ class SearchHandler(BaseSearchHandler):
self.application.user_manager.last_widget[request_context.chat.chat_id] = prefetch_message.id self.application.user_manager.last_widget[request_context.chat.chat_id] = prefetch_message.id
try: try:
await self.do_search( await self.do_search(
event, request_context, prefetch_message, event=event,
request_context=request_context,
prefetch_message=prefetch_message,
query=query, query=query,
is_group_mode=is_group_mode, is_group_mode=is_group_mode,
is_shortpath_enabled=True, is_shortpath_enabled=True,
@ -205,7 +208,7 @@ class SearchEditHandler(BaseSearchHandler):
def parse_pattern(self, event: events.ChatAction): def parse_pattern(self, event: events.ChatAction):
search_prefix = event.pattern_match.group(1) search_prefix = event.pattern_match.group(1)
query = event.pattern_match.group(2) query = self.preprocess_query(event.pattern_match.group(2))
is_group_mode = event.is_group or event.is_channel is_group_mode = event.is_group or event.is_channel
return search_prefix, query, is_group_mode return search_prefix, query, is_group_mode
@ -231,8 +234,8 @@ class SearchEditHandler(BaseSearchHandler):
if next_message.is_reply and event.id == next_message.reply_to_msg_id: if next_message.is_reply and event.id == next_message.reply_to_msg_id:
request_context.statbox(action='resolved') request_context.statbox(action='resolved')
return await self.do_search( return await self.do_search(
event, event=event,
request_context, request_context=request_context,
prefetch_message=next_message, prefetch_message=next_message,
query=query, query=query,
is_group_mode=is_group_mode, is_group_mode=is_group_mode,
@ -249,11 +252,19 @@ class SearchPagingHandler(BaseCallbackQueryHandler):
filter = events.CallbackQuery(pattern='^/search_([A-Za-z0-9]+)_([0-9]+)_([0-9]+)$') filter = events.CallbackQuery(pattern='^/search_([A-Za-z0-9]+)_([0-9]+)_([0-9]+)$')
should_reset_last_widget = False should_reset_last_widget = False
async def handler(self, event: events.ChatAction, request_context: RequestContext): def preprocess_query(self, query):
return query.replace(f'@{self.application.config["telegram"]["bot_external_name"]}', '').strip()
def parse_pattern(self, event: events.ChatAction):
session_id = event.pattern_match.group(1).decode() session_id = event.pattern_match.group(1).decode()
message_id = int(event.pattern_match.group(2).decode()) message_id = int(event.pattern_match.group(2).decode())
page = int(event.pattern_match.group(3).decode()) page = int(event.pattern_match.group(3).decode())
return session_id, message_id, page
async def handler(self, event: events.ChatAction, request_context: RequestContext):
session_id, message_id, page = self.parse_pattern(event)
request_context.add_default_fields(mode='search_paging', session_id=session_id) request_context.add_default_fields(mode='search_paging', session_id=session_id)
start_time = time.time() start_time = time.time()
@ -265,7 +276,7 @@ class SearchPagingHandler(BaseCallbackQueryHandler):
try: try:
if not reply_message: if not reply_message:
raise MessageHasBeenDeletedError() raise MessageHasBeenDeletedError()
query = reply_message.raw_text query = self.preprocess_query(reply_message.raw_text)
search_widget = await SearchWidget.create( search_widget = await SearchWidget.create(
application=self.application, application=self.application,
chat=request_context.chat, chat=request_context.chat,

View File

@ -0,0 +1,54 @@
from library.telegram.base import RequestContext
from nexus.bot.widgets.document_list_widget import DocumentListWidget
from nexus.translations import t
from telethon import events
from .base import BaseHandler
class TopMissedHandler(BaseHandler):
filter = events.NewMessage(incoming=True, pattern='^/tm$')
is_group_handler = False
should_reset_last_widget = False
async def do_request(self, request_context: RequestContext, session_id: str, message_id: int, page: int):
response = await self.application.meta_api_client.top_missed(
page=page,
page_size=10,
session_id=session_id,
request_id=request_context.request_id,
)
document_list_widget = DocumentListWidget(
application=self.application,
chat=request_context.chat,
typed_documents=response.typed_documents,
cmd='tm',
has_next=response.has_next,
session_id=session_id,
message_id=message_id,
request_id=request_context.request_id,
page=page,
)
serp, buttons = await document_list_widget.render()
return await self.application.telegram_client.edit_message(
request_context.chat.chat_id,
message_id,
serp,
buttons=buttons,
link_preview=False,
)
async def handler(self, event, request_context: RequestContext):
session_id = self.generate_session_id()
request_context.add_default_fields(mode='top_missed', session_id=session_id)
request_context.statbox()
prefetch_message = await event.reply(t("SEARCHING", language=request_context.chat.language))
message_id = prefetch_message.id
return await self.do_request(
request_context=request_context,
session_id=session_id,
message_id=message_id,
page=0,
)

View File

@ -1,5 +1,4 @@
import asyncio import asyncio
import re
from library.telegram.base import RequestContext from library.telegram.base import RequestContext
from nexus.bot.exceptions import MessageHasBeenDeletedError from nexus.bot.exceptions import MessageHasBeenDeletedError
@ -20,7 +19,6 @@ class ViewHandler(BaseHandler):
def parse_pattern(self, event: events.ChatAction): def parse_pattern(self, event: events.ChatAction):
short_schema = event.pattern_match.group(1) short_schema = event.pattern_match.group(1)
parent_view_type = event.pattern_match.group(2) or 's'
schema = self.short_schema_to_schema(short_schema) schema = self.short_schema_to_schema(short_schema)
session_id = event.pattern_match.group(3) session_id = event.pattern_match.group(3)
old_message_id = int(event.pattern_match.group(4)) old_message_id = int(event.pattern_match.group(4))
@ -29,7 +27,7 @@ class ViewHandler(BaseHandler):
page = int(position / self.application.config['application']['page_size']) page = int(position / self.application.config['application']['page_size'])
return parent_view_type, schema, session_id, old_message_id, document_id, position, page return schema, session_id, old_message_id, document_id, position, page
async def process_widgeting(self, has_found_old_widget, old_message_id, request_context: RequestContext): async def process_widgeting(self, has_found_old_widget, old_message_id, request_context: RequestContext):
if has_found_old_widget: if has_found_old_widget:
@ -51,29 +49,14 @@ class ViewHandler(BaseHandler):
async def compose_back_command( async def compose_back_command(
self, self,
parent_view_type,
session_id, session_id,
old_message_id,
message_id, message_id,
page, page,
): ):
back_command = None return f'/search_{session_id}_{message_id}_{page}'
if parent_view_type == 's':
back_command = f'/search_{session_id}_{message_id}_{page}'
elif parent_view_type == 'r':
messages = (await self.application.telegram_client(
functions.messages.GetMessagesRequest(id=[old_message_id])
)).messages
if not messages:
raise MessageHasBeenDeletedError()
message = messages[0]
referencing_to = re.search(r'Linked to: ([0-9]+)', message.raw_text).group(1)
back_command = f'/rp_{session_id}_{message_id}_{referencing_to}_{page}'
return back_command
async def handler(self, event: events.ChatAction, request_context: RequestContext): async def handler(self, event: events.ChatAction, request_context: RequestContext):
parent_view_type, schema, session_id, old_message_id, document_id, position, page = self.parse_pattern(event) schema, session_id, old_message_id, document_id, position, page = self.parse_pattern(event)
request_context.add_default_fields(mode='view', session_id=session_id) request_context.add_default_fields(mode='view', session_id=session_id)
request_context.statbox(action='view', document_id=document_id, position=position, schema=schema) request_context.statbox(action='view', document_id=document_id, position=position, schema=schema)
@ -96,9 +79,7 @@ class ViewHandler(BaseHandler):
) )
try: try:
back_command = await self.compose_back_command( back_command = await self.compose_back_command(
parent_view_type=parent_view_type,
session_id=session_id, session_id=session_id,
old_message_id=old_message_id,
message_id=message_id, message_id=message_id,
page=page, page=page,
) )

View File

@ -1,9 +1,12 @@
from typing import Optional from typing import (
List,
Optional,
)
from idm.api.proto.chat_manager_service_pb2 import Chat as ChatPb from idm.api.proto.chat_manager_service_pb2 import Chat as ChatPb
from nexus.bot.application import TelegramApplication from nexus.bot.application import TelegramApplication
from nexus.meta_api.proto.meta_search_service_pb2 import \ from nexus.models.proto.typed_document_pb2 import \
ScoredDocument as ScoredDocumentPb TypedDocument as TypedDocumentPb
from nexus.translations import t from nexus.translations import t
from nexus.views.telegram.common import close_button from nexus.views.telegram.common import close_button
from nexus.views.telegram.registry import parse_typed_document_to_view from nexus.views.telegram.registry import parse_typed_document_to_view
@ -15,87 +18,36 @@ class DocumentListWidget:
self, self,
application: TelegramApplication, application: TelegramApplication,
chat: ChatPb, chat: ChatPb,
typed_documents: List[TypedDocumentPb],
cmd: str,
has_next: bool,
session_id: str, session_id: str,
message_id: int, message_id: int,
request_id: str, request_id: str,
referencing_to: int,
page: int = 0, page: int = 0,
page_size: int = 5,
): ):
self.application = application self.application = application
self.chat = chat self.chat = chat
self.typed_documents = typed_documents
self.cmd = cmd
self.has_next = has_next
self.session_id = session_id self.session_id = session_id
self.message_id = message_id self.message_id = message_id
self.request_id = request_id self.request_id = request_id
self.referencing_to = referencing_to
self.page = page self.page = page
self.page_size = page_size
@staticmethod
async def create(
application: TelegramApplication,
chat: ChatPb,
session_id: str,
message_id: int,
request_id: str,
referencing_to: int,
page: int = 0,
) -> 'DocumentListWidget':
document_list_view = DocumentListWidget(
application=application,
chat=chat,
session_id=session_id,
message_id=message_id,
request_id=request_id,
referencing_to=referencing_to,
page=page,
)
await document_list_view._acquire_documents()
return document_list_view
async def _acquire_documents(self):
typed_document_pb = await self.application.meta_api_client.get(
schema='scimag',
document_id=self.referencing_to,
position=0,
request_id=self.request_id,
session_id=self.session_id,
user_id=self.chat.chat_id,
)
self._response = await self.application.meta_api_client.search(
schemas=('scimag',),
query=f'references:"{typed_document_pb.scimag.doi}"',
page=self.page,
request_id=self.request_id,
session_id=self.session_id,
user_id=self.chat.chat_id,
)
@property
def has_next(self) -> bool:
return self._response.has_next
@property
def scored_documents(self) -> list[ScoredDocumentPb]:
return self._response.scored_documents
async def render(self) -> tuple[str, Optional[list]]: async def render(self) -> tuple[str, Optional[list]]:
if not len(self.scored_documents): if not len(self.typed_documents):
return t('COULD_NOT_FIND_ANYTHING', language=self.chat.language), [close_button(self.session_id)] return t('COULD_NOT_FIND_ANYTHING', language=self.chat.language), [close_button(self.session_id)]
serp_elements = [ serp_elements = []
f'Linked to: {self.referencing_to}', for position, typed_document in enumerate(self.typed_documents):
] view = parse_typed_document_to_view(typed_document)
for scored_document in self.scored_documents:
view = parse_typed_document_to_view(scored_document.typed_document)
view_command = view.get_view_command(
session_id=self.session_id,
message_id=self.message_id,
parent_view_type='r',
position=scored_document.position,
)
serp_elements.append( serp_elements.append(
view.get_snippet( view.get_snippet(
language=self.chat.language, language=self.chat.language,
view_command=view_command,
limit=512 + 128, limit=512 + 128,
) )
) )
@ -111,17 +63,17 @@ class DocumentListWidget:
buttons = [ buttons = [
Button.inline( Button.inline(
text='<<1' if self.page > 1 else ' ', text='<<1' if self.page > 1 else ' ',
data=f'/rp_{self.session_id}_{self.message_id}_{self.referencing_to}_0' data=f'/{self.cmd}_{self.session_id}_{self.message_id}_0'
if self.page > 1 else '/noop', if self.page > 1 else '/noop',
), ),
Button.inline( Button.inline(
text=f'<{self.page}' if self.page > 0 else ' ', text=f'<{self.page}' if self.page > 0 else ' ',
data=f'/rp_{self.session_id}_{self.message_id}_{self.referencing_to}_{self.page - 1}' data=f'/{self.cmd}_{self.session_id}_{self.message_id}_{self.page - 1}'
if self.page > 0 else '/noop', if self.page > 0 else '/noop',
), ),
Button.inline( Button.inline(
text=f'{self.page + 2}>' if self.has_next else ' ', text=f'{self.page + 2}>' if self.has_next else ' ',
data=f'/rp_{self.session_id}_{self.message_id}_{self.referencing_to}_{self.page + 1}' data=f'/{self.cmd}_{self.session_id}_{self.message_id}_{self.page + 1}'
if self.has_next else '/noop', if self.has_next else '/noop',
) )
] ]

View File

@ -16,6 +16,10 @@ from nexus.meta_api.proto.documents_service_pb2 import \
RollRequest as RollRequestPb RollRequest as RollRequestPb
from nexus.meta_api.proto.documents_service_pb2 import \ from nexus.meta_api.proto.documents_service_pb2 import \
RollResponse as RollResponsePb RollResponse as RollResponsePb
from nexus.meta_api.proto.documents_service_pb2 import \
TopMissedRequest as TopMissedRequestPb
from nexus.meta_api.proto.documents_service_pb2 import \
TopMissedResponse as TopMissedResponsePb
from nexus.meta_api.proto.documents_service_pb2 import \ from nexus.meta_api.proto.documents_service_pb2 import \
TypedDocumentRequest as TypedDocumentRequestPb TypedDocumentRequest as TypedDocumentRequestPb
from nexus.meta_api.proto.documents_service_pb2_grpc import DocumentsStub from nexus.meta_api.proto.documents_service_pb2_grpc import DocumentsStub
@ -130,3 +134,23 @@ class MetaApiGrpcClient(AioThing):
('request-id', request_id), ('request-id', request_id),
), ),
) )
async def top_missed(
self,
page: int,
page_size: int,
request_id: Optional[str] = None,
session_id: Optional[str] = None,
user_id: Optional[int] = None,
) -> TopMissedResponsePb:
return await self.documents_stub.top_missed(
TopMissedRequestPb(
page=page,
page_size=page_size,
session_id=session_id,
user_id=user_id,
),
metadata=(
('request-id', request_id),
),
)

View File

@ -13,6 +13,18 @@ message RollResponse {
uint64 document_id = 1; uint64 document_id = 1;
} }
message TopMissedRequest {
uint32 page = 1;
uint32 page_size = 2;
string session_id = 3;
int64 user_id = 4;
}
message TopMissedResponse {
repeated nexus.models.proto.TypedDocument typed_documents = 1;
bool has_next = 2;
}
message TypedDocumentRequest { message TypedDocumentRequest {
string schema = 1; string schema = 1;
uint64 document_id = 2; uint64 document_id = 2;
@ -26,4 +38,5 @@ message PutTypedDocumentResponse {}
service Documents { service Documents {
rpc get (TypedDocumentRequest) returns (nexus.models.proto.TypedDocument) {} rpc get (TypedDocumentRequest) returns (nexus.models.proto.TypedDocument) {}
rpc roll (RollRequest) returns (RollResponse) {} rpc roll (RollRequest) returns (RollResponse) {}
rpc top_missed (TopMissedRequest) returns (TopMissedResponse) {}
} }

View File

@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
syntax='proto3', syntax='proto3',
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
serialized_pb=b'\n,nexus/meta_api/proto/documents_service.proto\x12\x14nexus.meta_api.proto\x1a\'nexus/models/proto/typed_document.proto\"D\n\x0bRollRequest\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x0f\n\x07user_id\x18\x03 \x01(\x03\"#\n\x0cRollResponse\x12\x13\n\x0b\x64ocument_id\x18\x01 \x01(\x04\"r\n\x14TypedDocumentRequest\x12\x0e\n\x06schema\x18\x01 \x01(\t\x12\x13\n\x0b\x64ocument_id\x18\x02 \x01(\x04\x12\x10\n\x08position\x18\x03 \x01(\r\x12\x12\n\nsession_id\x18\x04 \x01(\t\x12\x0f\n\x07user_id\x18\x05 \x01(\x03\"\x1a\n\x18PutTypedDocumentResponse2\xb4\x01\n\tDocuments\x12V\n\x03get\x12*.nexus.meta_api.proto.TypedDocumentRequest\x1a!.nexus.models.proto.TypedDocument\"\x00\x12O\n\x04roll\x12!.nexus.meta_api.proto.RollRequest\x1a\".nexus.meta_api.proto.RollResponse\"\x00\x62\x06proto3' serialized_pb=b'\n,nexus/meta_api/proto/documents_service.proto\x12\x14nexus.meta_api.proto\x1a\'nexus/models/proto/typed_document.proto\"D\n\x0bRollRequest\x12\x10\n\x08language\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x0f\n\x07user_id\x18\x03 \x01(\x03\"#\n\x0cRollResponse\x12\x13\n\x0b\x64ocument_id\x18\x01 \x01(\x04\"X\n\x10TopMissedRequest\x12\x0c\n\x04page\x18\x01 \x01(\r\x12\x11\n\tpage_size\x18\x02 \x01(\r\x12\x12\n\nsession_id\x18\x03 \x01(\t\x12\x0f\n\x07user_id\x18\x04 \x01(\x03\"a\n\x11TopMissedResponse\x12:\n\x0ftyped_documents\x18\x01 \x03(\x0b\x32!.nexus.models.proto.TypedDocument\x12\x10\n\x08has_next\x18\x02 \x01(\x08\"r\n\x14TypedDocumentRequest\x12\x0e\n\x06schema\x18\x01 \x01(\t\x12\x13\n\x0b\x64ocument_id\x18\x02 \x01(\x04\x12\x10\n\x08position\x18\x03 \x01(\r\x12\x12\n\nsession_id\x18\x04 \x01(\t\x12\x0f\n\x07user_id\x18\x05 \x01(\x03\"\x1a\n\x18PutTypedDocumentResponse2\x95\x02\n\tDocuments\x12V\n\x03get\x12*.nexus.meta_api.proto.TypedDocumentRequest\x1a!.nexus.models.proto.TypedDocument\"\x00\x12O\n\x04roll\x12!.nexus.meta_api.proto.RollRequest\x1a\".nexus.meta_api.proto.RollResponse\"\x00\x12_\n\ntop_missed\x12&.nexus.meta_api.proto.TopMissedRequest\x1a\'.nexus.meta_api.proto.TopMissedResponse\"\x00\x62\x06proto3'
, ,
dependencies=[nexus_dot_models_dot_proto_dot_typed__document__pb2.DESCRIPTOR,]) dependencies=[nexus_dot_models_dot_proto_dot_typed__document__pb2.DESCRIPTOR,])
@ -106,6 +106,98 @@ _ROLLRESPONSE = _descriptor.Descriptor(
) )
_TOPMISSEDREQUEST = _descriptor.Descriptor(
name='TopMissedRequest',
full_name='nexus.meta_api.proto.TopMissedRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='page', full_name='nexus.meta_api.proto.TopMissedRequest.page', index=0,
number=1, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='page_size', full_name='nexus.meta_api.proto.TopMissedRequest.page_size', index=1,
number=2, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='session_id', full_name='nexus.meta_api.proto.TopMissedRequest.session_id', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='user_id', full_name='nexus.meta_api.proto.TopMissedRequest.user_id', index=3,
number=4, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=218,
serialized_end=306,
)
_TOPMISSEDRESPONSE = _descriptor.Descriptor(
name='TopMissedResponse',
full_name='nexus.meta_api.proto.TopMissedResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='typed_documents', full_name='nexus.meta_api.proto.TopMissedResponse.typed_documents', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='has_next', full_name='nexus.meta_api.proto.TopMissedResponse.has_next', index=1,
number=2, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=308,
serialized_end=405,
)
_TYPEDDOCUMENTREQUEST = _descriptor.Descriptor( _TYPEDDOCUMENTREQUEST = _descriptor.Descriptor(
name='TypedDocumentRequest', name='TypedDocumentRequest',
full_name='nexus.meta_api.proto.TypedDocumentRequest', full_name='nexus.meta_api.proto.TypedDocumentRequest',
@ -161,8 +253,8 @@ _TYPEDDOCUMENTREQUEST = _descriptor.Descriptor(
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=218, serialized_start=407,
serialized_end=332, serialized_end=521,
) )
@ -186,12 +278,15 @@ _PUTTYPEDDOCUMENTRESPONSE = _descriptor.Descriptor(
extension_ranges=[], extension_ranges=[],
oneofs=[ oneofs=[
], ],
serialized_start=334, serialized_start=523,
serialized_end=360, serialized_end=549,
) )
_TOPMISSEDRESPONSE.fields_by_name['typed_documents'].message_type = nexus_dot_models_dot_proto_dot_typed__document__pb2._TYPEDDOCUMENT
DESCRIPTOR.message_types_by_name['RollRequest'] = _ROLLREQUEST DESCRIPTOR.message_types_by_name['RollRequest'] = _ROLLREQUEST
DESCRIPTOR.message_types_by_name['RollResponse'] = _ROLLRESPONSE DESCRIPTOR.message_types_by_name['RollResponse'] = _ROLLRESPONSE
DESCRIPTOR.message_types_by_name['TopMissedRequest'] = _TOPMISSEDREQUEST
DESCRIPTOR.message_types_by_name['TopMissedResponse'] = _TOPMISSEDRESPONSE
DESCRIPTOR.message_types_by_name['TypedDocumentRequest'] = _TYPEDDOCUMENTREQUEST DESCRIPTOR.message_types_by_name['TypedDocumentRequest'] = _TYPEDDOCUMENTREQUEST
DESCRIPTOR.message_types_by_name['PutTypedDocumentResponse'] = _PUTTYPEDDOCUMENTRESPONSE DESCRIPTOR.message_types_by_name['PutTypedDocumentResponse'] = _PUTTYPEDDOCUMENTRESPONSE
_sym_db.RegisterFileDescriptor(DESCRIPTOR) _sym_db.RegisterFileDescriptor(DESCRIPTOR)
@ -210,6 +305,20 @@ RollResponse = _reflection.GeneratedProtocolMessageType('RollResponse', (_messag
}) })
_sym_db.RegisterMessage(RollResponse) _sym_db.RegisterMessage(RollResponse)
TopMissedRequest = _reflection.GeneratedProtocolMessageType('TopMissedRequest', (_message.Message,), {
'DESCRIPTOR' : _TOPMISSEDREQUEST,
'__module__' : 'nexus.meta_api.proto.documents_service_pb2'
# @@protoc_insertion_point(class_scope:nexus.meta_api.proto.TopMissedRequest)
})
_sym_db.RegisterMessage(TopMissedRequest)
TopMissedResponse = _reflection.GeneratedProtocolMessageType('TopMissedResponse', (_message.Message,), {
'DESCRIPTOR' : _TOPMISSEDRESPONSE,
'__module__' : 'nexus.meta_api.proto.documents_service_pb2'
# @@protoc_insertion_point(class_scope:nexus.meta_api.proto.TopMissedResponse)
})
_sym_db.RegisterMessage(TopMissedResponse)
TypedDocumentRequest = _reflection.GeneratedProtocolMessageType('TypedDocumentRequest', (_message.Message,), { TypedDocumentRequest = _reflection.GeneratedProtocolMessageType('TypedDocumentRequest', (_message.Message,), {
'DESCRIPTOR' : _TYPEDDOCUMENTREQUEST, 'DESCRIPTOR' : _TYPEDDOCUMENTREQUEST,
'__module__' : 'nexus.meta_api.proto.documents_service_pb2' '__module__' : 'nexus.meta_api.proto.documents_service_pb2'
@ -233,8 +342,8 @@ _DOCUMENTS = _descriptor.ServiceDescriptor(
index=0, index=0,
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
serialized_start=363, serialized_start=552,
serialized_end=543, serialized_end=829,
methods=[ methods=[
_descriptor.MethodDescriptor( _descriptor.MethodDescriptor(
name='get', name='get',
@ -256,6 +365,16 @@ _DOCUMENTS = _descriptor.ServiceDescriptor(
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
), ),
_descriptor.MethodDescriptor(
name='top_missed',
full_name='nexus.meta_api.proto.Documents.top_missed',
index=2,
containing_service=None,
input_type=_TOPMISSEDREQUEST,
output_type=_TOPMISSEDRESPONSE,
serialized_options=None,
create_key=_descriptor._internal_create_key,
),
]) ])
_sym_db.RegisterServiceDescriptor(_DOCUMENTS) _sym_db.RegisterServiceDescriptor(_DOCUMENTS)

View File

@ -27,6 +27,11 @@ class DocumentsStub(object):
request_serializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollRequest.SerializeToString, request_serializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollRequest.SerializeToString,
response_deserializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollResponse.FromString, response_deserializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollResponse.FromString,
) )
self.top_missed = channel.unary_unary(
'/nexus.meta_api.proto.Documents/top_missed',
request_serializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.TopMissedRequest.SerializeToString,
response_deserializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.TopMissedResponse.FromString,
)
class DocumentsServicer(object): class DocumentsServicer(object):
@ -44,6 +49,12 @@ class DocumentsServicer(object):
context.set_details('Method not implemented!') context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!') raise NotImplementedError('Method not implemented!')
def top_missed(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_DocumentsServicer_to_server(servicer, server): def add_DocumentsServicer_to_server(servicer, server):
rpc_method_handlers = { rpc_method_handlers = {
@ -57,6 +68,11 @@ def add_DocumentsServicer_to_server(servicer, server):
request_deserializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollRequest.FromString, request_deserializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollRequest.FromString,
response_serializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollResponse.SerializeToString, response_serializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollResponse.SerializeToString,
), ),
'top_missed': grpc.unary_unary_rpc_method_handler(
servicer.top_missed,
request_deserializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.TopMissedRequest.FromString,
response_serializer=nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.TopMissedResponse.SerializeToString,
),
} }
generic_handler = grpc.method_handlers_generic_handler( generic_handler = grpc.method_handlers_generic_handler(
'nexus.meta_api.proto.Documents', rpc_method_handlers) 'nexus.meta_api.proto.Documents', rpc_method_handlers)
@ -100,3 +116,20 @@ class Documents(object):
nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollResponse.FromString, nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.RollResponse.FromString,
options, channel_credentials, options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata) insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def top_missed(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/nexus.meta_api.proto.Documents/top_missed',
nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.TopMissedRequest.SerializeToString,
nexus_dot_meta__api_dot_proto_dot_documents__service__pb2.TopMissedResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

View File

@ -5,8 +5,8 @@ import "nexus/models/proto/typed_document.proto";
message ScoredDocument { message ScoredDocument {
nexus.models.proto.TypedDocument typed_document = 1; nexus.models.proto.TypedDocument typed_document = 1;
float score = 3; float score = 2;
uint32 position = 4; uint32 position = 3;
} }
message SearchResponse { message SearchResponse {

View File

@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
syntax='proto3', syntax='proto3',
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
serialized_pb=b'\n.nexus/meta_api/proto/meta_search_service.proto\x12\x14nexus.meta_api.proto\x1a\'nexus/models/proto/typed_document.proto\"l\n\x0eScoredDocument\x12\x39\n\x0etyped_document\x18\x01 \x01(\x0b\x32!.nexus.models.proto.TypedDocument\x12\r\n\x05score\x18\x03 \x01(\x02\x12\x10\n\x08position\x18\x04 \x01(\r\"b\n\x0eSearchResponse\x12>\n\x10scored_documents\x18\x01 \x03(\x0b\x32$.nexus.meta_api.proto.ScoredDocument\x12\x10\n\x08has_next\x18\x02 \x01(\x08\"\xa3\x01\n\rSearchRequest\x12\x0f\n\x07schemas\x18\x01 \x03(\t\x12\r\n\x05query\x18\x02 \x01(\t\x12\x0c\n\x04page\x18\x03 \x01(\r\x12\x11\n\tpage_size\x18\x04 \x01(\r\x12\x10\n\x08language\x18\x05 \x01(\t\x12\x0f\n\x07user_id\x18\x06 \x01(\x03\x12\x12\n\nsession_id\x18\x07 \x01(\t\x12\x1a\n\x12skip_query_rewrite\x18\t \x01(\x08\x32\x63\n\nMetaSearch\x12U\n\x06search\x12#.nexus.meta_api.proto.SearchRequest\x1a$.nexus.meta_api.proto.SearchResponse\"\x00\x62\x06proto3' serialized_pb=b'\n.nexus/meta_api/proto/meta_search_service.proto\x12\x14nexus.meta_api.proto\x1a\'nexus/models/proto/typed_document.proto\"l\n\x0eScoredDocument\x12\x39\n\x0etyped_document\x18\x01 \x01(\x0b\x32!.nexus.models.proto.TypedDocument\x12\r\n\x05score\x18\x02 \x01(\x02\x12\x10\n\x08position\x18\x03 \x01(\r\"b\n\x0eSearchResponse\x12>\n\x10scored_documents\x18\x01 \x03(\x0b\x32$.nexus.meta_api.proto.ScoredDocument\x12\x10\n\x08has_next\x18\x02 \x01(\x08\"\x87\x01\n\rSearchRequest\x12\x0f\n\x07schemas\x18\x01 \x03(\t\x12\r\n\x05query\x18\x02 \x01(\t\x12\x0c\n\x04page\x18\x03 \x01(\r\x12\x11\n\tpage_size\x18\x04 \x01(\r\x12\x10\n\x08language\x18\x05 \x01(\t\x12\x0f\n\x07user_id\x18\x06 \x01(\x03\x12\x12\n\nsession_id\x18\x07 \x01(\t2c\n\nMetaSearch\x12U\n\x06search\x12#.nexus.meta_api.proto.SearchRequest\x1a$.nexus.meta_api.proto.SearchResponse\"\x00\x62\x06proto3'
, ,
dependencies=[nexus_dot_models_dot_proto_dot_typed__document__pb2.DESCRIPTOR,]) dependencies=[nexus_dot_models_dot_proto_dot_typed__document__pb2.DESCRIPTOR,])
@ -45,14 +45,14 @@ _SCOREDDOCUMENT = _descriptor.Descriptor(
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='score', full_name='nexus.meta_api.proto.ScoredDocument.score', index=1, name='score', full_name='nexus.meta_api.proto.ScoredDocument.score', index=1,
number=3, type=2, cpp_type=6, label=1, number=2, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0), has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor( _descriptor.FieldDescriptor(
name='position', full_name='nexus.meta_api.proto.ScoredDocument.position', index=2, name='position', full_name='nexus.meta_api.proto.ScoredDocument.position', index=2,
number=4, type=13, cpp_type=3, label=1, number=3, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0, has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
@ -170,13 +170,6 @@ _SEARCHREQUEST = _descriptor.Descriptor(
message_type=None, enum_type=None, containing_type=None, message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None, is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='skip_query_rewrite', full_name='nexus.meta_api.proto.SearchRequest.skip_query_rewrite', index=7,
number=9, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
], ],
extensions=[ extensions=[
], ],
@ -190,7 +183,7 @@ _SEARCHREQUEST = _descriptor.Descriptor(
oneofs=[ oneofs=[
], ],
serialized_start=324, serialized_start=324,
serialized_end=487, serialized_end=459,
) )
_SCOREDDOCUMENT.fields_by_name['typed_document'].message_type = nexus_dot_models_dot_proto_dot_typed__document__pb2._TYPEDDOCUMENT _SCOREDDOCUMENT.fields_by_name['typed_document'].message_type = nexus_dot_models_dot_proto_dot_typed__document__pb2._TYPEDDOCUMENT
@ -230,8 +223,8 @@ _METASEARCH = _descriptor.ServiceDescriptor(
index=0, index=0,
serialized_options=None, serialized_options=None,
create_key=_descriptor._internal_create_key, create_key=_descriptor._internal_create_key,
serialized_start=489, serialized_start=461,
serialized_end=588, serialized_end=560,
methods=[ methods=[
_descriptor.MethodDescriptor( _descriptor.MethodDescriptor(
name='search', name='search',

View File

@ -6,6 +6,7 @@ from typing import (
) )
from urllib.parse import quote from urllib.parse import quote
from izihawa_types.safecast import safe_int
from izihawa_utils.common import filter_none from izihawa_utils.common import filter_none
from nexus.models.proto.scimag_pb2 import Scimag as ScimagPb from nexus.models.proto.scimag_pb2 import Scimag as ScimagPb
from nexus.nlptools.utils import ( from nexus.nlptools.utils import (
@ -204,16 +205,17 @@ class ScimagView(BaseView, AuthorMixin, DoiMixin, FileMixin, IssuedAtMixin):
) )
if self.ref_by_count: if self.ref_by_count:
buttons[-1].append( buttons[-1].append(
Button.inline( Button.switch_inline(
text=f'🔗 {self.ref_by_count or ""}', text=f'🔗 {self.ref_by_count or ""}',
data=f'/r_{session_id}_{self.id}', query=f'references:"{self.doi}"',
same_peer=True,
) )
) )
buttons[-1].append(close_button(session_id)) buttons[-1].append(close_button(session_id))
return '\n'.join(parts).strip()[:4096], buttons return '\n'.join(parts).strip()[:4096], buttons
def get_view_command(self, session_id: str, message_id: int, parent_view_type: str = 's', position: int = 0) -> str: def get_view_command(self, session_id: str, message_id: int, position: int = 0) -> str:
return f'/va{parent_view_type}_{session_id}_{message_id}_{self.id}_{position}' return f'/va_{session_id}_{message_id}_{self.id}_{position}'
def get_robust_journal(self): def get_robust_journal(self):
if self.type != 'chapter' and self.type != 'book-chapter': if self.type != 'chapter' and self.type != 'book-chapter':
@ -232,4 +234,7 @@ class ScimagView(BaseView, AuthorMixin, DoiMixin, FileMixin, IssuedAtMixin):
if self.issue: if self.issue:
return f'vol. {self.volume}({self.issue})' return f'vol. {self.volume}({self.issue})'
else: else:
return self.volume if safe_int(self.volume):
return f'vol. {self.volume}'
else:
return self.volume