@@ -35,28 +35,33 @@ class FairDatabase(object):
3535 >>> query_output_string = db2.query('SELECT uuid, json FROM model')
3636
3737 """
38+
3839 def __init__ (self , path ):
3940 self ._path = pathlib .Path (path )
4041 self ._initialize ()
4142
4243 def _initialize (self ):
4344 """Initialize database with tables if necessary."""
4445 with sqlite3 .connect (self ._path ) as conn :
45- conn .execute ("""CREATE TABLE IF NOT EXISTS models (
46+ conn .execute (
47+ """CREATE TABLE IF NOT EXISTS models (
4648 uuid string,
4749 name string,
4850 creation_date text NOT NULL,
4951 json string NOT NULL,
5052 CONSTRAINT model_pk PRIMARY KEY (uuid));
51- """ )
52- conn .execute ("""CREATE TABLE IF NOT EXISTS results (
53+ """
54+ )
55+ conn .execute (
56+ """CREATE TABLE IF NOT EXISTS results (
5357 uuid string,
5458 mean real NOT NULL,
5559 stdev real NOT NULL,
5660 min real NOT NULL,
5761 max real NOT NULL,
5862 CONSTRAINT results_pk PRIMARY KEY (uuid));
59- """ )
63+ """
64+ )
6065
6166 def _dict_factory (self , cursor , row ):
6267 """Convenience function for sqlite queries"""
@@ -69,17 +74,24 @@ def _dict_factory(self, cursor, row):
6974 def load (self , name_or_uuid ):
7075 """Loads a model from the database
7176
72- This takes a name or UUID and looks up the model using a UUID
73- function using self._load_uuid(). If that fails, it attempts to
74- look of the funciton by name using self._load_name().
77+ This takes a name or UUID. It first attempts to interpret the input
78+ as a UUID and load directly. If that fails (e.g., the input is not
79+ in UUID format), it attempts to load by model name.
80+
81+ If loading by name, the method retrieves the *first* model found
82+ matching that name (based on internal database ordering). If multiple
83+ distinct models (with different UUIDs) share the same name, this
84+ may not be the most recent or a specific version unless names are
85+ managed uniquely. For precise loading, using the model's UUID is
86+ recommended.
7587
7688 Parameters
7789 ----------
7890 name_or_uuid : str
7991 The name model or its UUID string
8092
8193 Returns
82- -------
94+ ------
8395 FairModel or FairMetaModel
8496 The model or metamodel corresponding with the input UUID string
8597 or input name string.
@@ -89,6 +101,9 @@ def load(self, name_or_uuid):
89101 FairException
90102 When the UUID or name does not exist in the database
91103
104+ See Also
105+ --------
106+ store : Method for storing models. Note its behavior regarding UUIDs.
92107 """
93108 # If it is a valid UUID
94109 try :
@@ -110,9 +125,9 @@ def _load_name(self, name):
110125 cursor .execute ("SELECT uuid FROM models WHERE name = ?" , (name ,))
111126 result = cursor .fetchone ()
112127 if not result :
113- raise FairException (' Name for model not found.' )
128+ raise FairException (" Name for model not found." )
114129 # Use model UUID query to load via _load_uuid function
115- model = self ._load_uuid (result [' uuid' ])
130+ model = self ._load_uuid (result [" uuid" ])
116131 return model
117132
118133 def _load_uuid (self , uuid ):
@@ -125,17 +140,17 @@ def _load_uuid(self, uuid):
125140 cursor .execute ("SELECT * FROM models WHERE uuid = ?" , (uuid ,))
126141 model_data = cursor .fetchone ()
127142 if not model_data :
128- raise FairException (' UUID for model not found.' )
143+ raise FairException (" UUID for model not found." )
129144 # Load model type based on json
130- json_data = model_data [' json' ]
145+ json_data = model_data [" json" ]
131146 model_param_data = json .loads (json_data )
132- model_type = model_param_data [' type' ]
133- if model_type == ' FairMetaModel' :
147+ model_type = model_param_data [" type" ]
148+ if model_type == " FairMetaModel" :
134149 model = FairMetaModel .read_json (json_data )
135- elif model_type == ' FairModel' :
150+ elif model_type == " FairModel" :
136151 model = FairModel .read_json (json_data )
137152 else :
138- raise FairException (' Unrecognized model type.' )
153+ raise FairException (" Unrecognized model type." )
139154 return model
140155
141156 def store (self , model_or_metamodel ):
@@ -147,10 +162,31 @@ def store(self, model_or_metamodel):
147162 statistics about the risk are stored in the 'results' table, and 2)
148163 the model data is stored in the 'models' table.
149164
165+ The model's UUID (obtained via `model_or_metamodel.get_uuid()`) is used
166+ as the primary key in the database.
167+
168+ If a record with the same UUID already exists, `INSERT OR REPLACE`
169+ semantics are used, meaning the existing record for that UUID will be
170+ overwritten with the data from the model being stored. This is how
171+ updates to an existing model (identified by its UUID) should be performed:
172+ load the model, modify the loaded instance, then store that same instance.
173+
174+ Creating a new `FairModel()` instance results in a new, unique UUID.
175+ Storing such a new instance will always create a new record or replace
176+ an existing record *only if that new UUID happened to match an old one*
177+ (which is astronomically unlikely for standard UUIDs).
178+ It does not replace based on model name.
179+
180+ Parameters
181+ ----------
182+ model_or_metamodel : FairModel or FairMetaModel
183+ The model instance to store.
184+
150185 Raises
151186 ------
152187 FairException
153188 If model or metamodel is not yet calculated
189+ and thus not ready for storage.
154190
155191 """
156192 m = model_or_metamodel
@@ -160,30 +196,25 @@ def store(self, model_or_metamodel):
160196 # Export from model
161197 meta = json .loads (m .to_json ())
162198 json_data = m .to_json ()
163- results = m .export_results ()[' Risk' ]
199+ results = m .export_results ()[" Risk" ]
164200 # Write to database
165201 with sqlite3 .connect (self ._path ) as conn :
166202 cursor = conn .cursor ()
167203 # Write model data
168204 cursor .execute (
169205 """INSERT OR REPLACE INTO models VALUES(?, ?, ?, ?)""" ,
170- (
171- meta ['model_uuid' ],
172- meta ['name' ],
173- meta ['creation_date' ],
174- json_data
175- )
206+ (meta ["model_uuid" ], meta ["name" ], meta ["creation_date" ], json_data ),
176207 )
177208 # Write cached results
178209 cursor .execute (
179210 """INSERT OR REPLACE INTO results VALUES(?, ?, ?, ?, ?)""" ,
180211 (
181- meta [' model_uuid' ],
212+ meta [" model_uuid" ],
182213 results .mean (axis = 0 ),
183214 results .std (axis = 0 ),
184215 results .min (axis = 0 ),
185- results .max (axis = 0 )
186- )
216+ results .max (axis = 0 ),
217+ ),
187218 )
188219 # Vacuum database
189220 conn = sqlite3 .connect (self ._path )
0 commit comments