/
manager.py
248 lines (215 loc) · 8.21 KB
/
manager.py
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
245
246
247
248
# coding: utf-8
# Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
# Distributed under the terms of "New BSD License", see the LICENSE file.
"""
A class for mediating connections to SQL databases.
"""
from urllib.parse import quote_plus
from pyiron_base.state.logger import logger
from pyiron_base.interfaces.singleton import Singleton
from pyiron_base.state.settings import settings as s
from pyiron_base.database.generic import DatabaseAccess
import os
__author__ = "Jan Janssen, Liam Huber"
__copyright__ = (
"Copyright 2020, Max-Planck-Institut für Eisenforschung GmbH"
" - Computational Materials Design (CM) Department"
)
__version__ = "0.0"
__maintainer__ = "Liam Huber"
__email__ = "huber@mpie.de"
__status__ = "development"
__date__ = "Sep 24, 2021"
class DatabaseManager(metaclass=Singleton):
def __init__(self):
self._database = None
self._use_local_database = False
self._database_is_disabled = s.configuration["disable_database"]
self.open_connection()
@property
def database(self):
return self._database
@property
def using_local_database(self):
return self._use_local_database
@property
def database_is_disabled(self):
return self._database_is_disabled
@property
def project_check_enabled(self):
if self.database_is_disabled:
return False
else:
return s.configuration["project_check_enabled"]
@property
def connection_timeout(self):
"""
Get the connection timeout in seconds. Zero means close the database after every connection.
Returns:
int: timeout in seconds
"""
return s.configuration["connection_timeout"]
@connection_timeout.setter
def connection_timeout(self, val):
s.configuration["connection_timeout"] = val
@staticmethod
def _sqlalchemy_string(prefix, user, key, host, database):
key = quote_plus(key)
return f"{prefix}://{user}:{key}@{host}/{database}"
def _credentialed_sqalchemy_string(self, prefix):
return self._sqlalchemy_string(
prefix,
s.configuration["user"],
s.configuration["sql_user_key"],
s.configuration["sql_host"],
s.configuration["sql_database"],
)
@property
def sql_connection_string(self):
sql_type = s.configuration["sql_type"]
if sql_type == "Postgres":
return self._credentialed_sqalchemy_string("postgresql")
elif sql_type == "MySQL":
return self._credentialed_sqalchemy_string("mysql+pymysql")
elif sql_type == "SQLalchemy":
return s.configuration["sql_connection_string"]
elif sql_type == "SQLite":
return "sqlite:///" + s.configuration["sql_file"].replace("\\", "/")
else:
raise ValueError(
f"Invalid SQL type {sql_type} -- This should have been caught at input processing, please contact the "
f"developers"
)
@property
def sql_view_connection_string(self):
if s.configuration["sql_view_user"] is None:
return None
else:
return self._sqlalchemy_string(
"postgresql",
s.configuration["sql_view_user"],
s.configuration["sql_view_user_key"],
s.configuration["sql_host"],
s.configuration["sql_database"],
)
@property
def sql_table_name(self):
return s.configuration["sql_table_name"]
@property
def sql_view_table_name(self):
return s.configuration["sql_view_table_name"]
def open_connection(self):
"""
Internal function to open the connection to the database. Only after this function is called the database is
accessable.
"""
if self._database is None and not self.database_is_disabled:
self._database = DatabaseAccess(
self.sql_connection_string,
self.sql_table_name,
timeout=self.connection_timeout,
)
def switch_to_local_database(self, file_name="pyiron.db", cwd=None):
"""
Swtich to an local SQLite based database.
Args:
file_name (str): SQLite database file name
cwd (str/None): directory where the SQLite database file is located in
"""
if self.using_local_database:
logger.info("Database is already in local mode or disabled!")
else:
if cwd is None and not os.path.isabs(file_name):
file_name = os.path.join(os.path.abspath(os.path.curdir), file_name)
elif cwd is not None:
file_name = os.path.join(cwd, file_name)
self.close_connection()
self.open_local_sqlite_connection(
connection_string="sqlite:///" + file_name
)
def open_local_sqlite_connection(self, connection_string):
self._database = DatabaseAccess(connection_string, self.sql_table_name)
self._use_local_database = True
self._database_is_disabled = False
def switch_to_central_database(self):
"""
Switch to central database
"""
if self.using_local_database:
self.update()
else:
logger.info("Database is already in central mode or disabled!")
def switch_to_viewer_mode(self):
"""
Switch from user mode to viewer mode - if view_mode is enable pyiron has read only access to the database.
"""
if (
self.sql_view_connection_string is not None
and not self.database_is_disabled
):
if self._database.view_mode:
logger.info("Database is already in viewer mode!")
else:
self.close_connection()
self._database = DatabaseAccess(
self.sql_view_connection_string,
self.sql_view_table_name,
)
self._database.view_mode = True
else:
print("Viewer Mode is not available on this pyiron installation.")
def switch_to_user_mode(self):
"""
Switch from viewer mode to user mode - if view_mode is enable pyiron has read only access to the database.
"""
if (
self.sql_view_connection_string is not None
and not self.database_is_disabled
):
if self._database.view_mode:
self.close_connection()
self._database = DatabaseAccess(
self.sql_connection_string,
self.sql_table_name,
)
self._database.view_mode = False
else:
logger.info("Database is already in user mode!")
else:
print("Viewer Mode is not available on this pyiron installation.")
def close_connection(self):
"""
Internal function to close the connection to the database.
"""
if self._database is not None:
self._database.conn.close()
self._database = None
def top_path(self, full_path):
"""
Validated that the full_path is a sub directory of one of the pyrion environments loaded.
Args:
full_path (str): path
Returns:
str: path
"""
full_path = full_path if full_path.endswith("/") else full_path + "/"
for path in s.configuration["project_paths"]:
if path in full_path:
return path
if self.project_check_enabled:
raise ValueError(
f"the current path {full_path} is not included in the .pyiron configuration 'project_paths': "
f"{s.configuration['project_paths']}"
)
else:
return None
def update(self):
"""
Warning: Database interaction does not have written spec. This method does a thing. It might not be the thing
you want.
"""
self.close_connection()
self._use_local_database = False
self._database_is_disabled = s.configuration["disable_database"]
self.open_connection()
database = DatabaseManager()