mirror of
https://github.com/nexus-stc/hyperboria
synced 2024-11-23 19:46:49 +01:00
- 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:
parent
422959914c
commit
6de3fb1250
@ -79,8 +79,6 @@ telegram:
|
||||
- nexus.bot.handlers.download.DownloadHandler
|
||||
- nexus.bot.handlers.emoji.EmojiHandler
|
||||
- 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.settings.SettingsButtonsHandler
|
||||
- nexus.bot.handlers.settings.SettingsHandler
|
||||
@ -88,6 +86,7 @@ telegram:
|
||||
- nexus.bot.handlers.submit.SubmitHandler
|
||||
- nexus.bot.handlers.start.StartHandler
|
||||
- nexus.bot.handlers.stop.StopHandler
|
||||
- nexus.bot.handlers.top_missed.TopMissedHandler
|
||||
- nexus.bot.handlers.view.ViewHandler
|
||||
- nexus.bot.handlers.vote.VoteHandler
|
||||
- nexus.bot.handlers.noop.NoopHandler
|
||||
|
@ -10,7 +10,6 @@ from . import (
|
||||
help,
|
||||
legacy,
|
||||
noop,
|
||||
referencing_to,
|
||||
roll,
|
||||
search,
|
||||
settings,
|
||||
@ -18,10 +17,11 @@ from . import (
|
||||
start,
|
||||
stop,
|
||||
submit,
|
||||
top_missed,
|
||||
view,
|
||||
vote,
|
||||
)
|
||||
|
||||
__all__ = ['admin', 'ban', 'contact', 'copyright', 'close', 'donate', 'download', 'emoji', 'help',
|
||||
'legacy', 'noop', 'referencing_to', 'roll', 'search', 'settings',
|
||||
'shortlink', 'start', 'stop', 'submit', 'view', 'vote']
|
||||
'legacy', 'noop', 'roll', 'search', 'settings',
|
||||
'shortlink', 'start', 'stop', 'submit', 'top_missed', 'view', 'vote']
|
||||
|
@ -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,
|
||||
)
|
@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import re
|
||||
import time
|
||||
from abc import ABC
|
||||
|
||||
from grpc import StatusCode
|
||||
from grpc.experimental.aio import AioRpcError
|
||||
@ -24,8 +25,9 @@ from .base import (
|
||||
)
|
||||
|
||||
|
||||
class BaseSearchHandler(BaseHandler):
|
||||
should_reset_last_widget = False
|
||||
class BaseSearchHandler(BaseHandler, ABC):
|
||||
def preprocess_query(self, query):
|
||||
return query.replace(f'@{self.application.config["telegram"]["bot_external_name"]}', '').strip()
|
||||
|
||||
async def do_search(
|
||||
self,
|
||||
@ -38,10 +40,9 @@ class BaseSearchHandler(BaseHandler):
|
||||
):
|
||||
session_id = self.generate_session_id()
|
||||
message_id = prefetch_message.id
|
||||
|
||||
request_context.add_default_fields(is_group_mode=is_group_mode, mode='search', session_id=session_id)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
search_widget = await SearchWidget.create(
|
||||
application=self.application,
|
||||
@ -152,7 +153,7 @@ class SearchHandler(BaseSearchHandler):
|
||||
|
||||
def parse_pattern(self, event: events.ChatAction):
|
||||
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
|
||||
|
||||
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
|
||||
try:
|
||||
await self.do_search(
|
||||
event, request_context, prefetch_message,
|
||||
event=event,
|
||||
request_context=request_context,
|
||||
prefetch_message=prefetch_message,
|
||||
query=query,
|
||||
is_group_mode=is_group_mode,
|
||||
is_shortpath_enabled=True,
|
||||
@ -205,7 +208,7 @@ class SearchEditHandler(BaseSearchHandler):
|
||||
|
||||
def parse_pattern(self, event: events.ChatAction):
|
||||
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
|
||||
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:
|
||||
request_context.statbox(action='resolved')
|
||||
return await self.do_search(
|
||||
event,
|
||||
request_context,
|
||||
event=event,
|
||||
request_context=request_context,
|
||||
prefetch_message=next_message,
|
||||
query=query,
|
||||
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]+)$')
|
||||
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()
|
||||
message_id = int(event.pattern_match.group(2).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)
|
||||
start_time = time.time()
|
||||
|
||||
@ -265,7 +276,7 @@ class SearchPagingHandler(BaseCallbackQueryHandler):
|
||||
try:
|
||||
if not reply_message:
|
||||
raise MessageHasBeenDeletedError()
|
||||
query = reply_message.raw_text
|
||||
query = self.preprocess_query(reply_message.raw_text)
|
||||
search_widget = await SearchWidget.create(
|
||||
application=self.application,
|
||||
chat=request_context.chat,
|
||||
|
54
nexus/bot/handlers/top_missed.py
Normal file
54
nexus/bot/handlers/top_missed.py
Normal 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,
|
||||
)
|
@ -1,5 +1,4 @@
|
||||
import asyncio
|
||||
import re
|
||||
|
||||
from library.telegram.base import RequestContext
|
||||
from nexus.bot.exceptions import MessageHasBeenDeletedError
|
||||
@ -20,7 +19,6 @@ class ViewHandler(BaseHandler):
|
||||
|
||||
def parse_pattern(self, event: events.ChatAction):
|
||||
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)
|
||||
session_id = event.pattern_match.group(3)
|
||||
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'])
|
||||
|
||||
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):
|
||||
if has_found_old_widget:
|
||||
@ -51,29 +49,14 @@ class ViewHandler(BaseHandler):
|
||||
|
||||
async def compose_back_command(
|
||||
self,
|
||||
parent_view_type,
|
||||
session_id,
|
||||
old_message_id,
|
||||
message_id,
|
||||
page,
|
||||
):
|
||||
back_command = None
|
||||
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
|
||||
return f'/search_{session_id}_{message_id}_{page}'
|
||||
|
||||
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.statbox(action='view', document_id=document_id, position=position, schema=schema)
|
||||
@ -96,9 +79,7 @@ class ViewHandler(BaseHandler):
|
||||
)
|
||||
try:
|
||||
back_command = await self.compose_back_command(
|
||||
parent_view_type=parent_view_type,
|
||||
session_id=session_id,
|
||||
old_message_id=old_message_id,
|
||||
message_id=message_id,
|
||||
page=page,
|
||||
)
|
||||
|
@ -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 nexus.bot.application import TelegramApplication
|
||||
from nexus.meta_api.proto.meta_search_service_pb2 import \
|
||||
ScoredDocument as ScoredDocumentPb
|
||||
from nexus.models.proto.typed_document_pb2 import \
|
||||
TypedDocument as TypedDocumentPb
|
||||
from nexus.translations import t
|
||||
from nexus.views.telegram.common import close_button
|
||||
from nexus.views.telegram.registry import parse_typed_document_to_view
|
||||
@ -15,87 +18,36 @@ class DocumentListWidget:
|
||||
self,
|
||||
application: TelegramApplication,
|
||||
chat: ChatPb,
|
||||
typed_documents: List[TypedDocumentPb],
|
||||
cmd: str,
|
||||
has_next: bool,
|
||||
session_id: str,
|
||||
message_id: int,
|
||||
request_id: str,
|
||||
referencing_to: int,
|
||||
page: int = 0,
|
||||
page_size: int = 5,
|
||||
):
|
||||
self.application = application
|
||||
self.chat = chat
|
||||
self.typed_documents = typed_documents
|
||||
self.cmd = cmd
|
||||
self.has_next = has_next
|
||||
self.session_id = session_id
|
||||
self.message_id = message_id
|
||||
self.request_id = request_id
|
||||
self.referencing_to = referencing_to
|
||||
self.page = page
|
||||
|
||||
@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
|
||||
self.page_size = page_size
|
||||
|
||||
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)]
|
||||
|
||||
serp_elements = [
|
||||
f'Linked to: {self.referencing_to}',
|
||||
]
|
||||
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 = []
|
||||
for position, typed_document in enumerate(self.typed_documents):
|
||||
view = parse_typed_document_to_view(typed_document)
|
||||
serp_elements.append(
|
||||
view.get_snippet(
|
||||
language=self.chat.language,
|
||||
view_command=view_command,
|
||||
limit=512 + 128,
|
||||
)
|
||||
)
|
||||
@ -111,17 +63,17 @@ class DocumentListWidget:
|
||||
buttons = [
|
||||
Button.inline(
|
||||
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',
|
||||
),
|
||||
Button.inline(
|
||||
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',
|
||||
),
|
||||
Button.inline(
|
||||
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',
|
||||
)
|
||||
]
|
||||
|
@ -16,6 +16,10 @@ from nexus.meta_api.proto.documents_service_pb2 import \
|
||||
RollRequest as RollRequestPb
|
||||
from nexus.meta_api.proto.documents_service_pb2 import \
|
||||
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 \
|
||||
TypedDocumentRequest as TypedDocumentRequestPb
|
||||
from nexus.meta_api.proto.documents_service_pb2_grpc import DocumentsStub
|
||||
@ -130,3 +134,23 @@ class MetaApiGrpcClient(AioThing):
|
||||
('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),
|
||||
),
|
||||
)
|
||||
|
@ -13,6 +13,18 @@ message RollResponse {
|
||||
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 {
|
||||
string schema = 1;
|
||||
uint64 document_id = 2;
|
||||
@ -26,4 +38,5 @@ message PutTypedDocumentResponse {}
|
||||
service Documents {
|
||||
rpc get (TypedDocumentRequest) returns (nexus.models.proto.TypedDocument) {}
|
||||
rpc roll (RollRequest) returns (RollResponse) {}
|
||||
rpc top_missed (TopMissedRequest) returns (TopMissedResponse) {}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
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,])
|
||||
|
||||
@ -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(
|
||||
name='TypedDocumentRequest',
|
||||
full_name='nexus.meta_api.proto.TypedDocumentRequest',
|
||||
@ -161,8 +253,8 @@ _TYPEDDOCUMENTREQUEST = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=218,
|
||||
serialized_end=332,
|
||||
serialized_start=407,
|
||||
serialized_end=521,
|
||||
)
|
||||
|
||||
|
||||
@ -186,12 +278,15 @@ _PUTTYPEDDOCUMENTRESPONSE = _descriptor.Descriptor(
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=334,
|
||||
serialized_end=360,
|
||||
serialized_start=523,
|
||||
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['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['PutTypedDocumentResponse'] = _PUTTYPEDDOCUMENTRESPONSE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
@ -210,6 +305,20 @@ RollResponse = _reflection.GeneratedProtocolMessageType('RollResponse', (_messag
|
||||
})
|
||||
_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,), {
|
||||
'DESCRIPTOR' : _TYPEDDOCUMENTREQUEST,
|
||||
'__module__' : 'nexus.meta_api.proto.documents_service_pb2'
|
||||
@ -233,8 +342,8 @@ _DOCUMENTS = _descriptor.ServiceDescriptor(
|
||||
index=0,
|
||||
serialized_options=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
serialized_start=363,
|
||||
serialized_end=543,
|
||||
serialized_start=552,
|
||||
serialized_end=829,
|
||||
methods=[
|
||||
_descriptor.MethodDescriptor(
|
||||
name='get',
|
||||
@ -256,6 +365,16 @@ _DOCUMENTS = _descriptor.ServiceDescriptor(
|
||||
serialized_options=None,
|
||||
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)
|
||||
|
||||
|
@ -27,6 +27,11 @@ class DocumentsStub(object):
|
||||
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,
|
||||
)
|
||||
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):
|
||||
@ -44,6 +49,12 @@ class DocumentsServicer(object):
|
||||
context.set_details('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):
|
||||
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,
|
||||
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(
|
||||
'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,
|
||||
options, channel_credentials,
|
||||
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)
|
||||
|
@ -5,8 +5,8 @@ import "nexus/models/proto/typed_document.proto";
|
||||
|
||||
message ScoredDocument {
|
||||
nexus.models.proto.TypedDocument typed_document = 1;
|
||||
float score = 3;
|
||||
uint32 position = 4;
|
||||
float score = 2;
|
||||
uint32 position = 3;
|
||||
}
|
||||
|
||||
message SearchResponse {
|
||||
|
@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
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,])
|
||||
|
||||
@ -45,14 +45,14 @@ _SCOREDDOCUMENT = _descriptor.Descriptor(
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
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),
|
||||
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='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,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
@ -170,13 +170,6 @@ _SEARCHREQUEST = _descriptor.Descriptor(
|
||||
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='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=[
|
||||
],
|
||||
@ -190,7 +183,7 @@ _SEARCHREQUEST = _descriptor.Descriptor(
|
||||
oneofs=[
|
||||
],
|
||||
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
|
||||
@ -230,8 +223,8 @@ _METASEARCH = _descriptor.ServiceDescriptor(
|
||||
index=0,
|
||||
serialized_options=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
serialized_start=489,
|
||||
serialized_end=588,
|
||||
serialized_start=461,
|
||||
serialized_end=560,
|
||||
methods=[
|
||||
_descriptor.MethodDescriptor(
|
||||
name='search',
|
||||
|
@ -6,6 +6,7 @@ from typing import (
|
||||
)
|
||||
from urllib.parse import quote
|
||||
|
||||
from izihawa_types.safecast import safe_int
|
||||
from izihawa_utils.common import filter_none
|
||||
from nexus.models.proto.scimag_pb2 import Scimag as ScimagPb
|
||||
from nexus.nlptools.utils import (
|
||||
@ -204,16 +205,17 @@ class ScimagView(BaseView, AuthorMixin, DoiMixin, FileMixin, IssuedAtMixin):
|
||||
)
|
||||
if self.ref_by_count:
|
||||
buttons[-1].append(
|
||||
Button.inline(
|
||||
Button.switch_inline(
|
||||
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))
|
||||
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:
|
||||
return f'/va{parent_view_type}_{session_id}_{message_id}_{self.id}_{position}'
|
||||
def get_view_command(self, session_id: str, message_id: int, position: int = 0) -> str:
|
||||
return f'/va_{session_id}_{message_id}_{self.id}_{position}'
|
||||
|
||||
def get_robust_journal(self):
|
||||
if self.type != 'chapter' and self.type != 'book-chapter':
|
||||
@ -232,4 +234,7 @@ class ScimagView(BaseView, AuthorMixin, DoiMixin, FileMixin, IssuedAtMixin):
|
||||
if self.issue:
|
||||
return f'vol. {self.volume}({self.issue})'
|
||||
else:
|
||||
return self.volume
|
||||
if safe_int(self.volume):
|
||||
return f'vol. {self.volume}'
|
||||
else:
|
||||
return self.volume
|
||||
|
Loading…
Reference in New Issue
Block a user