Source code for modelarchive.modelcif.ma_dict

"""Functionality for ModelCIF data/ categories when represented as
:class:`dict`.
"""

# Copyright (c) 2026, SIB - Swiss Institute of Bioinformatics and
#                     Biozentrum - University of Basel
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


[docs] def add_row_to_category_dict( cat_dict, row_dict, ordinal_item=None, null_value=False ): """Add a row to a category dict or create it if empty. Appends a new row to ``cat_dict``, which holds mmCIF category data as a column-oriented dict of lists, as obtained from ``gemmi.cif.Block.find_mmcif_category(raw=False)`` or an empty dict. If ``cat_dict`` is empty, it is populated from ``row_dict`` directly. Args: cat_dict (dict[str]): Column-oriented category dict to update. Each key maps to a list of values, one entry per row. row_dict (dict[str]): Dict with keys and single values representing the new row. Keys not present in ``cat_dict`` are added as new columns, back-filled with ``null_value``. Keys only in ``cat_dict`` are filled with ``null_value``. ordinal_item (:class:`str` or ``None``): Key whose value is auto-incremented on each call. Set to ``"1"`` when ``cat_dict`` is empty. If set but not present in a non-empty ``cat_dict``, it is silently ignored. Defaults to ``None``. null_value: Value used to fill missing keys. ``False`` for inapplicable (``'.'``), ``None`` for unknown (``'?'``). Defaults to ``False``. Returns: :class:`str` or ``None``: The new ordinal value as a string, or ``None`` if ``ordinal_item`` is not set. Raises: ValueError: If column lengths in ``cat_dict`` are inconsistent after appending. """ new_ordinal = None if cat_dict: # add row to cat_dict num_rows = None for key in cat_dict: if key == ordinal_item: new_ordinal = str(int(cat_dict[key][-1]) + 1) cat_dict[key].append(new_ordinal) else: cat_dict[key].append(row_dict.get(key, null_value)) # check all lengths equal if num_rows is None: num_rows = len(cat_dict[key]) else: if num_rows != len(cat_dict[key]): raise ValueError( f"Column length mismatch for key '{key}': expected " f"{num_rows}, got {len(cat_dict[key])} values" ) # add extra columns if needed for key in row_dict: if key not in cat_dict: cat_dict[key] = [null_value] * (num_rows - 1) cat_dict[key].append(row_dict[key]) else: # fill new cat_dict for key, val in row_dict.items(): cat_dict[key] = [val] if ordinal_item is not None: new_ordinal = "1" cat_dict[ordinal_item] = [new_ordinal] return new_ordinal