Source code for aequilibrae.project.about
import string
import uuid
from os.path import join, dirname, realpath
from aequilibrae.project.project_creation import run_queries_from_sql_file
from aequilibrae.utils.db_utils import commit_and_close
from aequilibrae.utils.spatialite_utils import connect_spatialite
[docs]
class About:
"""Provides an interface for querying and editing the **about** table of an AequilibraE project
.. code-block:: python
>>> project = create_example(project_path)
# Adding a new field and saving it
>>> project.about.add_info_field('my_super_relevant_field')
>>> project.about.my_super_relevant_field = 'super relevant information'
>>> project.about.write_back()
# changing the value for an existing value/field
>>> project.about.scenario_name = 'Just a better scenario name'
>>> project.about.write_back()
"""
[docs]
def __init__(self, project):
self.__characteristics = []
self.__original = {}
self.__path_to_file = project.path_to_file
self.logger = project.logger
with commit_and_close(connect_spatialite(self.__path_to_file)) as conn:
if self.__has_about(conn):
self.__load(conn)
[docs]
def create(self):
"""Creates the 'about' table for project files that did not previously contain it"""
with commit_and_close(connect_spatialite(self.__path_to_file)) as conn:
if not self.__has_about(conn):
qry_file = join(dirname(realpath(__file__)), "database_specification", "tables", "about.sql")
run_queries_from_sql_file(conn, self.logger, qry_file)
sql = "SELECT count(*) as num_records from about;"
if conn.execute(sql).fetchone()[0] == 0:
conn.execute(f"UPDATE 'about' set infovalue='{uuid.uuid4().hex}' where infoname='project_ID'")
conn.execute("UPDATE 'about' set infovalue='right' where infoname='driving_side'")
self.__load(conn)
else:
self.logger.warning("About table already exists. Nothing was done")
[docs]
def list_fields(self) -> list:
"""Returns a list of all characteristics the about table holds"""
return list(self.__characteristics)
[docs]
def add_info_field(self, info_field: str) -> None:
"""Adds new information field to the model
:Arguments:
**info_field** (:obj:`str`): Name of the desired information field to be added. Has to be a valid
Python VARIABLE name (i.e. letter as first character, no spaces and no special characters)
.. code-block:: python
>>> project = create_example(project_path)
>>> project.about.add_info_field('a_cool_field')
>>> project.about.a_cool_field = 'super relevant information'
>>> project.about.write_back()
"""
allowed = string.ascii_lowercase + "_"
has_forbidden = [x for x in info_field if x not in allowed]
if has_forbidden:
raise ValueError(f"{info_field} is not valid as a metadata field. Should be a lower case ascii letter or _")
with commit_and_close(connect_spatialite(self.__path_to_file)) as conn:
conn.execute("INSERT INTO 'about' (infoname) VALUES(?)", [info_field])
self.__characteristics.append(info_field)
self.__original[info_field] = None
[docs]
def write_back(self):
"""Saves the information parameters back to the project database
.. code-block:: python
>>> project = create_example(project_path)
>>> project.about.description = 'This is the example project. Do not use for forecast'
>>> project.about.write_back()
"""
with commit_and_close(connect_spatialite(self.__path_to_file)) as conn:
for k in self.__characteristics:
v = self.__dict__[k]
if v != self.__original[k]:
conn.execute("UPDATE 'about' set infovalue = ? where infoname=?", [v, k])
self.logger.info(f"Updated {k} on About_Table to {v}")
def __has_about(self, conn):
sql = "SELECT name FROM sqlite_master WHERE type='table';"
return any("about" in x[0] for x in conn.execute(sql).fetchall())
def __load(self, conn):
self.__characteristics = []
sql = "select infoname, infovalue from 'about'"
for x in conn.execute(sql).fetchall():
self.__characteristics.append(x[0])
self.__dict__[x[0]] = x[1]
self.__original[x[0]] = x[1]