/ allthethings / extensions.py
extensions.py
  1  import os
  2  import random
  3  
  4  from flask_babel import Babel
  5  from flask_debugtoolbar import DebugToolbarExtension
  6  from flask_static_digest import FlaskStaticDigest
  7  from sqlalchemy import Column, Integer, ForeignKey, inspect, create_engine, Text
  8  from sqlalchemy.orm import declarative_base, relationship
  9  from sqlalchemy.ext.declarative import DeferredReflection
 10  from elasticsearch import Elasticsearch
 11  from flask_mail import Mail
 12  from config.settings import ELASTICSEARCH_HOST, ELASTICSEARCHAUX_HOST, ELASTICSEARCH_HOST_PREFERRED, ELASTICSEARCHAUX_HOST_PREFERRED
 13  
 14  debug_toolbar = DebugToolbarExtension()
 15  flask_static_digest = FlaskStaticDigest()
 16  Base = declarative_base()
 17  babel = Babel()
 18  mail = Mail()
 19  
 20  class FallbackNodeSelector: # Selects only the first live node
 21      def __init__(self, node_configs):
 22          self.node_configs = node_configs
 23      def select(self, nodes):
 24          node_configs = list(self.node_configs)
 25          reverse = (random.randint(0, 100) < 10)
 26          if reverse:
 27              node_configs.reverse() # Occasionally pick the fallback to check it.
 28          for node_config in node_configs:
 29              for node in nodes:
 30                  if node.config == node_config:
 31                      if node_config != self.node_configs[0]:
 32                          print(f"FallbackNodeSelector warning: using fallback node! {reverse=} {node_config=}")
 33                      return node
 34          raise Exception("No node_config found!")
 35  
 36  if len(ELASTICSEARCH_HOST_PREFERRED) > 0:
 37      es = Elasticsearch(hosts=[ELASTICSEARCH_HOST_PREFERRED,ELASTICSEARCH_HOST], node_selector_class=FallbackNodeSelector, max_retries=1, retry_on_timeout=False, http_compress=True, randomize_hosts=False)
 38  else:
 39      es = Elasticsearch(hosts=[ELASTICSEARCH_HOST], max_retries=1, retry_on_timeout=False, http_compress=False, randomize_hosts=False)
 40  if len(ELASTICSEARCHAUX_HOST_PREFERRED) > 0:
 41      # Let's not fall back here, because ELASTICSEARCHAUX_HOST is just so slow..
 42      es_aux = Elasticsearch(hosts=[ELASTICSEARCHAUX_HOST_PREFERRED], max_retries=1, retry_on_timeout=False, http_compress=True, randomize_hosts=False)
 43  else:
 44      es_aux = Elasticsearch(hosts=[ELASTICSEARCHAUX_HOST], max_retries=1, retry_on_timeout=False, http_compress=False, randomize_hosts=False)
 45  
 46  mariadb_user = os.getenv("MARIADB_USER", "allthethings")
 47  mariadb_password = os.getenv("MARIADB_PASSWORD", "password")
 48  mariadb_host = os.getenv("MARIADB_HOST", "mariadb")
 49  mariadb_port = os.getenv("MARIADB_PORT", "3306")
 50  mariadb_db = os.getenv("MARIADB_DATABASE", mariadb_user)
 51  mariadb_url = f"mysql+pymysql://{mariadb_user}:{mariadb_password}@{mariadb_host}:{mariadb_port}/{mariadb_db}?read_timeout=120&write_timeout=120"
 52  mariadb_url_no_timeout = f"mysql+pymysql://root:{mariadb_password}@{mariadb_host}:{mariadb_port}/{mariadb_db}"
 53  if os.getenv("DATA_IMPORTS_MODE", "") == "1":
 54      mariadb_url = mariadb_url_no_timeout
 55  engine = create_engine(mariadb_url, future=True, isolation_level="AUTOCOMMIT", pool_size=5, max_overflow=0, pool_recycle=300, pool_pre_ping=True)
 56  
 57  mariapersist_user = os.getenv("MARIAPERSIST_USER", "allthethings")
 58  mariapersist_password = os.getenv("MARIAPERSIST_PASSWORD", "password")
 59  mariapersist_host = os.getenv("MARIAPERSIST_HOST", "mariapersist")
 60  mariapersist_port = os.getenv("MARIAPERSIST_PORT", "3333")
 61  mariapersist_db = os.getenv("MARIAPERSIST_DATABASE", mariapersist_user)
 62  mariapersist_url = f"mysql+pymysql://{mariapersist_user}:{mariapersist_password}@{mariapersist_host}:{mariapersist_port}/{mariapersist_db}?read_timeout=120&write_timeout=120"
 63  mariapersist_engine = create_engine(mariapersist_url, future=True, isolation_level="AUTOCOMMIT", pool_size=5, max_overflow=0, pool_recycle=300, pool_pre_ping=True)
 64  
 65  class Reflected(DeferredReflection, Base):
 66      __abstract__ = True
 67      def to_dict(self):
 68          unloaded = inspect(self).unloaded
 69          return dict((col.name, getattr(self, col.name)) for col in self.__table__.columns if col.name not in unloaded)
 70  
 71  class ReflectedMariapersist(DeferredReflection, Base):
 72      __abstract__ = True
 73      def to_dict(self):
 74          unloaded = db.inspect(self).unloaded
 75          return dict((col.name, getattr(self, col.name)) for col in self.__table__.columns if col.name not in unloaded)
 76  
 77  class ZlibBook(Reflected):
 78      __tablename__ = "zlib_book"
 79      isbns = relationship("ZlibIsbn", lazy="selectin")
 80  class ZlibIsbn(Reflected):
 81      __tablename__ = "zlib_isbn"
 82      zlibrary_id = Column(Integer, ForeignKey("zlib_book.zlibrary_id"))
 83  
 84  class IsbndbIsbns(Reflected):
 85      __tablename__ = "isbndb_isbns"
 86  
 87  class LibgenliFiles(Reflected):
 88      __tablename__ = "libgenli_files"
 89      add_descrs = relationship("LibgenliFilesAddDescr", lazy="selectin")
 90      editions = relationship("LibgenliEditions", lazy="selectin", secondary="libgenli_editions_to_files")
 91  class LibgenliFilesAddDescr(Reflected):
 92      __tablename__ = "libgenli_files_add_descr"
 93      f_id = Column(Integer, ForeignKey("libgenli_files.f_id"))
 94  class LibgenliEditionsToFiles(Reflected):
 95      __tablename__ = "libgenli_editions_to_files"
 96      f_id = Column(Integer, ForeignKey("libgenli_files.f_id"))
 97      e_id = Column(Integer, ForeignKey("libgenli_editions.e_id"))
 98  class LibgenliEditions(Reflected):
 99      __tablename__ = "libgenli_editions"
100      issue_s_id = Column(Integer, ForeignKey("libgenli_series.s_id"))
101      series = relationship("LibgenliSeries", lazy="joined")
102      add_descrs = relationship("LibgenliEditionsAddDescr", lazy="selectin")
103  class LibgenliEditionsAddDescr(Reflected):
104      __tablename__ = "libgenli_editions_add_descr"
105      e_id = Column(Integer, ForeignKey("libgenli_editions.e_id"))
106      publisher = relationship("LibgenliPublishers", lazy="joined", primaryjoin="(remote(LibgenliEditionsAddDescr.value) == foreign(LibgenliPublishers.p_id)) & (LibgenliEditionsAddDescr.key == 308)")
107  class LibgenliPublishers(Reflected):
108      __tablename__ = "libgenli_publishers"
109  class LibgenliSeries(Reflected):
110      __tablename__ = "libgenli_series"
111      issn_add_descrs = relationship("LibgenliSeriesAddDescr", lazy="joined", primaryjoin="(LibgenliSeries.s_id == LibgenliSeriesAddDescr.s_id) & (LibgenliSeriesAddDescr.key == 501)")
112  class LibgenliSeriesAddDescr(Reflected):
113      __tablename__ = "libgenli_series_add_descr"
114      s_id = Column(Integer, ForeignKey("libgenli_series.s_id"))
115  class LibgenliElemDescr(Reflected):
116      __tablename__ = "libgenli_elem_descr"
117  
118  class LibgenrsDescription(Reflected):
119      __tablename__ = "libgenrs_description"
120  class LibgenrsHashes(Reflected):
121      __tablename__ = "libgenrs_hashes"
122  class LibgenrsTopics(Reflected):
123      __tablename__ = "libgenrs_topics"
124  class LibgenrsUpdated(Reflected):
125      __tablename__ = "libgenrs_updated"
126  
127  class LibgenrsFiction(Reflected):
128      __tablename__ = "libgenrs_fiction"
129  class LibgenrsFictionDescription(Reflected):
130      __tablename__ = "libgenrs_fiction_description"
131  class LibgenrsFictionHashes(Reflected):
132      __tablename__ = "libgenrs_fiction_hashes"
133  
134  class OlBase(Reflected):
135      __tablename__ = "ol_base"
136  
137  class AaIa202306Metadata(Reflected):
138      __tablename__ = "aa_ia_2023_06_metadata"
139  class AaIa202306Files(Reflected):
140      __tablename__ = "aa_ia_2023_06_files"
141  class Ia2Records(Reflected):
142      __tablename__ = "annas_archive_meta__aacid__ia2_records"
143  class Ia2AcsmpdfFiles(Reflected):
144      __tablename__ = "annas_archive_meta__aacid__ia2_acsmpdf_files"
145  
146  
147  class MariapersistDownloadsTotalByMd5(ReflectedMariapersist):
148      __tablename__ = "mariapersist_downloads_total_by_md5"
149  class MariapersistAccounts(ReflectedMariapersist):
150      __tablename__ = "mariapersist_accounts"
151  class MariapersistDownloads(ReflectedMariapersist):
152      __tablename__ = "mariapersist_downloads"
153  class MariapersistDownloadsHourlyByMd5(ReflectedMariapersist):
154      __tablename__ = "mariapersist_downloads_hourly_by_md5"
155  class MariapersistDownloadsHourly(ReflectedMariapersist):
156      __tablename__ = "mariapersist_downloads_hourly"
157  class MariapersistMd5Report(ReflectedMariapersist):
158      __tablename__ = "mariapersist_md5_report"
159  class MariapersistComments(ReflectedMariapersist):
160      __tablename__ = "mariapersist_comments"
161  class MariapersistReactions(ReflectedMariapersist):
162      __tablename__ = "mariapersist_reactions"
163  class MariapersistLists(ReflectedMariapersist):
164      __tablename__ = "mariapersist_lists"
165  class MariapersistListEntries(ReflectedMariapersist):
166      __tablename__ = "mariapersist_list_entries"
167  class MariapersistDonations(ReflectedMariapersist):
168      __tablename__ = "mariapersist_donations"
169  class MariapersistCopyrightClaims(ReflectedMariapersist):
170      __tablename__ = "mariapersist_copyright_claims"
171  class MariapersistFastDownloadAccess(ReflectedMariapersist):
172      __tablename__ = "mariapersist_fast_download_access"
173  class MariapersistSmallFiles(ReflectedMariapersist):
174      __tablename__ = "mariapersist_small_files"
175  # class MariapersistSearches(ReflectedMariapersist):
176  #     __tablename__ = "mariapersist_searches"
177  
178