-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDatabaseManager.py
More file actions
244 lines (218 loc) · 10.3 KB
/
Copy pathDatabaseManager.py
File metadata and controls
244 lines (218 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#!/usr/bin/env python3
"""
Database Manager for Cosmos Collection
Handles database connections and table initialization
"""
import sqlite3
import logging
from contextlib import contextmanager
# Set up logging
logger = logging.getLogger(__name__)
class DatabaseManager:
"""Singleton database manager for the Cosmos Collection application"""
_instance = None
_connection = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(DatabaseManager, cls).__new__(cls)
cls._instance._initialize_database()
return cls._instance
def _initialize_database(self):
"""Create necessary tables if they do not exist, e.g. usersettings."""
try:
with self.get_connection() as conn:
cursor = conn.cursor()
# Create usersettings table
cursor.execute("""
CREATE TABLE IF NOT EXISTS usersettings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
location_lat REAL,
location_lon REAL,
location_name TEXT,
timezone TEXT
)
""")
# Create usertelescopes table
cursor.execute("""
CREATE TABLE IF NOT EXISTS usertelescopes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
aperture REAL,
focal_length REAL,
mount_type TEXT,
notes TEXT,
created_date TEXT DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1
)
""")
# Create userequipment table for cameras, eyepieces, barlows, and reducers
cursor.execute("""
CREATE TABLE IF NOT EXISTS userequipment (
id INTEGER PRIMARY KEY AUTOINCREMENT,
equipment_type TEXT NOT NULL,
name TEXT NOT NULL,
sensor_width REAL,
sensor_height REAL,
focal_length REAL,
apparent_fov REAL,
factor REAL,
notes TEXT,
created_date TEXT DEFAULT CURRENT_TIMESTAMP
)
""")
# Create junction table for telescope-equipment many-to-many relationship
cursor.execute("""
CREATE TABLE IF NOT EXISTS telescope_equipment (
id INTEGER PRIMARY KEY AUTOINCREMENT,
telescope_id INTEGER NOT NULL REFERENCES usertelescopes(id) ON DELETE CASCADE,
equipment_id INTEGER NOT NULL REFERENCES userequipment(id) ON DELETE CASCADE,
UNIQUE(telescope_id, equipment_id)
)
""")
# Create userimages table if it doesn't exist
cursor.execute("""
CREATE TABLE IF NOT EXISTS userimages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dsodetailid INTEGER,
image_path TEXT,
integration_time TEXT,
equipment TEXT,
date_taken TEXT,
notes TEXT,
is_favorite BOOLEAN DEFAULT 0,
created_date TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (dsodetailid) REFERENCES dso_detail (id)
)
""")
# Add is_favorite column if it doesn't exist (for existing databases)
try:
cursor.execute("ALTER TABLE userimages ADD COLUMN is_favorite BOOLEAN DEFAULT 0")
logger.debug("Added is_favorite column to userimages table")
except sqlite3.OperationalError:
# Column already exists, ignore
pass
# Add created_date column if it doesn't exist (for existing databases)
# Note: ALTER TABLE cannot use CURRENT_TIMESTAMP as default, so we use NULL
try:
cursor.execute("ALTER TABLE userimages ADD COLUMN created_date TEXT")
logger.debug("Added created_date column to userimages table")
except sqlite3.OperationalError:
# Column already exists, ignore
pass
# Add is_active column to usersettings for multi-location support
try:
cursor.execute("ALTER TABLE usersettings ADD COLUMN is_active INTEGER DEFAULT 0")
# Mark most recent record as active for migration
cursor.execute("""
UPDATE usersettings SET is_active = 1
WHERE id = (SELECT id FROM usersettings ORDER BY id DESC LIMIT 1)
""")
logger.debug("Added is_active column to usersettings table")
except sqlite3.OperationalError:
# Column already exists, ignore
pass
# Create usertargetlist table for user's observing target list
cursor.execute("""
CREATE TABLE IF NOT EXISTS usertargetlist (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
dso_type TEXT,
constellation TEXT,
ra_deg REAL,
dec_deg REAL,
magnitude REAL,
size_info TEXT,
priority TEXT DEFAULT 'Medium',
status TEXT DEFAULT 'Not Observed',
best_months TEXT,
notes TEXT,
date_added TEXT,
date_observed TEXT,
created_date TEXT DEFAULT CURRENT_TIMESTAMP,
telescope_id INTEGER REFERENCES usertelescopes(id) ON DELETE SET NULL
)
""")
# Add telescope_id column if it doesn't exist (for existing databases)
try:
cursor.execute("ALTER TABLE usertargetlist ADD COLUMN telescope_id INTEGER REFERENCES usertelescopes(id) ON DELETE SET NULL")
except sqlite3.OperationalError:
pass # Column already exists
# Create usercollages table for user's collage projects
cursor.execute("""
CREATE TABLE IF NOT EXISTS usercollages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dsodetailid INTEGER,
name TEXT NOT NULL,
grid_width INTEGER NOT NULL DEFAULT 3,
grid_height INTEGER NOT NULL DEFAULT 3,
cell_size INTEGER NOT NULL DEFAULT 400,
spacing INTEGER NOT NULL DEFAULT 20,
background_color TEXT NOT NULL DEFAULT 'black',
created_date TEXT NOT NULL,
modified_date TEXT NOT NULL,
FOREIGN KEY (dsodetailid) REFERENCES dsodetail(id)
)
""")
# Create usercollageimages table for collage image associations
cursor.execute("""
CREATE TABLE IF NOT EXISTS usercollageimages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
collage_id INTEGER NOT NULL,
userimage_id INTEGER NOT NULL,
position_index INTEGER NOT NULL,
FOREIGN KEY (collage_id) REFERENCES usercollages(id) ON DELETE CASCADE,
FOREIGN KEY (userimage_id) REFERENCES userimages(id) ON DELETE CASCADE
)
""")
conn.commit()
logger.debug("Database tables initialized successfully")
except Exception as e:
logger.error(f"Error initializing database: {str(e)}")
@contextmanager
def get_connection(self):
"""Get a database connection with proper error handling"""
if self._connection is None:
# Import here to avoid circular imports
from ResourceManager import ResourceManager, attach_update_catalogs
db_path = ResourceManager.get_database_path()
self._connection = sqlite3.connect(str(db_path))
self._connection.row_factory = sqlite3.Row
attach_update_catalogs(self._connection)
try:
yield self._connection
except Exception as e:
logger.error(f"Database error: {str(e)}")
raise
def close(self):
"""Close the database connection"""
if self._connection:
self._connection.close()
self._connection = None
logger.debug("Database connection closed")
def execute_query(self, query, params=None):
"""Execute a query and return results"""
try:
with self.get_connection() as conn:
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
return cursor.fetchall()
except Exception as e:
logger.error(f"Error executing query: {query}, error: {str(e)}")
raise
def execute_update(self, query, params=None):
"""Execute an update/insert/delete query"""
try:
with self.get_connection() as conn:
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
conn.commit()
return cursor.rowcount
except Exception as e:
logger.error(f"Error executing update: {query}, error: {str(e)}")
raise