Skip to content

Subcomponent Models - Attributes

SubcomponentModelAttributes(perfdb)

Class used for handling subcomponent models attributes. Can be accessed via perfdb.components.subcomponents.models.attributes.

Parameters:

  • perfdb

    (PerfDB) –

    Top level object carrying all functionality and the connection handler.

Source code in echo_postgres/perfdb_root.py
def __init__(self, perfdb: e_pg.PerfDB) -> None:
    """Base class that all subclasses should inherit from.

    Parameters
    ----------
    perfdb : PerfDB
        Top level object carrying all functionality and the connection handler.

    """
    self._perfdb: e_pg.PerfDB = perfdb

delete(subcomponent_model_name, attribute_name)

Deletes an attribute value.

Parameters:

  • subcomponent_model_name

    (str) –

    Name of the subcomponent model to delete the attribute value from.

  • attribute_name

    (str) –

    Name of the attribute to delete the value from.

Source code in echo_postgres/subcomponent_model_attributes.py
@validate_call
def delete(
    self,
    subcomponent_model_name: str,
    attribute_name: str,
) -> None:
    """Deletes an attribute value.

    Parameters
    ----------
    subcomponent_model_name : str
        Name of the subcomponent model to delete the attribute value from.
    attribute_name : str
        Name of the attribute to delete the value from.
    """
    # building the query
    query = [
        sql.SQL(
            "DELETE FROM performance.subcomponent_model_attributes "
            "WHERE subcomponent_model_id = (SELECT id FROM performance.subcomponent_models WHERE name = {subcomponent_model_name}) "
            "AND attribute_id = (SELECT id FROM performance.attributes_def WHERE name = {attribute_name}) ",
        ).format(
            subcomponent_model_name=sql.Literal(subcomponent_model_name),
            attribute_name=sql.Literal(attribute_name),
        ),
    ]
    # executing the query
    with self._perfdb.conn.reconnect() as conn:
        # deleting
        result = conn.execute(sql.Composed(query))

    logger.debug(f"Deleted {result.rowcount} rows from performance.subcomponent_model_attributes")

get(subcomponent_models=None, attribute_names=None, filter_type='and', output_type='dict', values_only=False)

Method to get the attributes of the given subcomponent models.

The most useful keys/columns returned are:

  • subcomponent_model_id
  • subcomponent_model_name
  • attribute_id
  • attribute_name
  • attribute_display_name
  • attribute_value
  • data_type_name

Parameters:

  • subcomponent_models

    (list[str] | None, default: None ) –

    List of subcomponent models to get the attributes from. If set to None will get from all. By default None

  • attribute_names

    (list[str] | None, default: None ) –

    List of attribute names to filter the results. If set to None will get all. By default None

  • filter_type

    (Literal['and', 'or'], default: 'and' ) –

    How to treat multiple filters. Can be one of ["and", "or"]. By default "and"

  • output_type

    (Literal['dict', 'DataFrame'], default: 'dict' ) –

    Output type of the data. Can be one of ["dict", "DataFrame"] By default "dict"

  • values_only

    (bool, default: False ) –

    If set to True, will only return the values of the attributes, skipping display_name, id, etc.

Returns:

  • dict[str, dict[str, Any | dict[str, Any]]]

    In case output_type is "dict", returns a dictionary in the format {subcomponent_model_name: {attribute_name: {attribute: value, ...}, ...}, ...} If values_only is set to True, returns a dictionary in the format {subcomponent_model_name: {attribute_name: value, ...}, ...}

  • DataFrame

    In case output_type is "DataFrame", returns a DataFrame with the following format: index = MultiIndex[subcomponent_model_name, attribute_name], columns = [attribute, ...] If values_only is set to True, returns a DataFrame with the following format: index = MultiIndex[subcomponent_model_name, attribute_name], columns = ["attribute_value"]

Source code in echo_postgres/subcomponent_model_attributes.py
@validate_call
def get(
    self,
    subcomponent_models: list[str] | None = None,
    attribute_names: list[str] | None = None,
    filter_type: Literal["and", "or"] = "and",
    output_type: Literal["dict", "DataFrame"] = "dict",
    values_only: bool = False,
) -> dict[str, dict[str, Any | dict[str, Any]]] | DataFrame:
    """Method to get the attributes of the given subcomponent models.

    The most useful keys/columns returned are:

    - subcomponent_model_id
    - subcomponent_model_name
    - attribute_id
    - attribute_name
    - attribute_display_name
    - attribute_value
    - data_type_name

    Parameters
    ----------
    subcomponent_models : list[str] | None, optional
        List of subcomponent models to get the attributes from. If set to None will get from all. By default None
    attribute_names : list[str] | None, optional
        List of attribute names to filter the results. If set to None will get all. By default None
    filter_type : Literal["and", "or"], optional
        How to treat multiple filters. Can be one of ["and", "or"].
        By default "and"
    output_type : Literal["dict", "DataFrame"], optional
        Output type of the data. Can be one of ["dict", "DataFrame"]
        By default "dict"
    values_only : bool, optional
        If set to True, will only return the values of the attributes, skipping display_name, id, etc.

    Returns
    -------
    dict[str, dict[str, Any | dict[str, Any]]]
        In case output_type is "dict", returns a dictionary in the format {subcomponent_model_name: {attribute_name: {attribute: value, ...}, ...}, ...}
        If values_only is set to True, returns a dictionary in the format {subcomponent_model_name: {attribute_name: value, ...}, ...}
    DataFrame
        In case output_type is "DataFrame", returns a DataFrame with the following format: index = MultiIndex[subcomponent_model_name, attribute_name], columns = [attribute, ...]
        If values_only is set to True, returns a DataFrame with the following format: index = MultiIndex[subcomponent_model_name, attribute_name], columns = ["attribute_value"]
    """
    # checking if all subcomponent models are valid
    if subcomponent_models:
        existing_models = self._perfdb.components.subcomponents.models.get_ids(subcomponent_models=subcomponent_models)
        if len(existing_models) == 0:
            raise ValueError(f"The subcomponent models {subcomponent_models} do not exist")
        component_type = next(iter(existing_models.keys()))
        subcomponent_type = next(iter(existing_models[component_type].keys()))
        if missing_models := set(subcomponent_models) - set(existing_models[component_type][subcomponent_type].keys()):
            raise ValueError(f"The following subcomponent models do not exist: {missing_models}")

    # building the query
    query = [
        sql.SQL(
            "SELECT {values} FROM performance.v_subcomponent_model_attributes",
        ).format(
            values=sql.SQL(
                "subcomponent_model_name, attribute_name, attribute_value, data_type_name",
            )
            if values_only
            else sql.SQL("*"),
        ),
    ]
    where = []
    if subcomponent_models:
        where.append(
            sql.SQL(" subcomponent_model_name IN ({subcomponent_models}) ").format(
                subcomponent_models=sql.SQL(",").join(sql.Literal(om) for om in subcomponent_models),
            ),
        )
    if attribute_names:
        where.append(
            sql.SQL(" attribute_name IN ({attribute_names}) ").format(
                attribute_names=sql.SQL(",").join(sql.Literal(an) for an in attribute_names),
            ),
        )
    if where:
        where = sql.SQL(f" {filter_type.upper()} ").join(where)
        query.append(sql.SQL(" WHERE "))
        query.append(where)
    query.append(sql.SQL(" ORDER BY subcomponent_model_name, attribute_name"))
    query = sql.Composed(query)

    # executing the query
    with self._perfdb.conn.reconnect() as conn:
        # setting attribute_value as subcomponent to avoid casting json column as string
        df = conn.read_to_pandas(query, post_convert="pyarrow")

    # casting the attribute values
    df = cast_attributes(df=df, index_cols=["subcomponent_model_name"])

    df = df.set_index(["subcomponent_model_name", "attribute_name"])

    # returning the result
    if output_type == "dict":
        # dropping unwanted columns
        if values_only:
            df = df["attribute_value"]
            output = df.to_dict()
        else:
            output = df[["attribute_id", "attribute_value", "data_type_id", "data_type_name", "modified_date"]].to_dict(orient="index")
        # converting dict where the keys are tuples {(key1, key2): value}, to a dict where the keys are strings like {key1: {key2: value}}
        new_output = {}
        for (om, an), values in output.items():
            if om not in new_output:
                new_output[om] = {}
            new_output[om][an] = values
        return new_output
    if output_type == "DataFrame" and values_only:
        df = df[["attribute_value"]].copy()

    return df

insert(subcomponent_model_name, attribute_name, attribute_value, on_conflict='raise')

Inserts a new attribute value.

Parameters:

  • subcomponent_model_name

    (str) –

    Name of the subcomponent model to insert the attribute value to.

  • attribute_name

    (str) –

    Name of the attribute to insert the value to.

  • attribute_value

    (Any) –

    Value of the attribute.

  • on_conflict

    (Literal['raise', 'ignore', 'update'], default: 'raise' ) –

    What to do in case of conflict. Can be one of ["raise", "ignore", "update"]. By default "raise"

Source code in echo_postgres/subcomponent_model_attributes.py
@validate_call
def insert(
    self,
    subcomponent_model_name: str,
    attribute_name: str,
    attribute_value: Any,
    on_conflict: Literal["raise", "ignore", "update"] = "raise",
) -> None:
    """Inserts a new attribute value.

    Parameters
    ----------
    subcomponent_model_name : str
        Name of the subcomponent model to insert the attribute value to.
    attribute_name : str
        Name of the attribute to insert the value to.
    attribute_value : Any
        Value of the attribute.
    on_conflict : Literal["raise", "ignore", "update"], optional
        What to do in case of conflict. Can be one of ["raise", "ignore", "update"].
        By default "raise"
    """
    # checking if subcomponent model exists
    existing_models = self._perfdb.components.subcomponents.models.get_ids(subcomponent_models=[subcomponent_model_name])
    if len(existing_models) == 0:
        raise ValueError(f"The subcomponent model {subcomponent_model_name} does not exist")
    component_type = next(iter(existing_models.keys()))
    subcomponent_type = next(iter(existing_models[component_type].keys()))
    model_id = existing_models[component_type][subcomponent_type][subcomponent_model_name]

    # checking and casting the attribute value
    insert_attribute_value, attribute_id = check_attribute_dtype(
        attribute_name=attribute_name,
        attribute_value=attribute_value,
        perfdb=self._perfdb,
    )

    # building the query
    query = [
        sql.SQL(
            "INSERT INTO performance.subcomponent_model_attributes (subcomponent_model_id, attribute_id, value) "
            "VALUES ({subcomponent_model_id}, {attribute_id}, {attribute_value}) ",
        ).format(
            subcomponent_model_id=sql.Literal(model_id),
            attribute_id=sql.Literal(attribute_id),
            attribute_value=sql.Literal(insert_attribute_value),
        ),
    ]
    match on_conflict:
        case "raise":
            # doing nothing will raise conflicts as expected
            pass
        case "ignore":
            query.append(sql.SQL(" ON CONFLICT DO NOTHING "))
        case "update":
            query.append(
                sql.SQL(
                    " ON CONFLICT (subcomponent_model_id, attribute_id) DO UPDATE SET value = EXCLUDED.value ",
                ),
            )

    # executing the query
    with self._perfdb.conn.reconnect() as conn:
        conn.execute(sql.Composed(query))

    logger.debug(f"Attribute '{attribute_name}' inserted to subcomponent model '{subcomponent_model_name}'")

update(subcomponent_model_name, attribute_name, attribute_value)

Updates an attribute value.

Parameters:

  • subcomponent_model_name

    (str) –

    Name of the subcomponent model to update the attribute value from.

  • attribute_name

    (str) –

    Name of the attribute to update the value from.

  • attribute_value

    (Any) –

    New value of the attribute.

Source code in echo_postgres/subcomponent_model_attributes.py
@validate_call
def update(
    self,
    subcomponent_model_name: str,
    attribute_name: str,
    attribute_value: Any,
) -> None:
    """Updates an attribute value.

    Parameters
    ----------
    subcomponent_model_name : str
        Name of the subcomponent model to update the attribute value from.
    attribute_name : str
        Name of the attribute to update the value from.
    attribute_value : Any
        New value of the attribute.
    """
    # checking if subcomponent model exists
    existing_models = self._perfdb.components.subcomponents.models.get_ids(subcomponent_models=[subcomponent_model_name])
    if len(existing_models) == 0:
        raise ValueError(f"The subcomponent model {subcomponent_model_name} does not exist")
    component_type = next(iter(existing_models.keys()))
    subcomponent_type = next(iter(existing_models[component_type].keys()))
    model_id = existing_models[component_type][subcomponent_type][subcomponent_model_name]

    # checking and casting the attribute value
    insert_attribute_value, attribute_id = check_attribute_dtype(
        attribute_name=attribute_name,
        attribute_value=attribute_value,
        perfdb=self._perfdb,
    )

    # building the query
    query = [
        sql.SQL(
            "UPDATE performance.subcomponent_model_attributes "
            "SET value = {attribute_value} "
            "WHERE subcomponent_model_id = {subcomponent_model_id} "
            "AND attribute_id = {attribute_id} ",
        ).format(
            subcomponent_model_id=sql.Literal(model_id),
            attribute_id=sql.Literal(attribute_id),
            attribute_value=sql.Literal(insert_attribute_value),
        ),
    ]
    # executing the query
    with self._perfdb.conn.reconnect() as conn:
        conn.execute(sql.Composed(query))

    logger.debug(f"Attribute '{attribute_name}' updated in subcomponent model '{subcomponent_model_name}'")