text: # -*- coding: utf-8 -*-
  # Database managers (base classes and SQLite).
  import logging
  import sqlite3
  import os
  
  
  log = logging.getLogger(f'logger.{__name__}')
  
  
  class DBManager:
      """The base class for all database managers."""
  
      def __init__(self, db_settings):
          """The simple constructor."""
          self.name_db = db_settings.get('NAME', '')
  
          if not self.name_db:
              raise ValueError('DB name is empty!')
  
      def get_all_subscribers(self):
          """Returns all subscribers, keywords and schedules in the one list. Must be implemented in children.
  
          Return value:
          list[0] -- subscriber (chat ID), int
          list[1] -- keyword, str
          list[2] -- schedule, str
  
          """
          pass
  
      def get_schedule(self, subscriber):
          """Returns the schedule (str) for the subscriber (chat ID). Must be called and implemented in children.
  
          Keywords arguments:
          subscriber -- chat ID, int
  
          """
          if not subscriber:
              raise ValueError('The argument "subscriber" is empty!')
  
      def save_link(self, subscriber, link):
          """Saves the link used for the subscriber in the database. Must be called and implemented in children.
  
          Keywords arguments:
          subscriber -- chat ID, int
          link -- str
  
          """
          if not subscriber:
              raise ValueError('The argument "subscriber" is empty!')
          if not link:
              raise ValueError('The argument "link" is empty!')
  
      def save_schedule(self, subscriber, keywords, schedule):
          """Saves all the data for the main work in the database. Must be called and implemented in children.
  
          Keywords arguments:
          subscriber -- chat ID, int
          keywords -- query for Google search, str
          schedule -- for auto sending images, str
  
          """
          if not subscriber:
              raise ValueError('The argument "subscriber" is empty!')
          if not keywords:
              raise ValueError('The argument "keywords" is empty!')
          if not schedule:
              raise ValueError('The argument "schedule" is empty!')
  
      def delete_schedule(self, subscriber):
          """Deletes all subscriber data from the database. Must be called and implemented in children.
  
          Keywords arguments:
          subscriber -- chat ID, int
  
          """
          if not subscriber:
              raise ValueError('The argument "subscriber" is empty!')
  
      def create_db(self):
          """Creates the new database and returns the connection or None in case of error.
          Must be called and implemented in children.
  
          It calls if the database is not found. You must check the rights of your database user before.
  
          """
          pass
  
      def get_keywords_and_links(self, subscriber):
          """Returns keywords and used links of the subscriber from database.
          Must be called and implemented in children.
  
          Keywords arguments:
          subscriber -- chat ID. int
  
          """
          if not subscriber:
              raise ValueError('The argument "subscriber" is empty!')
  
  
  class SQLManager(DBManager):
      """The base class for all SQL database managers."""
  
      def __init__(self, db_settings):
          super().__init__(db_settings)
          self.ph = '?'  # Placeholder for sql queries.
  
      def get_connection(self):
          """Returns the connection to database or None in case of error. Must be implemented in children."""
          pass
  
      def create_tables_if_not_exists(self, conn):
          """Creates tables in database. If conn is None, raises ValueError.
  
          Keywords arguments:
          conn -- connection to the database
  
          """
          if conn is None:
              raise ValueError('Argument "conn" is None!')
  
      def save_schedule(self, subscriber, keywords, schedule):
  
          super().save_schedule(subscriber, keywords, schedule)
  
          try:
              conn = self.get_connection()
              cur = conn.cursor()
              cur.execute(f'INSERT INTO subscribers (subscriber, keywords, schedule) '
                          f'VALUES({self.ph}, {self.ph}, {self.ph})',
                          (subscriber, keywords.strip(), schedule))
              cur.close()
              conn.commit()
              conn.close()
  
          except Exception as e:
              log.exception(f'DataBase error when saving schedule: {type(e)} - {str(e)}')
  
      def delete_schedule(self, subscriber):
  
          super().delete_schedule(subscriber)
  
          try:
              conn = self.get_connection()
              cur = conn.cursor()
              cur.execute(f'DELETE FROM used_links WHERE subscriber={self.ph}', (subscriber,))
              cur.execute(f'DELETE FROM subscribers WHERE subscriber={self.ph}', (subscriber,))
              cur.close()
              conn.commit()
              conn.close()
  
          except Exception as e:
              log.exception(f'DataBase error when deleting schedule: {type(e)} - {str(e)}')
  
      def save_link(self, subscriber, link):
  
          super().save_link(subscriber, link)
  
          try:
              conn = self.get_connection()
              cur = conn.cursor()
              cur.execute(f'INSERT INTO used_links (subscriber, link) '
                          f'VALUES({self.ph}, {self.ph})', (subscriber, link))
              cur.close()
              conn.commit()
              conn.close()
  
          except Exception as e:
              log.exception(f'DataBase error when saving link: {type(e)} - {str(e)}')
  
      def get_all_subscribers(self):
  
          result = None
  
          try:
              conn = self.get_connection()
              cur = conn.cursor()
              cur.execute('SELECT subscriber, keywords, schedule FROM subscribers')
              result = cur.fetchall()
              cur.close()
              conn.close()
  
          except Exception as e:
              log.exception(f'DataBase error when getting all subscribers: {type(e)} - {str(e)}')
  
          if (result is None) or (len(result) == 0):
              return []
  
          return list(result)
  
      def get_schedule(self, subscriber):
  
          super().get_schedule(subscriber)
          result = None
  
          try:
              conn = self.get_connection()
              cur = conn.cursor()
              cur.execute(f'SELECT schedule FROM subscribers WHERE subscriber={self.ph}', (subscriber,))
              result = cur.fetchone()
              cur.close()
              conn.close()
  
          except Exception as e:
              log.exception(f'DataBase error when getting schedule: {type(e)} - {str(e)}')
  
          if (result is None) or (len(result) == 0):
              return ''
  
          return result[0].strip()
  
      def get_keywords_and_links(self, subscriber):
  
          super().get_keywords_and_links(subscriber)
          result = None
  
          try:
              conn = self.get_connection()
              cur = conn.cursor()
              cur.execute(f'SELECT subscribers.keywords, used_links.link FROM subscribers LEFT JOIN used_links ON'
                          f' subscribers.subscriber = used_links.subscriber WHERE subscribers.subscriber ={self.ph}',
                          (subscriber,))
              result = cur.fetchall()
              cur.close()
              conn.close()
  
          except Exception as e:
              log.exception(f'DataBase error when getting keywords and links: {type(e)} - {str(e)}')
  
          if (result is None) or (len(result) == 0):
              return []
  
          result = list(result)
          used_links = [link[1] for link in result]
          result = [result[0][0], used_links]
          return result
  
  
  class SQLiteManager(SQLManager):
      """This class implements SQLite support. DO NOT use on Heroku!"""
  
      def __init__(self, db_settings):
  
          super().__init__(db_settings)
  
          cur_dir = os.getcwd()
          self.path_db = os.path.join(cur_dir, self.name_db)
  
      def __repr__(self):
          return f'SQLite base. Path: {self.path_db}\n\rName: {self.name_db}'
  
      def get_connection(self):
          """Returns the connection to database or None in case of error."""
  
          conn = None
  
          try:
              conn = sqlite3.connect(self.path_db)
          except sqlite3.Error as e:
              log.exception(f'DataBase error when connecting: {type(e)} - {str(e)}')
              return conn
  
          if conn is not None:
              self.create_tables_if_not_exists(conn)
  
          return conn
  
      def create_tables_if_not_exists(self, conn):
  
          super().create_tables_if_not_exists(conn)
  
          try:
              conn.executescript("""
                                        BEGIN TRANSACTION;
  
                                        CREATE TABLE IF NOT EXISTS subscribers (
                                        subscriber INTEGER PRIMARY KEY,                                  
                                        keywords TEXT NOT NULL,
                                        schedule TEXT NOT NULL);
  
                                        CREATE TABLE IF NOT EXISTS used_links (
                                        id INTEGER PRIMARY KEY,
                                        subscriber INTEGER NOT NULL,                                  
                                        link TEXT NOT NULL);
  
                                        COMMIT; """)
  
              conn.commit()
  
          except sqlite3.Error as e:
              log.exception(f'DataBase error when creating tables: {type(e)} - {str(e)}')
  