pleasant-database 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pleasant_database-1.3.0/LICENSE +21 -0
- pleasant_database-1.3.0/PKG-INFO +13 -0
- pleasant_database-1.3.0/README.md +115 -0
- pleasant_database-1.3.0/pleasant_database/__init__.py +43 -0
- pleasant_database-1.3.0/pleasant_database/base_table.py +127 -0
- pleasant_database-1.3.0/pleasant_database/database_connections.py +57 -0
- pleasant_database-1.3.0/pleasant_database/database_file.py +129 -0
- pleasant_database-1.3.0/pleasant_database/database_manager.py +749 -0
- pleasant_database-1.3.0/pleasant_database/utils.py +174 -0
- pleasant_database-1.3.0/pleasant_database.egg-info/PKG-INFO +13 -0
- pleasant_database-1.3.0/pleasant_database.egg-info/SOURCES.txt +21 -0
- pleasant_database-1.3.0/pleasant_database.egg-info/dependency_links.txt +1 -0
- pleasant_database-1.3.0/pleasant_database.egg-info/requires.txt +5 -0
- pleasant_database-1.3.0/pleasant_database.egg-info/top_level.txt +2 -0
- pleasant_database-1.3.0/pyproject.toml +20 -0
- pleasant_database-1.3.0/setup.cfg +4 -0
- pleasant_database-1.3.0/tests/__init__.py +9 -0
- pleasant_database-1.3.0/tests/mock_table_object.py +90 -0
- pleasant_database-1.3.0/tests/test_base_table.py +60 -0
- pleasant_database-1.3.0/tests/test_database_file.py +148 -0
- pleasant_database-1.3.0/tests/test_database_manager.py +633 -0
- pleasant_database-1.3.0/tests/test_db_connections.py +41 -0
- pleasant_database-1.3.0/tests/test_utils.py +134 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 EliasRodkey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pleasant-database
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: package for handling local database files and data
|
|
5
|
+
Author-email: Elias Rodkey <elias.rodkey@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: requests
|
|
9
|
+
Requires-Dist: numpy
|
|
10
|
+
Requires-Dist: sqlalchemy
|
|
11
|
+
Requires-Dist: loggers
|
|
12
|
+
Requires-Dist: pandas
|
|
13
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# local_db_handler
|
|
2
|
+
|
|
3
|
+
this package is a modular python package that allows for easy
|
|
4
|
+
creation, reading, editing, and deleting local database files
|
|
5
|
+
|
|
6
|
+
## License
|
|
7
|
+
|
|
8
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
|
9
|
+
|
|
10
|
+
## Usage/Examples
|
|
11
|
+
|
|
12
|
+
**Install Package**
|
|
13
|
+
Ensure you have access to the github repository
|
|
14
|
+
Run the command:
|
|
15
|
+
pip install git+<https://github.com/EliasRodkey/local_db.git>
|
|
16
|
+
|
|
17
|
+
### Import Package
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
From local_db_handler import DatabaseFile, DatabaseManager, BaseTable
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Set Up Table Object
|
|
24
|
+
|
|
25
|
+
Use the BaseTable class to create a table object that will be connected to using the DatabaseManager
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
class TableName(BaseTable):
|
|
29
|
+
"""A table object"""
|
|
30
|
+
__tablename__ = "table_name"
|
|
31
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
32
|
+
name = Column(String(50), nullable=False)
|
|
33
|
+
age = Column(Integer)
|
|
34
|
+
email = Column(String(100), unique=True)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The attributes here represent column names in the table, additional funcionality can be added to avoid duplicate entries, for example:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
email = Column(String(100), unique=True)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This forces all emails to be unique in the table.
|
|
44
|
+
Another option is to add a special hash row that would be unique to each entry:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import hashlib
|
|
48
|
+
|
|
49
|
+
def generate_hash(row):
|
|
50
|
+
# Concatenate values of all relevant columns
|
|
51
|
+
data = f"{row["col1"]}_{row["col2"]}_{row["col3"]}"
|
|
52
|
+
return hashlib.sha256(data.encode()).hexdigest()
|
|
53
|
+
|
|
54
|
+
df["unique_id"] = df.apply(generate_hash, axis=1)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Additionally the \_\_tableargs\_\_ can be updated to make a custom unique filter based on multiple columns to the class attributes:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
__table_args__ = (
|
|
61
|
+
UniqueConstraint("column1", "column2", name="unique_combo"), # Combination must be unique
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Creating the DataFile
|
|
66
|
+
|
|
67
|
+
The DatabaseFile object represents the actual file of the database and is required to initialize a DatabaseManager object.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
file = DatabaseFile(db_name, directory="data/dbs")
|
|
71
|
+
|
|
72
|
+
db_name: valid .db filename
|
|
73
|
+
directory: relative path to database directory (default data//dbs)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
DatabaseFile funcitons:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
file.create() # Creates a db file in the directory location
|
|
80
|
+
file.move(target_directory) # Moves the db file to a new directory
|
|
81
|
+
file.exists() # Returns boolean, if the file exists
|
|
82
|
+
file.delete() # Deletes the actual db file
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Create DatabaseManager
|
|
86
|
+
|
|
87
|
+
The DatabaseManager takes a file and table as arguments and allows for common database operations on that table
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
manager = DatabaseManager(table_class: BaseTable, databasefile: DatabaseFile)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The DatabaseFile functions can be accessed through the DatabaseManager.file attribute
|
|
94
|
+
Basic database funcitons:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
manager.add_item(entry: dictionary with columns matching the db table class)
|
|
98
|
+
manager.add_multiple_items(entries: list of entries)
|
|
99
|
+
manager.append_dataframe(df) # pandas DataFrame with columns that match the db table class
|
|
100
|
+
|
|
101
|
+
manager.fetch_all(as_dataframe=True) # returns all table class instances in the table
|
|
102
|
+
manager.fetch_item_by_id(id: int, as_dataframe=True) # returns an individual table class instance with data
|
|
103
|
+
manager.fetch_items_by_attribute(**kwargs, as_dataframe=True) # allows filtering of table by kwargs
|
|
104
|
+
manager.filter_items(filters: dict, use_or=False, as_dataframe=True) # allows filtering of database table and reading of filtered items
|
|
105
|
+
manager.to_dataframe() -> Returns the entire database as a pandas DataFrame
|
|
106
|
+
|
|
107
|
+
manager.update_item(item_id: int, **kwargs) # updates values kwargs of an item with a given ID
|
|
108
|
+
manager.delete_item(item_id: int) # Deletes an item with the given item id
|
|
109
|
+
manager.delete_items_by_attribute(**kwargs) # Deletes items in a database where column values match kwargs.
|
|
110
|
+
manager.delete_items_by_filter(filters: dict, use_or=False) # Deletes items in a database where filters apply.
|
|
111
|
+
manager.clear_table() # Deletes all items in the database table.
|
|
112
|
+
|
|
113
|
+
manager.start_session() # initiates when instance initialized
|
|
114
|
+
manager.end_session() # should be called before exiting program
|
|
115
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!python3
|
|
2
|
+
"""
|
|
3
|
+
local_db_handler
|
|
4
|
+
|
|
5
|
+
This package contains modules related to local database connection / creation / deletion / editing / etc.
|
|
6
|
+
|
|
7
|
+
Modules:
|
|
8
|
+
- database_connections.py
|
|
9
|
+
- database_file.py:
|
|
10
|
+
- database_manager.py:
|
|
11
|
+
- utils.py:
|
|
12
|
+
"""
|
|
13
|
+
# Metadata
|
|
14
|
+
__version__ = "1.3.0"
|
|
15
|
+
__author__ = "Elias Rodkey"
|
|
16
|
+
|
|
17
|
+
# Package Level Constants
|
|
18
|
+
import os
|
|
19
|
+
DEFAULT_DB_DIRECTORY = os.path.join(os.getcwd(), "data", "dbs")
|
|
20
|
+
LOG_DIRECTORY = os.path.join(os.getcwd(), "data", "logs")
|
|
21
|
+
|
|
22
|
+
# Configure root logger
|
|
23
|
+
from loggers import configure_logging
|
|
24
|
+
handler_controller = configure_logging(log_directory=LOG_DIRECTORY)
|
|
25
|
+
|
|
26
|
+
# Imports modules, functions, and classes for clean package interface
|
|
27
|
+
from .base_table import BaseTable, DatabaseIntegrityError
|
|
28
|
+
from .database_connections import create_engine_conn, create_session
|
|
29
|
+
from .database_file import DatabaseFile
|
|
30
|
+
from .database_manager import DatabaseManager
|
|
31
|
+
from .utils import check_db_exists, is_db_file
|
|
32
|
+
|
|
33
|
+
# Defines importable functions for package
|
|
34
|
+
__all__ = [
|
|
35
|
+
"check_db_exists",
|
|
36
|
+
"is_db_file",
|
|
37
|
+
"create_engine_conn",
|
|
38
|
+
"create_session",
|
|
39
|
+
"DatabaseFile",
|
|
40
|
+
"DatabaseManager",
|
|
41
|
+
"BaseTable",
|
|
42
|
+
"DatabaseIntegrityError"
|
|
43
|
+
]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
local_db.base_table.py
|
|
3
|
+
This module defines a base table class using SQLAlchemy"s declarative base.
|
|
4
|
+
It provides a foundation for creating database models and includes utility
|
|
5
|
+
functions to retrieve column names and their corresponding types.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
Base: The declarative base class for all database models.
|
|
9
|
+
BaseTable: An abstract base class for database tables with utility methods.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Standard library imports
|
|
13
|
+
from datetime import datetime, date, time, timedelta
|
|
14
|
+
from decimal import Decimal
|
|
15
|
+
from enum import Enum
|
|
16
|
+
import re
|
|
17
|
+
from typing import Any
|
|
18
|
+
import uuid
|
|
19
|
+
|
|
20
|
+
from sqlalchemy.orm import declarative_base
|
|
21
|
+
from sqlalchemy import (
|
|
22
|
+
Column, # Defines a column in a table
|
|
23
|
+
UniqueConstraint, # Ensures unique values in specified columns
|
|
24
|
+
Integer, # Integer type
|
|
25
|
+
String, # String type with optional length
|
|
26
|
+
Float, # Floating-point number
|
|
27
|
+
Boolean, # Boolean type (True/False)
|
|
28
|
+
Date, # Date type (year, month, day)
|
|
29
|
+
DateTime, # Date and time type
|
|
30
|
+
Time, # Time type (hour, minute, second)
|
|
31
|
+
Text, # Large text field
|
|
32
|
+
LargeBinary, # Binary data (e.g., files, images)
|
|
33
|
+
JSON, # JSON-encoded data
|
|
34
|
+
Enum, # Enumerated type with fixed values
|
|
35
|
+
Numeric, # Fixed-precision number
|
|
36
|
+
SmallInteger, # Smaller integer type
|
|
37
|
+
BigInteger, # Larger integer type
|
|
38
|
+
Interval, # Time interval
|
|
39
|
+
Unicode, # Unicode string
|
|
40
|
+
UnicodeText, # Large Unicode text field
|
|
41
|
+
PickleType, # Stores Python objects serialized via pickle
|
|
42
|
+
UUID, # Universally unique identifier
|
|
43
|
+
ARRAY, # Array type (PostgreSQL-specific)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
SQLA_TYPE_MAP = {
|
|
47
|
+
Integer: int,
|
|
48
|
+
String: str,
|
|
49
|
+
Float: float,
|
|
50
|
+
Boolean: bool,
|
|
51
|
+
Date: date,
|
|
52
|
+
DateTime: datetime,
|
|
53
|
+
Time: time,
|
|
54
|
+
Text: str,
|
|
55
|
+
LargeBinary: bytes,
|
|
56
|
+
JSON: dict,
|
|
57
|
+
Enum: Enum,
|
|
58
|
+
Numeric: Decimal,
|
|
59
|
+
SmallInteger: int,
|
|
60
|
+
BigInteger: int,
|
|
61
|
+
Interval: timedelta,
|
|
62
|
+
Unicode: str,
|
|
63
|
+
UnicodeText: str,
|
|
64
|
+
PickleType: Any,
|
|
65
|
+
UUID: uuid.UUID,
|
|
66
|
+
ARRAY: list,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Base = declarative_base()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class BaseTable(Base):
|
|
74
|
+
"""
|
|
75
|
+
Abstract base class for database tables.
|
|
76
|
+
|
|
77
|
+
properties:
|
|
78
|
+
- column_names: Returns a list of column names for the model.
|
|
79
|
+
- column_types: Returns a dictionary mapping column names to their types.
|
|
80
|
+
"""
|
|
81
|
+
__abstract__ = True # Prevents SQLAlchemy from creating a table for this class
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def get_column_names(cls):
|
|
85
|
+
"""Returns a list of column names for the model."""
|
|
86
|
+
return [column.name for column in cls.__table__.columns]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def get_column_sqla_types(cls):
|
|
91
|
+
"""Returns a dictionary mapping column names to their types."""
|
|
92
|
+
return {column.name: type(column.type) for column in cls.__table__.columns}
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def get_column_python_types(cls):
|
|
96
|
+
"""Returns a dictionary mapping column names to their types."""
|
|
97
|
+
return {column.name: SQLA_TYPE_MAP[type(column.type)] for column in cls.__table__.columns}
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def column_names(self):
|
|
101
|
+
"""Returns a list of column names for the instance."""
|
|
102
|
+
return self.get_column_names()
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def column_types(self):
|
|
106
|
+
"""Returns a dictionary mapping column names to their types for the instance."""
|
|
107
|
+
return self.get_column_sqla_types()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class DatabaseIntegrityError(Exception):
|
|
112
|
+
def __init__(self, orig: Exception, table: "BaseTable"):
|
|
113
|
+
self.table_name = table.__tablename__
|
|
114
|
+
self.message = str(orig.orig)
|
|
115
|
+
match = re.search(r"UNIQUE constraint failed: \w+\.(\w+)", self.message)
|
|
116
|
+
self.column = match.group(1) if match else None
|
|
117
|
+
detail = f", column: {self.column}" if self.column else ""
|
|
118
|
+
super().__init__(f"Database integrity error in {self.table_name}{detail}. {self.message}")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ItemNotFoundError(Exception):
|
|
123
|
+
def __init__(self, item_id, table: BaseTable, message: str="Item not found in database:"):
|
|
124
|
+
self.item_id = item_id
|
|
125
|
+
self.table_name = table.__tablename__
|
|
126
|
+
self.message = f"{message} {self.table_name}, id: {item_id}"
|
|
127
|
+
super().__init__(self.message)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!python3
|
|
2
|
+
"""
|
|
3
|
+
local_db.database_connections.py
|
|
4
|
+
|
|
5
|
+
This module contains functions which allow for connections to existing local database files
|
|
6
|
+
|
|
7
|
+
Functions:
|
|
8
|
+
- create_engine_conn: creates a SQLalchemy engine object
|
|
9
|
+
- create_session: creates and returns a SQLalchemy Session class object
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Third-Party library imports
|
|
13
|
+
from sqlalchemy import create_engine
|
|
14
|
+
from sqlalchemy.orm import sessionmaker
|
|
15
|
+
|
|
16
|
+
# Local Imports
|
|
17
|
+
from pleasant_database import DEFAULT_DB_DIRECTORY
|
|
18
|
+
from pleasant_database.utils import LoggingExtras
|
|
19
|
+
|
|
20
|
+
# Initialize module logger
|
|
21
|
+
import logging
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Functions
|
|
26
|
+
def create_engine_conn(relative_db_path:str=DEFAULT_DB_DIRECTORY) -> object:
|
|
27
|
+
"""
|
|
28
|
+
Create and returns an SQLalchemy engine from a given db URL string
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
relative_db_path (str): the relative path to the database file, must be a valid .db filename
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
engine: a SQLalchemy engine object which can be used to connect to the database
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
logger.info(f"Creating connection engine to {relative_db_path}...", extra={
|
|
38
|
+
LoggingExtras.FILE: relative_db_path
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return create_engine(f"sqlite:///{relative_db_path}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def create_session(engine):
|
|
45
|
+
"""
|
|
46
|
+
Create and return a SQLalchemy session
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
engine: a SQLalchemy engine object which can be used to connect to the database
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Session: a SQLalchemy session object which can be used to interact with the database
|
|
53
|
+
"""
|
|
54
|
+
logger.info(f"Creating session for {engine}...")
|
|
55
|
+
|
|
56
|
+
Session = sessionmaker(bind=engine)
|
|
57
|
+
return Session()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!python3
|
|
2
|
+
"""
|
|
3
|
+
local_db.database_file.py
|
|
4
|
+
|
|
5
|
+
this module creates, deletes, and archives database files within a python project
|
|
6
|
+
|
|
7
|
+
required project structure:
|
|
8
|
+
|
|
9
|
+
root\\
|
|
10
|
+
|───data
|
|
11
|
+
| └──db_files
|
|
12
|
+
| └──log_dir
|
|
13
|
+
|───src
|
|
14
|
+
└ └──package
|
|
15
|
+
|
|
16
|
+
Class:
|
|
17
|
+
- DatabaseFile: a class which represents a db file object
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Standard library imports
|
|
21
|
+
import sqlite3
|
|
22
|
+
|
|
23
|
+
# Third-party imports
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
# local imports
|
|
27
|
+
from . import DEFAULT_DB_DIRECTORY
|
|
28
|
+
from .utils import LoggingExtras, check_db_exists, is_db_file
|
|
29
|
+
|
|
30
|
+
# Initialize module logger
|
|
31
|
+
import logging
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DatabaseFile():
|
|
37
|
+
"""
|
|
38
|
+
Class which manages a single database file, name attribute must be valid .db filename
|
|
39
|
+
|
|
40
|
+
Functions:
|
|
41
|
+
- exxists: checks if the database file exists in the given directory
|
|
42
|
+
- create: creates a new database file in the given directory
|
|
43
|
+
- move: moves the database file to a new directory
|
|
44
|
+
- delete: deletes the database file from the filesystem
|
|
45
|
+
"""
|
|
46
|
+
def __init__(self, name, directory=DEFAULT_DB_DIRECTORY):
|
|
47
|
+
"""
|
|
48
|
+
initializes a DatabaseFile object with a name and directory
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
name (str): the name of the database file, must be a valid .db filename
|
|
52
|
+
directory (str): the relative directory where the database file is located, default is "os.curdir/data/db_files"
|
|
53
|
+
"""
|
|
54
|
+
# Check if the name is a valid database filename
|
|
55
|
+
if is_db_file(name):
|
|
56
|
+
self.name = name
|
|
57
|
+
else:
|
|
58
|
+
logger.error(f"{name} is not a valid database filename.", extra={LoggingExtras.FILE: name})
|
|
59
|
+
raise ValueError(f"Database file {name} is not a valid database filename.")
|
|
60
|
+
|
|
61
|
+
# Initialize the directory and path attributes
|
|
62
|
+
self.directory = directory
|
|
63
|
+
self.file_path = os.path.join(self.directory, self.name)
|
|
64
|
+
self.abspath = os.path.abspath(self.file_path)
|
|
65
|
+
|
|
66
|
+
# If the directory does not exist, create it
|
|
67
|
+
if not os.path.exists(self.directory):
|
|
68
|
+
logger.warning(f"Database directory {directory} does not exist. Creating directory...", extra={LoggingExtras.FILE: directory})
|
|
69
|
+
os.makedirs(directory)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def exists(self) -> bool:
|
|
73
|
+
"""checks the if the database name exists in the given directiry"""
|
|
74
|
+
return check_db_exists(self.name, self.directory)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def create(self):
|
|
78
|
+
"""creates a new database file in a given directory, default .\\data."""
|
|
79
|
+
logger.info(f"Creating database file {self.name}...")
|
|
80
|
+
if self.exists():
|
|
81
|
+
logger.info(f"Database file {self.name} already exists in {self.directory}", extra={LoggingExtras.FILE: os.path.join(self.directory, self.name)})
|
|
82
|
+
else:
|
|
83
|
+
sqlite3.connect(self.file_path).close()
|
|
84
|
+
logger.info(f"Database file {self.name} created.", extra={LoggingExtras.FILE: self.name})
|
|
85
|
+
logger.debug(f"New db file path: {self.file_path}", extra={LoggingExtras.FILE: self.file_path})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def move(self, target_directory: str) -> None:
|
|
89
|
+
"""moves a DatabaseFile object to a new directory"""
|
|
90
|
+
logger.info(f"Moving {self.name} from {self.directory} to {target_directory}...", extra={
|
|
91
|
+
LoggingExtras.FILE: self.name,
|
|
92
|
+
LoggingExtras.FILE: target_directory
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
# Check if db with that filename already exists in target dir. return False, not moved
|
|
96
|
+
target_exists = check_db_exists(self.name, target_directory)
|
|
97
|
+
if target_exists:
|
|
98
|
+
# Does nothing if it already exists
|
|
99
|
+
logger.error(f"Database file {self.name} already exists in {target_directory}.", extra={
|
|
100
|
+
LoggingExtras.FILE: self.name,
|
|
101
|
+
LoggingExtras.FILE: target_directory
|
|
102
|
+
})
|
|
103
|
+
elif self.exists():
|
|
104
|
+
# Moves to new directory if it doesn"t exist and the current file exists
|
|
105
|
+
new_path = os.path.join(target_directory, self.name)
|
|
106
|
+
os.rename(self.file_path, new_path)
|
|
107
|
+
self.file_path = new_path
|
|
108
|
+
self.abspath = os.path.abspath(self.file_path)
|
|
109
|
+
self.directory = target_directory
|
|
110
|
+
else:
|
|
111
|
+
logger.error(f"Database file {self.name} cannot be moved because it doesn't exist in {self.directory}", extra={
|
|
112
|
+
LoggingExtras.FILE: self.name,
|
|
113
|
+
LoggingExtras.FILE: self.directory
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def delete(self):
|
|
118
|
+
"""deletes the file managed by the DatabaseFile instance"""
|
|
119
|
+
if os.path.exists(self.abspath):
|
|
120
|
+
os.remove(self.abspath)
|
|
121
|
+
logger.info(f"Database file {self.name} deleted from {self.directory}...", extra={
|
|
122
|
+
LoggingExtras.FILE: self.name,
|
|
123
|
+
LoggingExtras.FILE: self.directory
|
|
124
|
+
})
|
|
125
|
+
else:
|
|
126
|
+
logger.error(f"DatabaseFile.delete() -> {self.name} does not exist in {self.directory}. Cannot delete.", extra={
|
|
127
|
+
LoggingExtras.FILE: self.name,
|
|
128
|
+
LoggingExtras.FILE: self.directory
|
|
129
|
+
})
|