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"], |     install_requires=["python-frontmatter", "termcolor"], | ||||||
|     entry_points={ |     entry_points={ | ||||||
|         "console_scripts": [ |         "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 controllers.controller import Controller | ||||||
| from services.database_service import DatabaseService | from services.database_service import DatabaseService | ||||||
| from services.parse_file_service import ParseFileService | from services.parse_file_service import ParseFileService | ||||||
| from services.sqlite_service import SqliteService | from services.table_service import TableService | ||||||
| 
 | 
 | ||||||
| database_service = DatabaseService("eolas") | database_service = DatabaseService("eolas") | ||||||
| database_connection = database_service.connect() | database_connection = database_service.connect() | ||||||
| sqlite_service = SqliteService(database_connection) | table_service = TableService(database_connection) | ||||||
| parse_file_service = ParseFileService(EOLAS_DIRECTORY) | 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(): | def main(): | ||||||
|  | @ -18,11 +18,11 @@ def main(): | ||||||
|         prog="eolas-db", description="Eolas database manager." |         prog="eolas-db", description="Eolas database manager." | ||||||
|     ) |     ) | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         "command", choices=["parse", "populate"], help="Command to execute" |         "command", choices=["populate-database"], help="Command to execute" | ||||||
|     ) |     ) | ||||||
|     args = parser.parse_args() |     args = parser.parse_args() | ||||||
| 
 | 
 | ||||||
|     if args.command == "populate": |     if args.command == "populate-database": | ||||||
|         controller.populate_database() |         controller.populate_database() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| class Controller: | 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.database_service = database_service | ||||||
|         self.sqlite_service = sqlite_service |         self.table_service = table_service | ||||||
|         self.parse_file_service = parse_file_service |         self.parse_file_service = parse_file_service | ||||||
| 
 | 
 | ||||||
|     def populate_database(self): |     def populate_database(self): | ||||||
|         try: |         try: | ||||||
|             entries = self.parse_file_service.parse_source_directory() |             entries = self.parse_file_service.parse_source_directory() | ||||||
|             self.sqlite_service.populate_tables(entries) |             self.table_service.populate_tables(entries) | ||||||
|         finally: |         finally: | ||||||
|             self.database_service.disconnect() |             self.database_service.disconnect() | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | import os | ||||||
| import sqlite3 | import sqlite3 | ||||||
| from typing import Optional | from typing import Optional | ||||||
| 
 | 
 | ||||||
|  | @ -15,6 +16,9 @@ class DatabaseService: | ||||||
|             return self.connection |             return self.connection | ||||||
| 
 | 
 | ||||||
|         try: |         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 = sqlite3.connect(f"{self.db_path}/{self.db_name}.db") | ||||||
|             self.connection.execute("PRAGMA foreign_keys = ON") |             self.connection.execute("PRAGMA foreign_keys = ON") | ||||||
|             print(colored("INFO Database connection established", "light_blue")) |             print(colored("INFO Database connection established", "light_blue")) | ||||||
|  |  | ||||||
|  | @ -8,11 +8,11 @@ from sql.create_tables import tables | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SqliteService: | class SqliteService: | ||||||
|     def __init__(self, connection): |     def __init__(self, db_connection): | ||||||
|         self.connection = connection |         self.connection = db_connection | ||||||
|         self.cursor = connection.cursor() |         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: |         try: | ||||||
|             if params: |             if params: | ||||||
|                 self.cursor.execute(sql, params) |                 self.cursor.execute(sql, params) | ||||||
|  | @ -24,72 +24,3 @@ class SqliteService: | ||||||
|             if errorMessage: |             if errorMessage: | ||||||
|                 raise Exception(f"ERROR {errorMessage}: {e}") |                 raise Exception(f"ERROR {errorMessage}: {e}") | ||||||
|             raise |             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