From d25191d991596aa3696e0847234f8388aa5986b7 Mon Sep 17 00:00:00 2001 From: lokas Date: Sun, 10 Sep 2023 08:16:32 +0300 Subject: [PATCH 1/4] init --- tasks/check_usernames/core/__init__.py | 0 .../core/connection/__init__.py | 0 .../core/connection/base_connection.py | 38 ++++++++ .../core/connection/mysql_connection.py | 62 +++++++++++++ .../core/connection/sqlite_connection.py | 48 ++++++++++ .../check_usernames/core/factory/__init__.py | 0 tasks/check_usernames/core/models/__init__.py | 0 tasks/check_usernames/core/models/user.py | 32 +++++++ .../core/persistence/__init__.py | 0 .../core/persistence/base_persistence.py | 54 +++++++++++ .../core/persistence/mysql_persistence.py | 91 +++++++++++++++++++ .../core/persistence/sqlite_persistence.py | 0 .../core/repositories/__init__.py | 0 13 files changed, 325 insertions(+) create mode 100644 tasks/check_usernames/core/__init__.py create mode 100644 tasks/check_usernames/core/connection/__init__.py create mode 100644 tasks/check_usernames/core/connection/base_connection.py create mode 100644 tasks/check_usernames/core/connection/mysql_connection.py create mode 100644 tasks/check_usernames/core/connection/sqlite_connection.py create mode 100644 tasks/check_usernames/core/factory/__init__.py create mode 100644 tasks/check_usernames/core/models/__init__.py create mode 100644 tasks/check_usernames/core/models/user.py create mode 100644 tasks/check_usernames/core/persistence/__init__.py create mode 100644 tasks/check_usernames/core/persistence/base_persistence.py create mode 100644 tasks/check_usernames/core/persistence/mysql_persistence.py create mode 100644 tasks/check_usernames/core/persistence/sqlite_persistence.py create mode 100644 tasks/check_usernames/core/repositories/__init__.py diff --git a/tasks/check_usernames/core/__init__.py b/tasks/check_usernames/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tasks/check_usernames/core/connection/__init__.py b/tasks/check_usernames/core/connection/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tasks/check_usernames/core/connection/base_connection.py b/tasks/check_usernames/core/connection/base_connection.py new file mode 100644 index 00000000..a29d58f6 --- /dev/null +++ b/tasks/check_usernames/core/connection/base_connection.py @@ -0,0 +1,38 @@ +from abc import ABC, abstractmethod + + +class Connection(ABC): + """ + Abstract base class for connection operations with databases and APIs. + This interface provides methods for connecting, disconnecting, and checking the connection status. + + Implementations of this interface can be used with MySQL, SQLite, or other databases, + as well as with API setups. + """ + + @abstractmethod + def connect(self): + """ + Connects to the database or API. + + Raises: + ConnectionError: If the connection fails. + """ + pass + + @abstractmethod + def disconnect(self): + """ + Disconnects from the database or API. + """ + pass + + @abstractmethod + def check(self): + """ + Checks the connection status. + + Returns: + bool: True if the connection is active, False otherwise. + """ + pass diff --git a/tasks/check_usernames/core/connection/mysql_connection.py b/tasks/check_usernames/core/connection/mysql_connection.py new file mode 100644 index 00000000..bf51a05e --- /dev/null +++ b/tasks/check_usernames/core/connection/mysql_connection.py @@ -0,0 +1,62 @@ +import pymysql +from abc import ABC, abstractmethod + +from tasks.check_usernames.core.connection.base_connection import Connection + + +class MySQLConnection(Connection): + """ + Implementation of the Connection interface for MySQL databases using the pymysql library. + """ + + def __init__(self, host, port, user, password, database): + """ + Initializes a MySQLConnection object with the provided connection parameters. + + Parameters: + host (str): The host name or IP address of the MySQL server. + port (int): The port number of the MySQL server. + user (str): The MySQL user name. + password (str): The password for the MySQL user. + database (str): The name of the MySQL database to connect to. + """ + self.host = host + self.port = port + self.user = user + self.password = password + self.database = database + self.connection = None + + def connect(self): + """ + Connects to the MySQL database using the provided parameters. + + Raises: + pymysql.err.OperationalError: If the connection fails. + """ + self.connection = pymysql.connect( + host=self.host, + port=self.port, + user=self.user, + password=self.password, + database=self.database + ) + + def disconnect(self): + """ + Disconnects from the MySQL database. + """ + if self.connection: + self.connection.close() + self.connection = None + + def check(self): + """ + Checks the status of the MySQL database connection. + + Returns: + bool: True if the connection is open, False otherwise. + """ + if self.connection: + return self.connection.open + return False diff --git a/tasks/check_usernames/core/connection/sqlite_connection.py b/tasks/check_usernames/core/connection/sqlite_connection.py new file mode 100644 index 00000000..02e3763e --- /dev/null +++ b/tasks/check_usernames/core/connection/sqlite_connection.py @@ -0,0 +1,48 @@ +from abc import ABC, abstractmethod + +from tasks.check_usernames.core.connection.base_connection import Connection +import sqlite3 + + +class SQLiteConnection(Connection): + """ + Implementation of the Connection interface for SQLite databases using the sqlite3 library. + """ + + def __init__(self, database_file): + """ + Initializes a SQLiteConnection object with the path to the SQLite database file. + + Parameters: + database_file (str): The path to the SQLite database file. + """ + self.database_file = database_file + self.connection = None + + def connect(self): + """ + Connects to the SQLite database using the provided database file path. + + Raises: + sqlite3.Error: If the connection fails. + """ + self.connection = sqlite3.connect(self.database_file) + + def disconnect(self): + """ + Disconnects from the SQLite database. + """ + if self.connection: + self.connection.close() + self.connection = None + + def check(self): + """ + Checks the status of the SQLite database connection. + + Returns: + bool: True if the connection is open, False otherwise. + """ + if self.connection: + return True + return False diff --git a/tasks/check_usernames/core/factory/__init__.py b/tasks/check_usernames/core/factory/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tasks/check_usernames/core/models/__init__.py b/tasks/check_usernames/core/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tasks/check_usernames/core/models/user.py b/tasks/check_usernames/core/models/user.py new file mode 100644 index 00000000..a168b611 --- /dev/null +++ b/tasks/check_usernames/core/models/user.py @@ -0,0 +1,32 @@ +from abc import ABC, abstractmethod + + +class User(ABC): + def __init__(self, id, user_name, created_at): + self.id = id + self.user_name = user_name + self.created_at = created_at + + @abstractmethod + def get_id(self): + pass + + @abstractmethod + def set_id(self, id): + pass + + @abstractmethod + def get_user_name(self): + pass + + @abstractmethod + def set_user_name(self, user_name): + pass + + @abstractmethod + def get_created_at(self): + pass + + @abstractmethod + def set_created_at(self, created_at): + pass diff --git a/tasks/check_usernames/core/persistence/__init__.py b/tasks/check_usernames/core/persistence/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tasks/check_usernames/core/persistence/base_persistence.py b/tasks/check_usernames/core/persistence/base_persistence.py new file mode 100644 index 00000000..3998f8fe --- /dev/null +++ b/tasks/check_usernames/core/persistence/base_persistence.py @@ -0,0 +1,54 @@ +from abc import ABC, abstractmethod + + +class BasePersistence(ABC): + @abstractmethod + def select(self, query, params=None): + """ + Executes a SELECT query and returns the result as a list of rows. + + Parameters: + query (str): The SQL query string to execute. + params (tuple or dict, optional): Parameters for the query (default: None). + + Returns: + list: A list of rows returned by the query. + """ + pass + + @abstractmethod + def select_one(self, query, params=None): + """ + Executes a SELECT query and returns a single row. + + Parameters: + query (str): The SQL query string to execute. + params (tuple or dict, optional): Parameters for the query (default: None). + + Returns: + tuple or None: A single row returned by the query or None if no rows are found. + """ + pass + + + @abstractmethod + def delete(self, query, params=None): + """ + Executes a DELETE query. + + Parameters: + query (str): The SQL query string to execute. + params (tuple or dict, optional): Parameters for the query (default: None). + """ + pass + + @abstractmethod + def update(self, query, params=None): + """ + Executes an UPDATE query. + + Parameters: + query (str): The SQL query string to execute. + params (tuple or dict, optional): Parameters for the query (default: None). + """ + pass diff --git a/tasks/check_usernames/core/persistence/mysql_persistence.py b/tasks/check_usernames/core/persistence/mysql_persistence.py new file mode 100644 index 00000000..c2ec1cd7 --- /dev/null +++ b/tasks/check_usernames/core/persistence/mysql_persistence.py @@ -0,0 +1,91 @@ +import pymysql +from base_persistence import BasePersistence + + +class MySQLPersistence(BasePersistence): + def __init__(self, connection=None): + """ + Initializes a new instance of the MySQLPersistence class. + + :param connection: The connection object to use for database operations. + :type connection: pymysql.connections.Connection | None + """ + self.connection = connection or self.create_connection() + + def delete(self, query, params=None): + """ + Deletes data from the database based on the given query and parameters. + + :param query: The SQL query used to delete data from the database. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + """ + with self.connection as conn: + cursor = conn.cursor() + cursor.execute(query, params) + conn.commit() + + def execute(self, query, params=None): + """ + Executes a custom SQL query. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + """ + with self.connection as conn: + cursor = conn.cursor() + cursor.execute(query, params) + conn.commit() + + def update(self, query, params=None): + """ + Updates data in the database based on the given query and parameters. + + :param query: The SQL query used to update data in the database. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + """ + with self.connection as conn: + cursor = conn.cursor() + cursor.execute(query, params) + conn.commit() + + def select(self, query, params=None): + """ + Executes a SELECT query and returns the results as a list of rows. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + + :return: A list of rows returned by the query. + :rtype: list + """ + with self.connection as conn: + cursor = conn.cursor() + cursor.execute(query, params) + result = cursor.fetchall() + return result + + def select_one(self, query, params=None): + """ + Executes a SELECT query and returns a single row. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + + :return: A single row returned by the query, or None if no rows are found. + :rtype: Union[tuple, None] + """ + with self.connection as conn: + cursor = conn.cursor() + cursor.execute(query, params) + result = cursor.fetchone() + return result diff --git a/tasks/check_usernames/core/persistence/sqlite_persistence.py b/tasks/check_usernames/core/persistence/sqlite_persistence.py new file mode 100644 index 00000000..e69de29b diff --git a/tasks/check_usernames/core/repositories/__init__.py b/tasks/check_usernames/core/repositories/__init__.py new file mode 100644 index 00000000..e69de29b From 19bc4a4bc55dfec9a1e17308d3c054659aee8566 Mon Sep 17 00:00:00 2001 From: lokas Date: Sun, 10 Sep 2023 19:09:14 +0300 Subject: [PATCH 2/4] some fix on sqlite --- .../core/connection/base_connection.py | 2 +- .../core/connection/mysql_connection.py | 46 ++++++--- .../core/connection/sqlite_connection.py | 13 ++- tasks/check_usernames/core/factory/factory.py | 21 ++++ tasks/check_usernames/core/models/user.py | 29 +----- .../core/persistence/base_persistence.py | 9 +- .../core/persistence/mysql_persistence.py | 32 +++---- .../core/persistence/sqlite_persistence.py | 96 +++++++++++++++++++ .../core/repositories/base_repository.py | 18 ++++ .../core/repositories/mysql_repository.py | 12 +++ tasks/check_usernames/core/test.py | 54 +++++++++++ 11 files changed, 258 insertions(+), 74 deletions(-) create mode 100644 tasks/check_usernames/core/factory/factory.py create mode 100644 tasks/check_usernames/core/repositories/base_repository.py create mode 100644 tasks/check_usernames/core/repositories/mysql_repository.py create mode 100644 tasks/check_usernames/core/test.py diff --git a/tasks/check_usernames/core/connection/base_connection.py b/tasks/check_usernames/core/connection/base_connection.py index a29d58f6..24e8ce5e 100644 --- a/tasks/check_usernames/core/connection/base_connection.py +++ b/tasks/check_usernames/core/connection/base_connection.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod -class Connection(ABC): +class BaseConnection(ABC): """ Abstract base class for connection operations with databases and APIs. This interface provides methods for connecting, disconnecting, and checking the connection status. diff --git a/tasks/check_usernames/core/connection/mysql_connection.py b/tasks/check_usernames/core/connection/mysql_connection.py index bf51a05e..b1f971fc 100644 --- a/tasks/check_usernames/core/connection/mysql_connection.py +++ b/tasks/check_usernames/core/connection/mysql_connection.py @@ -1,31 +1,36 @@ +from __future__ import annotations + import pymysql -from abc import ABC, abstractmethod -from tasks.check_usernames.core.connection.base_connection import Connection +from tasks.check_usernames.core.connection.base_connection import BaseConnection -class MySQLConnection(Connection): +class MySQLBaseConnection(BaseConnection): """ - Implementation of the Connection interface for MySQL databases using the pymysql library. + Implementation of the BaseConnection interface for MySQL databases using the pymysql library. """ - def __init__(self, host, port, user, password, database): + def __init__(self, host: str, port: str | int, database: str, db_connect_file: str = None, user: str = None, + password: str = None): """ - Initializes a MySQLConnection object with the provided connection parameters. + Initializes a MySQLBaseConnection object with the provided connection parameters. Parameters: host (str): The host name or IP address of the MySQL server. port (int): The port number of the MySQL server. - user (str): The MySQL user name. - password (str): The password for the MySQL user. database (str): The name of the MySQL database to connect to. + db_connect_file (str, optional): The path to the MySQL database connection file. Defaults to None. + user (str, optional): The name of the MySQL user. Defaults to None. + password (str, optional): The password of the MySQL user. Defaults to None. """ + self.host = host self.port = port self.user = user self.password = password self.database = database self.connection = None + self.db_connect_file = db_connect_file def connect(self): """ @@ -34,13 +39,24 @@ def connect(self): Raises: pymysql.err.OperationalError: If the connection fails. """ - self.connection = pymysql.connect( - host=self.host, - port=self.port, - user=self.user, - password=self.password, - database=self.database - ) + if self.db_connect_file is not None: + self.connection = pymysql.connect( + host=self.host, + read_default_file=self.db_connect_file, + db=self.database, + charset='utf8mb4', + port=self.port, + cursorclass=pymysql.cursors.DictCursor, + ) + else: + self.connection = pymysql.connect( + host=self.host, + port=self.port, + charset='utf8mb4', + user=self.user, + password=self.password, + db=self.database + ) def disconnect(self): """ diff --git a/tasks/check_usernames/core/connection/sqlite_connection.py b/tasks/check_usernames/core/connection/sqlite_connection.py index 02e3763e..cb2856c3 100644 --- a/tasks/check_usernames/core/connection/sqlite_connection.py +++ b/tasks/check_usernames/core/connection/sqlite_connection.py @@ -1,17 +1,16 @@ -from abc import ABC, abstractmethod - -from tasks.check_usernames.core.connection.base_connection import Connection import sqlite3 +from tasks.check_usernames.core.connection.base_connection import BaseConnection + -class SQLiteConnection(Connection): +class SQLiteBaseConnection(BaseConnection): """ - Implementation of the Connection interface for SQLite databases using the sqlite3 library. + Implementation of the BaseConnection interface for SQLite databases using the sqlite3 library. """ - def __init__(self, database_file): + def __init__(self, database_file: str): """ - Initializes a SQLiteConnection object with the path to the SQLite database file. + Initializes a SQLiteBaseConnection object with the path to the SQLite database file. Parameters: database_file (str): The path to the SQLite database file. diff --git a/tasks/check_usernames/core/factory/factory.py b/tasks/check_usernames/core/factory/factory.py new file mode 100644 index 00000000..690af33b --- /dev/null +++ b/tasks/check_usernames/core/factory/factory.py @@ -0,0 +1,21 @@ +from pywikibot import config as _config + +from tasks.check_usernames.core.connection.mysql_connection import MySQLBaseConnection +from tasks.check_usernames.core.persistence.mysql_persistence import MySQLPersistence +from tasks.check_usernames.core.repositories.mysql_repository import MySQLRepository + + +class DatabaseFactory: + def create_mysql_repository(self) -> MySQLRepository: + connection = MySQLBaseConnection( + host=_config.db_hostname_format.format("arwiki"), + port=_config.db_port, + database=_config.db_name_format.format("arwiki"), + db_connect_file=_config.db_connect_file, + ) + connection.connect() + return MySQLRepository( + persistence=MySQLPersistence( + connection=connection + ) + ) diff --git a/tasks/check_usernames/core/models/user.py b/tasks/check_usernames/core/models/user.py index a168b611..8cb62cdd 100644 --- a/tasks/check_usernames/core/models/user.py +++ b/tasks/check_usernames/core/models/user.py @@ -1,32 +1,5 @@ -from abc import ABC, abstractmethod - - -class User(ABC): +class User: def __init__(self, id, user_name, created_at): self.id = id self.user_name = user_name self.created_at = created_at - - @abstractmethod - def get_id(self): - pass - - @abstractmethod - def set_id(self, id): - pass - - @abstractmethod - def get_user_name(self): - pass - - @abstractmethod - def set_user_name(self, user_name): - pass - - @abstractmethod - def get_created_at(self): - pass - - @abstractmethod - def set_created_at(self, created_at): - pass diff --git a/tasks/check_usernames/core/persistence/base_persistence.py b/tasks/check_usernames/core/persistence/base_persistence.py index 3998f8fe..6059bdd4 100644 --- a/tasks/check_usernames/core/persistence/base_persistence.py +++ b/tasks/check_usernames/core/persistence/base_persistence.py @@ -3,7 +3,7 @@ class BasePersistence(ABC): @abstractmethod - def select(self, query, params=None): + def select(self, query: str, params=None): """ Executes a SELECT query and returns the result as a list of rows. @@ -17,7 +17,7 @@ def select(self, query, params=None): pass @abstractmethod - def select_one(self, query, params=None): + def select_one(self, query: str, params=None): """ Executes a SELECT query and returns a single row. @@ -30,9 +30,8 @@ def select_one(self, query, params=None): """ pass - @abstractmethod - def delete(self, query, params=None): + def delete(self, query: str, params=None): """ Executes a DELETE query. @@ -43,7 +42,7 @@ def delete(self, query, params=None): pass @abstractmethod - def update(self, query, params=None): + def update(self, query: str, params=None): """ Executes an UPDATE query. diff --git a/tasks/check_usernames/core/persistence/mysql_persistence.py b/tasks/check_usernames/core/persistence/mysql_persistence.py index c2ec1cd7..d61ec970 100644 --- a/tasks/check_usernames/core/persistence/mysql_persistence.py +++ b/tasks/check_usernames/core/persistence/mysql_persistence.py @@ -1,16 +1,16 @@ -import pymysql -from base_persistence import BasePersistence +from tasks.check_usernames.core.connection.base_connection import BaseConnection +from tasks.check_usernames.core.persistence.base_persistence import BasePersistence class MySQLPersistence(BasePersistence): - def __init__(self, connection=None): + def __init__(self, connection: BaseConnection): """ Initializes a new instance of the MySQLPersistence class. :param connection: The connection object to use for database operations. - :type connection: pymysql.connections.Connection | None + :type connection: BaseConnection """ - self.connection = connection or self.create_connection() + self.connection = connection def delete(self, query, params=None): """ @@ -21,10 +21,9 @@ def delete(self, query, params=None): :param params: Optional parameters to be substituted in the query. :type params: Union[None, tuple] """ - with self.connection as conn: - cursor = conn.cursor() + with self.connection.connection.cursor() as cursor: cursor.execute(query, params) - conn.commit() + self.connection.connection.commit() def execute(self, query, params=None): """ @@ -35,10 +34,9 @@ def execute(self, query, params=None): :param params: Optional parameters to be substituted in the query. :type params: Union[None, tuple] """ - with self.connection as conn: - cursor = conn.cursor() + with self.connection.connection.cursor() as cursor: cursor.execute(query, params) - conn.commit() + self.connection.connection.commit() def update(self, query, params=None): """ @@ -49,10 +47,9 @@ def update(self, query, params=None): :param params: Optional parameters to be substituted in the query. :type params: Union[None, tuple] """ - with self.connection as conn: - cursor = conn.cursor() + with self.connection.connection.cursor() as cursor: cursor.execute(query, params) - conn.commit() + self.connection.connection.commit() def select(self, query, params=None): """ @@ -66,8 +63,8 @@ def select(self, query, params=None): :return: A list of rows returned by the query. :rtype: list """ - with self.connection as conn: - cursor = conn.cursor() + with self.connection.connection.cursor() as cursor: + print(query, params) cursor.execute(query, params) result = cursor.fetchall() return result @@ -84,8 +81,7 @@ def select_one(self, query, params=None): :return: A single row returned by the query, or None if no rows are found. :rtype: Union[tuple, None] """ - with self.connection as conn: - cursor = conn.cursor() + with self.connection.connection.cursor() as cursor: cursor.execute(query, params) result = cursor.fetchone() return result diff --git a/tasks/check_usernames/core/persistence/sqlite_persistence.py b/tasks/check_usernames/core/persistence/sqlite_persistence.py index e69de29b..55f3a38d 100644 --- a/tasks/check_usernames/core/persistence/sqlite_persistence.py +++ b/tasks/check_usernames/core/persistence/sqlite_persistence.py @@ -0,0 +1,96 @@ +from base_persistence import BasePersistence +from tasks.check_usernames.core.connection.base_connection import BaseConnection + + +class SQLitePersistence(BasePersistence): + def __init__(self, connection: BaseConnection): + """ + Initializes a new instance of the SQLitePersistence class. + + :param connection: The SQLiteConnection object to use for database operations. + :type connection: BaseConnection + """ + self.connection = connection + + def delete(self, query: str, params=None): + """ + Deletes data from the SQLite database based on the given query and parameters. + + :param query: The SQL query used to delete data from the database. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + """ + self.connection.connect() + cursor = self.connection.connection.cursor() + cursor.execute(query, params) + self.connection.connection.commit() + self.connection.disconnect() + + def execute(self, query: str, params=None): + """ + Executes a custom SQL query against the SQLite database. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + """ + self.connection.connect() + cursor = self.connection.connection.cursor() + cursor.execute(query, params) + self.connection.connection.commit() + self.connection.disconnect() + + def update(self, query: str, params=None): + """ + Updates data in the SQLite database based on the given query and parameters. + + :param query: The SQL query used to update data in the database. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + """ + self.connection.connect() + cursor = self.connection.connection.cursor() + cursor.execute(query, params) + self.connection.connection.commit() + self.connection.disconnect() + + def select(self, query: str, params=None): + """ + Executes a SELECT query against the SQLite database and returns the results as a list of rows. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + + :return: A list of rows returned by the query. + :rtype: list + """ + self.connection.connect() + cursor = self.connection.connection.cursor() + cursor.execute(query, params) + result = cursor.fetchall() + self.connection.disconnect() + return result + + def select_one(self, query: str, params=None): + """ + Executes a SELECT query against the SQLite database and returns a single row. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + + :return: A single row returned by the query, or None if no rows are found. + :rtype: Union[tuple, None] + """ + self.connection.connect() + cursor = self.connection.connection.cursor() + cursor.execute(query, params) + result = cursor.fetchone() + self.connection.disconnect() + return result diff --git a/tasks/check_usernames/core/repositories/base_repository.py b/tasks/check_usernames/core/repositories/base_repository.py new file mode 100644 index 00000000..8817e12e --- /dev/null +++ b/tasks/check_usernames/core/repositories/base_repository.py @@ -0,0 +1,18 @@ +from abc import ABC, abstractmethod + + +class BaseRepository(ABC): + @abstractmethod + def selectAllUsers(self, query: str, params=None): + """ + Executes a SELECT query and returns the results. + + :param query: The SQL query to execute. + :type query: str + :param params: Optional parameters to be substituted in the query. + :type params: Union[None, tuple] + + :return: The results of the query. + :rtype: Any + """ + pass diff --git a/tasks/check_usernames/core/repositories/mysql_repository.py b/tasks/check_usernames/core/repositories/mysql_repository.py new file mode 100644 index 00000000..a963332c --- /dev/null +++ b/tasks/check_usernames/core/repositories/mysql_repository.py @@ -0,0 +1,12 @@ +from abc import ABC + +from tasks.check_usernames.core.persistence.base_persistence import BasePersistence +from tasks.check_usernames.core.repositories.base_repository import BaseRepository + + +class MySQLRepository(BaseRepository, ABC): + def __init__(self, persistence: BasePersistence): + self.persistence = persistence + + def selectAllUsers(self, query: str, params=None): + return self.persistence.select(query, params) diff --git a/tasks/check_usernames/core/test.py b/tasks/check_usernames/core/test.py new file mode 100644 index 00000000..6229f359 --- /dev/null +++ b/tasks/check_usernames/core/test.py @@ -0,0 +1,54 @@ +import datetime + +from tasks.check_usernames.core.factory.factory import DatabaseFactory +from tasks.check_usernames.core.models.user import User + +# Get yesterday's date +yesterday = datetime.date.today() - datetime.timedelta(days=1) + +# Get start time for yesterday +start_time = datetime.datetime.combine(yesterday, datetime.time.min) + +# Get last time for yesterday +last_time = datetime.datetime.combine(yesterday, datetime.time.max) + +# Format dates for SQL query +start_time_sql = start_time.strftime("%Y%m%d%H%M%S") +# start_time_sql = 20221207000000 +last_time_sql = last_time.strftime("%Y%m%d%H%M%S") +# last_time_sql = 20230322235959 + + +factory = DatabaseFactory() + +mysql_repository = factory.create_mysql_repository() + +query = """ select log_title as "q_log_title",log_id as "q_log_id" + from logging + where log_type in ("newusers") + and log_timestamp BETWEEN {} AND {} + and log_title not in ( + select page.page_title from categorylinks + inner join page on page.page_id = categorylinks.cl_from + where cl_to like "أسماء_مستخدمين_مخالفة_مرشحة_للمنع" + and cl_type in ("page") + ) + and log_title not in ( + select replace(user.user_name," ","_") as "user_name_temp" from ipblocks + inner join user on ipblocks.ipb_user = user.user_id + ) + """.format(start_time_sql, last_time_sql) + +users_models = [] +raw_users = mysql_repository.selectAllUsers(query=query) + +for row in raw_users: + users_models.append( + User( + id=row["q_log_id"], + user_name=str(row["q_log_title"], 'utf-8'), + created_at=datetime.datetime.now() + ) + ) + +print(users_models) From a6590b6ac04dbfe410de5db4599bb1715d47980f Mon Sep 17 00:00:00 2001 From: lokas Date: Sun, 10 Sep 2023 19:38:01 +0300 Subject: [PATCH 3/4] add sqlite_repository.py --- tasks/check_usernames/core/factory/factory.py | 15 ++++++ .../core/persistence/base_persistence.py | 17 +++++++ .../core/persistence/sqlite_persistence.py | 47 ++++++++++++++++--- .../core/repositories/base_repository.py | 12 +++++ .../core/repositories/mysql_repository.py | 6 +++ .../core/repositories/sqlite_repository.py | 27 +++++++++++ tasks/check_usernames/core/test.py | 5 +- 7 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 tasks/check_usernames/core/repositories/sqlite_repository.py diff --git a/tasks/check_usernames/core/factory/factory.py b/tasks/check_usernames/core/factory/factory.py index 690af33b..018be420 100644 --- a/tasks/check_usernames/core/factory/factory.py +++ b/tasks/check_usernames/core/factory/factory.py @@ -1,8 +1,11 @@ from pywikibot import config as _config from tasks.check_usernames.core.connection.mysql_connection import MySQLBaseConnection +from tasks.check_usernames.core.connection.sqlite_connection import SQLiteBaseConnection from tasks.check_usernames.core.persistence.mysql_persistence import MySQLPersistence +from tasks.check_usernames.core.persistence.sqlite_persistence import SQLitePersistence from tasks.check_usernames.core.repositories.mysql_repository import MySQLRepository +from tasks.check_usernames.core.repositories.sqlite_repository import SQLiteRepository class DatabaseFactory: @@ -19,3 +22,15 @@ def create_mysql_repository(self) -> MySQLRepository: connection=connection ) ) + + def create_sqlite_memory_repository(self) -> SQLiteRepository: + connection = SQLiteBaseConnection( + # database_file="file::memory:?cache=shared", + database_file="/home/lokas/PycharmProjects/pythonProject3/code/tasks/check_usernames/core/demo.db", + ) + connection.connect() + return SQLiteRepository( + persistence=SQLitePersistence( + connection=connection + ) + ) diff --git a/tasks/check_usernames/core/persistence/base_persistence.py b/tasks/check_usernames/core/persistence/base_persistence.py index 6059bdd4..7449f689 100644 --- a/tasks/check_usernames/core/persistence/base_persistence.py +++ b/tasks/check_usernames/core/persistence/base_persistence.py @@ -2,6 +2,23 @@ class BasePersistence(ABC): + + @abstractmethod + def execute(self, query: str, params=None): + """ + Executes a custom SQL query. + + :param query: The SQL query to execute. + + :type query: str + + :param params: Optional parameters to be substituted in the query. + + :type params: Union[None, tuple] + + """ + pass + @abstractmethod def select(self, query: str, params=None): """ diff --git a/tasks/check_usernames/core/persistence/sqlite_persistence.py b/tasks/check_usernames/core/persistence/sqlite_persistence.py index 55f3a38d..70eab18a 100644 --- a/tasks/check_usernames/core/persistence/sqlite_persistence.py +++ b/tasks/check_usernames/core/persistence/sqlite_persistence.py @@ -1,5 +1,5 @@ -from base_persistence import BasePersistence from tasks.check_usernames.core.connection.base_connection import BaseConnection +from tasks.check_usernames.core.persistence.base_persistence import BasePersistence class SQLitePersistence(BasePersistence): @@ -23,7 +23,14 @@ def delete(self, query: str, params=None): """ self.connection.connect() cursor = self.connection.connection.cursor() - cursor.execute(query, params) + if params is None: + cursor.execute(query) + elif isinstance(params, tuple): + cursor.execute(query, params) + elif isinstance(params, dict): + cursor.execute(query, params) + else: + raise ValueError("Unsupported type for 'params' argument") self.connection.connection.commit() self.connection.disconnect() @@ -38,7 +45,14 @@ def execute(self, query: str, params=None): """ self.connection.connect() cursor = self.connection.connection.cursor() - cursor.execute(query, params) + if params is None: + cursor.execute(query) + elif isinstance(params, tuple): + cursor.execute(query, params) + elif isinstance(params, dict): + cursor.execute(query, params) + else: + raise ValueError("Unsupported type for 'params' argument") self.connection.connection.commit() self.connection.disconnect() @@ -53,7 +67,14 @@ def update(self, query: str, params=None): """ self.connection.connect() cursor = self.connection.connection.cursor() - cursor.execute(query, params) + if params is None: + cursor.execute(query) + elif isinstance(params, tuple): + cursor.execute(query, params) + elif isinstance(params, dict): + cursor.execute(query, params) + else: + raise ValueError("Unsupported type for 'params' argument") self.connection.connection.commit() self.connection.disconnect() @@ -71,7 +92,14 @@ def select(self, query: str, params=None): """ self.connection.connect() cursor = self.connection.connection.cursor() - cursor.execute(query, params) + if params is None: + cursor.execute(query) + elif isinstance(params, tuple): + cursor.execute(query, params) + elif isinstance(params, dict): + cursor.execute(query, params) + else: + raise ValueError("Unsupported type for 'params' argument") result = cursor.fetchall() self.connection.disconnect() return result @@ -90,7 +118,14 @@ def select_one(self, query: str, params=None): """ self.connection.connect() cursor = self.connection.connection.cursor() - cursor.execute(query, params) + if params is None: + cursor.execute(query) + elif isinstance(params, tuple): + cursor.execute(query, params) + elif isinstance(params, dict): + cursor.execute(query, params) + else: + raise ValueError("Unsupported type for 'params' argument") result = cursor.fetchone() self.connection.disconnect() return result diff --git a/tasks/check_usernames/core/repositories/base_repository.py b/tasks/check_usernames/core/repositories/base_repository.py index 8817e12e..a87feb73 100644 --- a/tasks/check_usernames/core/repositories/base_repository.py +++ b/tasks/check_usernames/core/repositories/base_repository.py @@ -2,6 +2,11 @@ class BaseRepository(ABC): + + @abstractmethod + def createUserTable(self): + pass + @abstractmethod def selectAllUsers(self, query: str, params=None): """ @@ -16,3 +21,10 @@ def selectAllUsers(self, query: str, params=None): :rtype: Any """ pass + + @abstractmethod + def deleteAllUsers(self): + """ + Deletes all users from the database. + """ + pass diff --git a/tasks/check_usernames/core/repositories/mysql_repository.py b/tasks/check_usernames/core/repositories/mysql_repository.py index a963332c..d9309a40 100644 --- a/tasks/check_usernames/core/repositories/mysql_repository.py +++ b/tasks/check_usernames/core/repositories/mysql_repository.py @@ -10,3 +10,9 @@ def __init__(self, persistence: BasePersistence): def selectAllUsers(self, query: str, params=None): return self.persistence.select(query, params) + + def createUserTable(self): + pass + + def deleteAllUsers(self): + pass diff --git a/tasks/check_usernames/core/repositories/sqlite_repository.py b/tasks/check_usernames/core/repositories/sqlite_repository.py new file mode 100644 index 00000000..6ae1ae47 --- /dev/null +++ b/tasks/check_usernames/core/repositories/sqlite_repository.py @@ -0,0 +1,27 @@ +from abc import ABC +from typing import List + +from tasks.check_usernames.core.models.user import User +from tasks.check_usernames.core.persistence.base_persistence import BasePersistence +from tasks.check_usernames.core.repositories.base_repository import BaseRepository + + +class SQLiteRepository(BaseRepository, ABC): + def __init__(self, persistence: BasePersistence): + self.persistence = persistence + + def selectAllUsers(self, query: str, params=None): + pass + + def createUserTable(self): + query = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, user_name TEXT NOT NULL UNIQUE)" + self.persistence.execute(query=query) + + def deleteAllUsers(self): + query = "DELETE FROM users" + self.persistence.delete(query=query) + + def saveUsers(self, users: List[User]): + for user in users: + query = "INSERT INTO users (user_name) VALUES ('{}')".format(user.user_name) + self.persistence.execute(query=query) diff --git a/tasks/check_usernames/core/test.py b/tasks/check_usernames/core/test.py index 6229f359..ca87ab5e 100644 --- a/tasks/check_usernames/core/test.py +++ b/tasks/check_usernames/core/test.py @@ -51,4 +51,7 @@ ) ) -print(users_models) +sqlite_repository = factory.create_sqlite_memory_repository() +sqlite_repository.createUserTable() +sqlite_repository.deleteAllUsers() +sqlite_repository.saveUsers(users_models) From 27d6d07961824d22f2b4b638477018247fd94f1a Mon Sep 17 00:00:00 2001 From: lokas Date: Sun, 10 Sep 2023 20:33:45 +0300 Subject: [PATCH 4/4] save init list --- tasks/check_usernames/core/models/user.py | 11 ++++++++++- .../core/repositories/sqlite_repository.py | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/tasks/check_usernames/core/models/user.py b/tasks/check_usernames/core/models/user.py index 8cb62cdd..20c4e42d 100644 --- a/tasks/check_usernames/core/models/user.py +++ b/tasks/check_usernames/core/models/user.py @@ -1,5 +1,14 @@ +class UserStatus: + STATE_GET_FROM_DB = 0 + STATE_SEND_TO_API = 1 + STATE_ACCEPT_ON_API = 2 + STATE_REJECT_ON_API = 3 + + class User: - def __init__(self, id, user_name, created_at): + + def __init__(self, id, user_name, created_at, status=UserStatus.STATE_GET_FROM_DB): self.id = id self.user_name = user_name self.created_at = created_at + self.status = status diff --git a/tasks/check_usernames/core/repositories/sqlite_repository.py b/tasks/check_usernames/core/repositories/sqlite_repository.py index 6ae1ae47..8274f704 100644 --- a/tasks/check_usernames/core/repositories/sqlite_repository.py +++ b/tasks/check_usernames/core/repositories/sqlite_repository.py @@ -14,7 +14,14 @@ def selectAllUsers(self, query: str, params=None): pass def createUserTable(self): - query = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, user_name TEXT NOT NULL UNIQUE)" + query = """ + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_name TEXT NOT NULL UNIQUE, + created_at DATETIME NOT NULL, + status INTEGER + ) + """ self.persistence.execute(query=query) def deleteAllUsers(self): @@ -23,5 +30,9 @@ def deleteAllUsers(self): def saveUsers(self, users: List[User]): for user in users: - query = "INSERT INTO users (user_name) VALUES ('{}')".format(user.user_name) + query = """ INSERT INTO users + (user_name, created_at, status) + VALUES + ('{}', '{}', {}) + """.format(user.user_name, user.created_at, user.status) self.persistence.execute(query=query)