#
##
## SPDX-FileCopyrightText: © 2007-2023 Benedict Verhegghe <bverheg@gmail.com>
## SPDX-License-Identifier: GPL-3.0-or-later
##
## This file is part of pyFormex 3.4 (Thu Nov 16 18:07:39 CET 2023)
## pyFormex is a tool for generating, manipulating and transforming 3D
## geometrical models by sequences of mathematical operations.
## Home page: https://pyformex.org
## Project page: https://savannah.nongnu.org/projects/pyformex/
## Development: https://gitlab.com/bverheg/pyformex
## Distributed under the GNU General Public License version 3 or later.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see http://www.gnu.org/licenses/.
##
"""A simple Database class
"""
import json
import pyformex as pf
from pyformex.mydict import Dict
class Record(Dict):
def __init__(self, data=None):
super().__init__(default_factory=Dict.returnNone, **data)
[docs]class Database(dict):
"""A class for storing data in a database-like structure.
The Database is a Dict where each key is a unique identifier string
and each value is a record. A record is itself a Dict , where each
key is a string (the field name) and the value can be anything.
Parameters
----------
default_factory: callable, optional
If provided, missing keys will be looked up by a call to the
default_factory.
data: dict | list | str, optional
The Database can be initialized from a dict, a list of records or
a file in a proper format.
If a dict, the Database will be directly initialized from it.
If a list, each record should have at least a key named 'name',
holding the string identifying the record. This value should be unique
in the list (or records will be overwritten). The 'name' value will be
use as a key to store the record in the Database.
If a string, it is the name of a Database file in one of the
supported formats, from where it will be read. See :meth:`read`.
If not provided, an empty database is constructed.
key: str, optional
The field name to be used as a key, if list type data are specified.
Examples
--------
>>> rec1 = {'id': 'john', 'first_name': 'John', 'last_name': 'Doe'}
>>> rec2 = {'id': 'jane', 'first_name': 'Jane', 'last_name': 'Roe'}
>>> db = Database(data=[rec1, rec2])
>>> print(db)
{
"john": {
"id": "john",
"first_name": "John",
"last_name": "Doe"
},
"jane": {
"id": "jane",
"first_name": "Jane",
"last_name": "Roe"
}
}
>>> db.write('test.json')
>>> db1 = Database(data='test.json')
>>> print(db1)
{
"john": {
"id": "john",
"first_name": "John",
"last_name": "Doe"
},
"jane": {
"id": "jane",
"first_name": "Jane",
"last_name": "Roe"
}
}
>>> print(type(db1['john']))
<class 'pyformex.database.Record'>
"""
format = 'pyfdb 1.0'
def __init__(self, *, data=None, key='id', record=Record):
"""Initialize a database"""
super().__init__()
self._key = key
self._record = record
if data is not None:
if isinstance(data, str):
self.read(data)
else:
self.add(data)
[docs] def add(self, data, key=None):
"""Add a list of records to the database.
Parameters
----------
data: list
A list of records. Each record should be a dict and have at least
a field name equal to the value specified for ``key``.
key: str, optional
The field name in the records to be used as the key to store the
records in the Database.
"""
if key is None:
key = self._key
self.update([(r[key], self._record(r)) for r in data])
[docs] def write(self, filename):
"""Export the Database to a file.
The file is written in .json format and contains a dict
with two keys: 'key' and 'records'. The key is a
"""
contents = list(self.values())
contents.insert(0, {
'format': Database.format,
'creator': pf.fullVersion(),
'key': self._key,
'nrecs': len(contents),
})
with open(filename, 'w+') as fil:
json.dump(contents, fil, indent=2)
fil.write('\n')
[docs] def read(self, filename):
"""Import all records from a Database file.
Parameters
----------
filename: :term:`path_like`
The file holding the Database. The file should be in
.json format as written by :meth:`write`.
Examples
--------
>>> db = Database()
>>> db.read(pf.cfg['prop/matdb'])
>>> print(db)
{
"steel": {
"name": "steel",
"young_modulus": 207000.0,
"poisson_ratio": 0.3,
"density": 7.85e-09
},
...
"""
with open(filename, 'r') as fil:
contents = json.load(fil)
header = contents.pop(0)
if header.get('format', None) != Database.format:
raise ValueError(
f"{filename} is not a pyFormex Database file")
self.add(contents, key=header['key'])
def __str__(self):
return json.dumps(self, indent=4)
# End