refactor: create TableService child class from SqliteService
This commit is contained in:
		
							parent
							
								
									808fb54db5
								
							
						
					
					
						commit
						428f1c435f
					
				
					 7 changed files with 96 additions and 82 deletions
				
			
		
							
								
								
									
										
											BIN
										
									
								
								db/eolas.db
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/eolas.db
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								setup.py
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
										
									
									
									
								
							|  | @ -8,7 +8,7 @@ setup( | |||
|     install_requires=["python-frontmatter", "termcolor"], | ||||
|     entry_points={ | ||||
|         "console_scripts": [ | ||||
|             "run=app:main", | ||||
|             "eolas-db=cli:main", | ||||
|         ], | ||||
|     }, | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/cli.py
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								src/cli.py
									
										
									
									
									
								
							|  | @ -4,13 +4,13 @@ from constants import EOLAS_DIRECTORY | |||
| from controllers.controller import Controller | ||||
| from services.database_service import DatabaseService | ||||
| from services.parse_file_service import ParseFileService | ||||
| from services.sqlite_service import SqliteService | ||||
| from services.table_service import TableService | ||||
| 
 | ||||
| database_service = DatabaseService("eolas") | ||||
| database_connection = database_service.connect() | ||||
| sqlite_service = SqliteService(database_connection) | ||||
| table_service = TableService(database_connection) | ||||
| parse_file_service = ParseFileService(EOLAS_DIRECTORY) | ||||
| controller = Controller(database_service, sqlite_service, parse_file_service) | ||||
| controller = Controller(database_service, table_service, parse_file_service) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|  | @ -18,11 +18,11 @@ def main(): | |||
|         prog="eolas-db", description="Eolas database manager." | ||||
|     ) | ||||
|     parser.add_argument( | ||||
|         "command", choices=["parse", "populate"], help="Command to execute" | ||||
|         "command", choices=["populate-database"], help="Command to execute" | ||||
|     ) | ||||
|     args = parser.parse_args() | ||||
| 
 | ||||
|     if args.command == "populate": | ||||
|     if args.command == "populate-database": | ||||
|         controller.populate_database() | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,12 +1,12 @@ | |||
| class Controller: | ||||
|     def __init__(self, database_service, sqlite_service, parse_file_service): | ||||
|     def __init__(self, database_service, table_service, parse_file_service): | ||||
|         self.database_service = database_service | ||||
|         self.sqlite_service = sqlite_service | ||||
|         self.table_service = table_service | ||||
|         self.parse_file_service = parse_file_service | ||||
| 
 | ||||
|     def populate_database(self): | ||||
|         try: | ||||
|             entries = self.parse_file_service.parse_source_directory() | ||||
|             self.sqlite_service.populate_tables(entries) | ||||
|             self.table_service.populate_tables(entries) | ||||
|         finally: | ||||
|             self.database_service.disconnect() | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| import os | ||||
| import sqlite3 | ||||
| from typing import Optional | ||||
| 
 | ||||
|  | @ -15,6 +16,9 @@ class DatabaseService: | |||
|             return self.connection | ||||
| 
 | ||||
|         try: | ||||
|             if not os.path.exists(self.db_path): | ||||
|                 os.makedirs(self.db_path) | ||||
|                 print(colored("INFO Created database directory", "light_blue")) | ||||
|             self.connection = sqlite3.connect(f"{self.db_path}/{self.db_name}.db") | ||||
|             self.connection.execute("PRAGMA foreign_keys = ON") | ||||
|             print(colored("INFO Database connection established", "light_blue")) | ||||
|  |  | |||
|  | @ -8,11 +8,11 @@ from sql.create_tables import tables | |||
| 
 | ||||
| 
 | ||||
| class SqliteService: | ||||
|     def __init__(self, connection): | ||||
|         self.connection = connection | ||||
|         self.cursor = connection.cursor() | ||||
|     def __init__(self, db_connection): | ||||
|         self.connection = db_connection | ||||
|         self.cursor = db_connection.cursor() | ||||
| 
 | ||||
|     def __query(self, sql, params=None, errorMessage: Optional[str] = None): | ||||
|     def _query(self, sql, params=None, errorMessage: Optional[str] = None): | ||||
|         try: | ||||
|             if params: | ||||
|                 self.cursor.execute(sql, params) | ||||
|  | @ -24,72 +24,3 @@ class SqliteService: | |||
|             if errorMessage: | ||||
|                 raise Exception(f"ERROR {errorMessage}: {e}") | ||||
|             raise | ||||
| 
 | ||||
|     def __create_tables(self): | ||||
|         for table in tables: | ||||
|             self.__query( | ||||
|                 table["create_statement"], | ||||
|                 errorMessage=f"Problem creating table {table['name']}", | ||||
|             ) | ||||
|         print(colored("INFO Created tables", "light_blue")) | ||||
| 
 | ||||
|     def __drop_tables(self): | ||||
|         # Reverse the order of `tables` list to avoid foreign key violation when | ||||
|         # deleting | ||||
|         for table in reversed(tables): | ||||
|             self.__query( | ||||
|                 f"DROP TABLE IF EXISTS {table['name']}", | ||||
|                 errorMessage=f"Problem truncating table {table['name']}", | ||||
|             ) | ||||
|         print(colored("INFO Cleared tables", "light_blue")) | ||||
| 
 | ||||
|     def __entry_exists(self, title) -> bool: | ||||
|         self.__query("SELECT 1 FROM entries WHERE title = :title", {"title": title}) | ||||
|         result = self.cursor.fetchone() | ||||
|         return result is not None | ||||
| 
 | ||||
|     def __populate_base_tables(self, entries: list[Entry]): | ||||
|         for entry in entries: | ||||
|             self.__query( | ||||
|                 "INSERT INTO entries (title, last_modified, size, body) VALUES (:title, :last_modified, :size, :body)", | ||||
|                 entry, | ||||
|                 errorMessage=f"The following entry could not be added to `entries` table: {entry}", | ||||
|             ) | ||||
|             tags = entry.get("tags") | ||||
|             if tags: | ||||
|                 for tag in tags: | ||||
|                     self.__query( | ||||
|                         "INSERT OR IGNORE INTO tags (name) VALUES (:tag_name)", | ||||
|                         {"tag_name": tag}, | ||||
|                     ) | ||||
| 
 | ||||
|         print(colored("INFO Base tables populated", "light_blue")) | ||||
| 
 | ||||
|     def __populate_junction_tables(self, entries: list[Entry]): | ||||
|         for entry in entries: | ||||
|             tags = entry.get("tags") | ||||
|             links = entry.get("links") | ||||
|             if tags: | ||||
|                 for tag in tags: | ||||
|                     self.__query( | ||||
|                         "INSERT INTO entries_tags (entry_title, tag_name) VALUES (:entry_title, :tag_name)", | ||||
|                         {"entry_title": entry.get("title"), "tag_name": tag}, | ||||
|                     ) | ||||
|             if links: | ||||
|                 for link in links: | ||||
|                     if self.__entry_exists(link): | ||||
|                         self.__query( | ||||
|                             "INSERT OR IGNORE INTO backlinks (source_entry_title, target_entry_title) VALUES (:source_entry_title, :target_entry_title)", | ||||
|                             { | ||||
|                                 "source_entry_title": entry.get("title"), | ||||
|                                 "target_entry_title": link, | ||||
|                             }, | ||||
|                         ) | ||||
| 
 | ||||
|         print(colored("INFO Junction tables populated", "light_blue")) | ||||
| 
 | ||||
|     def populate_tables(self, entries: list[Entry]): | ||||
|         self.__drop_tables() | ||||
|         self.__create_tables() | ||||
|         self.__populate_base_tables(entries) | ||||
|         self.__populate_junction_tables(entries) | ||||
|  |  | |||
							
								
								
									
										79
									
								
								src/services/table_service.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/services/table_service.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| from termcolor import colored | ||||
| 
 | ||||
| from models.entry import Entry | ||||
| from services.sqlite_service import SqliteService | ||||
| from sql.create_tables import tables | ||||
| 
 | ||||
| 
 | ||||
| class TableService(SqliteService): | ||||
|     def __init__(self, db_connection): | ||||
|         super().__init__(db_connection) | ||||
| 
 | ||||
|     def __create_tables(self): | ||||
|         for table in tables: | ||||
|             self._query( | ||||
|                 table["create_statement"], | ||||
|                 errorMessage=f"Problem creating table {table['name']}", | ||||
|             ) | ||||
|         print(colored("INFO Created tables", "light_blue")) | ||||
| 
 | ||||
|     def __drop_tables(self): | ||||
|         # Reverse the order of `tables` list to avoid foreign key violation when | ||||
|         # deleting | ||||
|         for table in reversed(tables): | ||||
|             self._query( | ||||
|                 f"DROP TABLE IF EXISTS {table['name']}", | ||||
|                 errorMessage=f"Problem truncating table {table['name']}", | ||||
|             ) | ||||
|         print(colored("INFO Cleared tables", "light_blue")) | ||||
| 
 | ||||
|     def __entry_exists(self, title) -> bool: | ||||
|         self._query("SELECT 1 FROM entries WHERE title = :title", {"title": title}) | ||||
|         result = self.cursor.fetchone() | ||||
|         return result is not None | ||||
| 
 | ||||
|     def __populate_base_tables(self, entries: list[Entry]): | ||||
|         for entry in entries: | ||||
|             self._query( | ||||
|                 "INSERT INTO entries (title, last_modified, size, body) VALUES (:title, :last_modified, :size, :body)", | ||||
|                 entry, | ||||
|                 errorMessage=f"The following entry could not be added to `entries` table: {entry}", | ||||
|             ) | ||||
|             tags = entry.get("tags") | ||||
|             if tags: | ||||
|                 for tag in tags: | ||||
|                     self._query( | ||||
|                         "INSERT OR IGNORE INTO tags (name) VALUES (:tag_name)", | ||||
|                         {"tag_name": tag}, | ||||
|                     ) | ||||
| 
 | ||||
|         print(colored("INFO Base tables populated", "light_blue")) | ||||
| 
 | ||||
|     def __populate_junction_tables(self, entries: list[Entry]): | ||||
|         for entry in entries: | ||||
|             tags = entry.get("tags") | ||||
|             links = entry.get("links") | ||||
|             if tags: | ||||
|                 for tag in tags: | ||||
|                     self._query( | ||||
|                         "INSERT INTO entries_tags (entry_title, tag_name) VALUES (:entry_title, :tag_name)", | ||||
|                         {"entry_title": entry.get("title"), "tag_name": tag}, | ||||
|                     ) | ||||
|             if links: | ||||
|                 for link in links: | ||||
|                     if self.__entry_exists(link): | ||||
|                         self._query( | ||||
|                             "INSERT OR IGNORE INTO backlinks (source_entry_title, target_entry_title) VALUES (:source_entry_title, :target_entry_title)", | ||||
|                             { | ||||
|                                 "source_entry_title": entry.get("title"), | ||||
|                                 "target_entry_title": link, | ||||
|                             }, | ||||
|                         ) | ||||
| 
 | ||||
|         print(colored("INFO Junction tables populated", "light_blue")) | ||||
| 
 | ||||
|     def populate_tables(self, entries: list[Entry]): | ||||
|         self.__drop_tables() | ||||
|         self.__create_tables() | ||||
|         self.__populate_base_tables(entries) | ||||
|         self.__populate_junction_tables(entries) | ||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 thomasabishop
						thomasabishop