hyperboria/nexus/views/telegram/base_view_builder.py
the-superpirate 43be16e4bc - [nexus] Update schema
- [nexus] Remove outdated protos
  - [nexus] Development
  - [nexus] Development
  - [nexus] Development
  - [nexus] Development
  - [nexus] Development
  - [nexus] Refactor views
  - [nexus] Update aiosumma
  - [nexus] Add tags
  - [nexus] Development
  - [nexus] Update repository
  - [nexus] Update repository
  - [nexus] Update dependencies
  - [nexus] Update dependencies
  - [nexus] Fixes for MetaAPI
  - [nexus] Support for new queries
  - [nexus] Adopt new versions of search
  - [nexus] Improving Nexus
  - [nexus] Various fixes
  - [nexus] Add profile
  - [nexus] Fixes for ingestion
  - [nexus] Refactorings and bugfixes
  - [idm] Add profile methods
  - [nexus] Fix stalled nexus-meta bugs
  - [nexus] Various bugfixes
  - [nexus] Restore IDM API functionality

GitOrigin-RevId: a0842345a6dde5b321279ab5510a50c0def0e71a
2022-09-02 19:15:47 +03:00

390 lines
13 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import datetime
import math
import re
from typing import Optional
from urllib.parse import quote
import numpy as np
from izihawa_types.datetime import CustomDatetime
from library.telegram.common import close_button
from nexus.translations import t
from nlptools.izihawa_nlptools.utils import (
despace_full,
escape_format,
)
from telethon import Button
from .common import (
TooLongQueryError,
encode_query_to_deep_link,
)
def highlight_markdown(snippet):
markdowned = b''
start_from = 0
for highlight in snippet.highlights:
from_ = getattr(highlight, 'from')
to = highlight.to
markdowned += escape_format(snippet.fragment[start_from:from_])
markdowned += b'**'
markdowned += escape_format(snippet.fragment[from_:to])
markdowned += b'**'
start_from = to
markdowned += escape_format(snippet.fragment[start_from:])
markdowned = markdowned.decode()
markdowned = despace_full(re.sub(r'\n+', ' ', markdowned))
if markdowned[0].islower() or (markdowned[:2] == '**' and markdowned[2].islower()):
markdowned = '...' + markdowned
markdowned = markdowned + '...'
markdowned = f'__{markdowned}__'
return markdowned
class TextPart:
def __init__(self, part):
self.part = str(part)
self._bold = False
self._italic = False
self._brackets = False
self._escaped = False
self._clickable = False
self._unbreakable = False
def escaped(self):
self._escaped = True
return self
def bold(self):
self._bold = True
return self
def italic(self):
self._italic = True
return self
def within_brackets(self):
self._brackets = True
return self
def clickable(self):
self._clickable = True
return self
def unbreakable(self):
self._unbreakable = True
return self
def limits(self, limit, with_dots: bool = False):
if len(self.part) > limit:
if self._unbreakable:
self.part = ''
else:
self.part = self.part[:limit]
if with_dots:
self.part += '...'
def __add__(self, other):
self.part += str(other)
def __len__(self):
return len(self.part)
def __str__(self):
r = self.part
if not self._escaped:
r = escape_format(r)
if self._clickable:
r = f'`{r}`'
if self._brackets:
r = f'({r})'
if self._bold:
r = f'**{r}**'
if self._italic:
r = f'__{r}__'
return r
class BaseViewBuilder:
icon = ''
def __init__(self, document_holder, user_language):
self.document_holder = document_holder
self.user_language = user_language
self.last_limit_part = None
self.last_limit = None
self.parts = []
self.has_cover = False
def has_field(self, name):
return self.document_holder.has_field(name)
def add(self, el, bold=False, italic=False, escaped=False, clickable=False, lower=False, with_brackets=False, unbreakable=False,
eol: Optional[str] = ' '):
if el:
if not isinstance(el, TextPart):
el = TextPart(el if not lower else el.lower())
if bold:
el.bold()
if italic:
el.italic()
if escaped:
el.escaped()
if clickable:
el.clickable()
if with_brackets:
el.within_brackets()
if unbreakable:
el.unbreakable()
self.parts.append(el)
if eol is not None:
self.parts.append(TextPart(eol))
return self
def limits(self, limit=None, with_dots: bool = False):
if limit:
current_length = 0
for i, part in enumerate(self.parts):
current_length += len(part)
if current_length > limit:
part.limits(limit + len(part) - current_length, with_dots=with_dots)
if len(part) > 0:
self.parts = self.parts[:i + 1]
else:
self.parts = self.parts[:i]
return self
return self
def limited(self, limit):
self.last_limit_part = len(self.parts)
self.last_limit = limit
return self
def end_limited(self, with_dots: bool = False):
if self.last_limit is not None:
current_length = 0
for i, part in enumerate(self.parts[self.last_limit_part:]):
current_length += len(part)
if current_length > self.last_limit:
part.limits(current_length - self.last_limit, with_dots=with_dots)
if len(part) > 0:
self.parts = self.parts[:self.last_limit_part + i + 1]
else:
self.parts = self.parts[:self.last_limit_part + i]
return self
self.last_limit = None
self.last_limit_part = None
return self
def add_icon(self):
return self.add(self.icon)
def add_label(self, label_name, bold=True, lower=False):
return self.add(t(label_name, self.user_language) + ':', bold=bold, lower=lower)
def add_new_line(self, n: int = 1):
return self.add('\n' * n, eol=None)
def add_formatted_datetime(self):
if self.has_field('issued_at') and self.document_holder.issued_at != -62135596800:
dt = CustomDatetime(dt=np.datetime64(self.document_holder.issued_at, 's'))
try:
ct = datetime.date(dt.year, dt.month, 1)
if datetime.date.today() - datetime.timedelta(days=365) < ct:
self.add(TextPart(f'{dt.year}.{dt.month:02d}').within_brackets())
else:
self.add(TextPart(str(dt.year)).within_brackets())
except ValueError:
pass
return self
def add_downloads_count(self):
return self.add(f'{math.log1p(self.document_holder.downloads_count):.1f}')
def add_links(self, bot_name):
self.add_label("LINKS")
self.add(" - ".join(self.document_holder.generate_links(bot_name)), escaped=True)
return self
def add_tags(self, bot_name):
tag_links = self.document_holder.generate_tags_links(bot_name)
if not tag_links:
return self
self.add(tag_links[0], escaped=True, unbreakable=True)
for tag_link in tag_links[1:]:
self.add('- ' + tag_link, escaped=True, unbreakable=True)
return self
def add_authors(self, et_al=True, first_n_authors=1, on_newline=True):
et_al_suffix = (' et al' if et_al else '')
if self.document_holder.authors:
if on_newline:
self.add_new_line()
if len(self.document_holder.authors) > first_n_authors:
self.add('; '.join(self.document_holder.authors[:first_n_authors]) + et_al_suffix)
elif len(self.document_holder.authors) == 1:
if self.document_holder.authors[0].count(';') >= 1:
comma_authors = list(map(str.strip, self.document_holder.authors[0].split(';')))
if len(comma_authors) > first_n_authors:
self.add('; '.join(comma_authors[:first_n_authors]) + et_al_suffix)
else:
self.add('; '.join(comma_authors))
elif self.document_holder.authors[0].count(',') >= 1:
comma_authors = list(map(str.strip, self.document_holder.authors[0].split(',')))
if len(comma_authors) > first_n_authors:
self.add('; '.join(comma_authors[:first_n_authors]) + et_al_suffix)
else:
self.add('; '.join(comma_authors))
else:
self.add(self.document_holder.authors[0])
else:
self.add('; '.join(self.document_holder.authors))
return self
def add_doi(self, clickable=True, with_brackets=False, with_leading_pipe=False):
if self.document_holder.doi:
if with_leading_pipe:
self.add('|')
return self.add(self.document_holder.doi, clickable=clickable, with_brackets=with_brackets)
def add_doi_link(self, with_leading_pipe=False, on_newline=False, label=False, text=None, end_newline=False):
if self.document_holder.doi:
if on_newline:
self.add_new_line()
if with_leading_pipe:
self.add('|')
if label:
self.add('DOI:', bold=True)
escaped_doi = escape_format(self.document_holder.doi)
if text is None:
text = escaped_doi
self.add(f'[{text}](https://doi.org/{quote(escaped_doi)})', escaped=True)
if end_newline:
self.add_new_line()
return self
def add_references_counter(self, bot_name, with_leading_pipe=False):
if self.has_field('referenced_by_count') and self.document_holder.referenced_by_count:
if with_leading_pipe:
self.add('|')
text = f'🔗 {self.document_holder.referenced_by_count}'
if self.document_holder.doi:
try:
link = encode_query_to_deep_link(f'refs:{self.document_holder.doi}', bot_name=bot_name)
text = f'[{text}]({link})'
except TooLongQueryError:
pass
self.add(text, escaped=True)
return self
def add_short_description(self):
return (
self.add_icon()
.add_title()
.limits(250, with_dots=True)
.add_locator()
)
def add_view(self, bot_name):
return (
self.add_cover_url()
.add_icon()
.add_title()
.limits(400, with_dots=True)
.add_locator(first_n_authors=5)
.add_new_line(2)
.add_stats()
.add_links(bot_name=bot_name)
.add_new_line(2)
.add_description()
.limits(1500, with_dots=True)
.add_new_line(2)
.add_tags(bot_name=bot_name)
.limits(3000)
)
def add_title(self):
raise NotImplementedError()
def add_description(self):
raise NotImplementedError()
def add_locator(self, first_n_authors=1, markup=True):
raise NotImplementedError()
def add_filedata(self, with_leading_pipe=False):
raise NotImplementedError()
def add_stats(self, end_newline=True):
return self
def add_cover_url(self):
cover_url = self.document_holder.get_cover_url()
if cover_url:
self.has_cover = True
self.add(f'[]({cover_url})', escaped=True, unbreakable=True)
return self
def add_isbns(self, on_newline=False, label=False, end_newline=False):
if self.document_holder.isbns:
if on_newline:
self.add_new_line()
if label:
self.add('ISBN:', bold=True)
self.add(', '.join(self.document_holder.isbns[:2]))
if end_newline:
self.add_new_line()
return self
def build(self):
text = ''.join(map(str, self.parts)).strip()
text = re.sub('\n\n+', '\n\n', text)
return text
class BaseButtonsBuilder:
def __init__(self, document_holder, user_language):
self.document_holder = document_holder
self.user_language = user_language
self.buttons = [[]]
def add_back_button(self, back_command):
self.buttons[-1].append(
Button.inline(
text='⬅️',
data=back_command
)
)
return self
def add_download_button(self, session_id, position: int = 0):
# ⬇️ is a mark, Find+F over sources before replacing
self.buttons[-1].append(
Button.inline(
text=f'⬇️ GET',
data=self.document_holder.get_download_command(session_id=session_id, position=position),
)
)
return self
def add_remote_download_button(self, bot_name):
# ⬇️ is a mark, Find+F over sources before replacing
self.buttons[-1].append(
Button.url('', self.document_holder.get_deep_id_link(bot_name))
)
return self
def add_close_button(self, session_id):
self.buttons[-1].append(close_button(session_id))
return self
def add_new_line(self):
self.buttons.append([])
return self
def add_default_layout(self, bot_name, session_id, position: int = 0):
raise NotImplementedError()
def build(self):
return self.buttons