예제 #1
0
    def test_single_row(self, rw_data):
        myDataList = []
        curContainer = DataContainer("myblock")
        aCat = DataCategory("pdbx_seqtool_mapping_ref")
        aCat.appendAttribute("ordinal")
        aCat.appendAttribute("entity_id")
        aCat.appendAttribute("auth_mon_id")
        aCat.appendAttribute("auth_mon_num")
        aCat.appendAttribute("pdb_chain_id")
        aCat.appendAttribute("ref_mon_id")
        aCat.appendAttribute("ref_mon_num")
        aCat.appendAttribute("details")
        aCat.append([1, 2, 3, 4, 5, 6, 7, 'data_my_big_data_file'])
        aCat.append([1, 2, 3, 4, 5, 6, 7, 'loop_my_big_data_loop'])
        aCat.append([1, 2, 3, 4, 5, 6, 7, 'save_my_big_data_saveframe'])
        aCat.append([1, 2, 3, 4, 5, 6, 7, '_category.item'])

        curContainer.append(aCat)

        bCat = curContainer.getObj("pdbx_seqtool_mapping_ref")
        print("----attribute list %r\n" % bCat.getAttributeList())
        row = bCat.getRow(0)
        print("----ROW %r\n" % row)

        with open(str(rw_data['pathOutputFile2']), "w") as ofh:
            myDataList.append(curContainer)
            pdbxW = PdbxWriter(ofh)
            pdbxW.write(myDataList)

        assert len(myDataList) == 1
예제 #2
0
    def testSingleRow(self):
        """Test case -  read /write single row and null row in data file
        """
        try:
            #
            myDataList = []
            # ofh = open(self.__pathOutputFile1, "w")
            curContainer = DataContainer("myblock")
            aCat = DataCategory("pdbx_seqtool_mapping_ref")
            aCat.appendAttribute("ordinal")
            aCat.appendAttribute("entity_id")
            aCat.appendAttribute("auth_mon_id")
            aCat.appendAttribute("auth_mon_num")
            aCat.appendAttribute("pdb_chain_id")
            aCat.appendAttribute("ref_mon_id")
            aCat.appendAttribute("ref_mon_num")
            aCat.appendAttribute("details")
            aCat.append([1, 2, 3, 4, 5, 6, 7, "data_my_big_data_file"])
            aCat.append([1, 2, 3, 4, 5, 6, 7, "loop_my_big_data_loop"])
            aCat.append([1, 2, 3, 4, 5, 6, 7, "save_my_big_data_saveframe"])
            aCat.append([1, 2, 3, 4, 5, 6, 7, "_category.item"])
            # aCat.dumpIt()
            curContainer.append(aCat)
            #
            bCat = curContainer.getObj("pdbx_seqtool_mapping_ref")
            logger.debug("----attribute list %r", bCat.getAttributeList())
            row = bCat.getRow(0)
            logger.debug("----ROW %r", row)
            #
            with open(self.__pathOutputFile2, "w") as ofh:
                myDataList.append(curContainer)
                pdbxW = PdbxWriter(ofh)
                pdbxW.write(myDataList)

            self.assertEqual(len(myDataList), 1)
        except Exception as e:
            logger.exception("Failing with %s", str(e))
            self.fail()
예제 #3
0
class CIF(object):
    """
    This class uses the mmcif library to create an mmCIF-like object
    Each object has one container, with a container ID and a list of DataCategory objects
    """
    DUMMY_CONTAINER_ID = "emd_0000"

    def __init__(self, cif_name_name):
        self.filename = cif_name_name
        # self.__dataList needed for PDBxWriter
        self.__dataList = []
        self.__container_id = None
        self.__container = None
        self.__dataMap = {}

    def write(self):
        """
        Given a file name, a pdbx writer is used to write data stored in self.__dataList
        :return written: a boolean; True when pdf writer is finished
        """
        written = False
        if self.filename:
            ofh = open(self.filename, "w")
            pdbx_writer = PdbxWriter(ofh)
            pdbx_writer.write(self.__dataList)
            ofh.close()
            written = True

        return written

    def add_container(self, container_id):
        """
        This method provides the basic functionality to set up a container
        :param container_id: a string; an mmcif category e.g. 'emd_admin'
        :return:
        """
        added = False
        self.__container_id = container_id
        self.__container = DataContainer(container_id)
        self.__dataMap[container_id] = len(self.__dataList)
        self.__dataList.append(self.__container)
        if self.__container is not None:
            added = True
        return added

    def prepare_container(self, container_id):
        """
        Creates a container is it doesn't exist using either provided value or the dummy value
        :param container_id: a string; an mmcif category e.g. 'emd_admin'
        :return:
        """
        if not self.__container:
            if container_id is None:
                container_id = self.DUMMY_CONTAINER_ID

            return self.add_container(container_id)

    def add_category(self, category_id, items):
        """
        This method creates a data category object, adds all items to it and appends it to the container
        :param category_id: a string; an mmcif category e.g. 'emd_admin'
        :param items: a list of strings; each element in the list is an item of mmcif category as defined by category_id
        :return: a list of strings; each element represents a value for the corresponding element in data_items
        """
        category = DataCategory(category_id)
        for item in items:
            category.appendAttribute(item)
        self.__container.append(category)

    #
    # def update_single_row_value(self, category_id, item_name, row, value):
    #     """Update value in single row
    #     """
    #     catObj = self.__container.getObj(category_id)
    #     if catObj is None:
    #         return
    #     #
    #     catObj.setValue(value, item_name, row)
    #
    # def update_multiple_rows_value(self, category_id, item_name, value):
    #     """Update value in multiple rows
    #     """
    #     cat_obj = self.__container.getObj(category_id)
    #     if cat_obj is None:
    #         return
    #     #
    #     row_no = cat_obj.getRowCount()
    #     for row in range(0, row_no):
    #         cat_obj.setValue(value, item_name, row)

    def insert_data(self, category_id, data_list):
        """
        This method appends the data in data_list to the container labeled category_id
        :param category_id: a string; an mmcif category e.g. 'emd_admin'
        :param data_list:
        :return:
        """
        cat_obj = self.__container.getObj(category_id)
        if cat_obj is None:
            return
        if any(isinstance(el, list) for el in data_list):
            # print(data_list)
            for data_ord in data_list[0]:
                new_list = []
                ord_index = data_list[0].index(data_ord)
                new_list.append(ord_index)
                new_list.append(data_list[1][ord_index])
                # print(new_list)
                cat_obj.append(new_list)
        else:
            cat_obj.append(data_list)

    def insert_data_into_category(self, category_id, data_items, data_list):
        """
        Helper method: calls two other methods, one to add a category and its items into a container and
        another to insert the data for the category items
        :param category_id: a string; an mmcif category e.g. 'emd_admin'
        :param data_items: a list of strings; each element in the list is an item of mmcif category as defined by category_id
        :param data_list: a list of strings; each element represents a value for the corresponding element in data_items
        :return:
        """
        # print('INSERT DATA INTO CATEGORY:', category_id, data_items, data_list)
        self.add_category(category_id, data_items)
        self.insert_data(category_id, data_list)
예제 #4
0
    def __writeModel(self, targetId, targetObj, fitFD, fitXyzMapD,
                     fitAtomUnMappedL, matchD, modelId, modelPath):
        """Write the chemical component model for the input chemical component Id and associated atom mapping and
        feature details --

            ComponentAtomDetails = namedtuple("ComponentAtomDetails", "index atNo name aType x y z fCharge")
            AlignAtomMap = namedtuple("AlignAtomMap", "refId refAtIdx refAtNo refAtName fitId fitAtIdx fitAtNo fitAtName")
            AlignAtomUnMapped = namedtuple("AlignAtomUnMapped", "fitId fitAtIdx fitAtNo fitAtType fitAtName fitAtFormalCharge x y z fitNeighbors")
        """
        try:
            unMappedTypeD = defaultdict(int)
            hAtomPrefix = "HEX"
            variantType = self.__getBuildVariant(targetId)
            #
            if not self.__testUnMappedProtonation(fitAtomUnMappedL):
                logger.info(
                    "Unmapped non-hydrogen atoms target %r model %r unMapped count (%d)",
                    targetId, modelId, len(fitAtomUnMappedL))
                return False, variantType
            # Get atom partners for the unmapped atoms
            fitAtMapD = {}
            for refAtName, fAtTup in fitXyzMapD.items():
                fitAtMapD[fAtTup.atName] = refAtName
            if fitAtomUnMappedL:
                #  Check if neighbors are all mapped
                ok = True
                for fitUnTup in fitAtomUnMappedL:
                    for nAtName in fitUnTup.fitNeighbors:
                        if nAtName not in fitAtMapD:
                            ok = False
                            logger.info(
                                "Missing mapped neighbor for %r target %r model %r",
                                nAtName, targetId, modelId)
                            break
                if not ok:
                    return False, variantType
                else:
                    logger.debug("%s match has unmapped protonation", modelId)
                    variantType = "tautomer_protomer"
            #
            #
            kList = ["xyz", "SMILES", "SMILES_STEREO", "InChI", "InChIKey"]
            for k in kList:
                if k not in fitFD:
                    logger.error(
                        "Fit feature dictionary for %s missing key %s",
                        targetId, k)
                    return False, variantType
            # ------------
            dataContainer = DataContainer(modelId)
            #
            myContainer = targetObj
            dbName = myContainer.getName()
            if dbName.upper() != targetId.upper():
                logger.info("mismatch datablock (%r) and targetId (%r)",
                            dbName, targetId)
            cObj = None
            if myContainer.exists("chem_comp"):
                cObj = myContainer.getObj("chem_comp")
            #
            #
            catName = "pdbx_chem_comp_model"
            if not dataContainer.exists(catName):
                dataContainer.append(
                    DataCategory(catName, attributeNameList=["id", "comp_id"]))
            #
            parentId = targetId.split("|")[0]
            wObj = dataContainer.getObj(catName)
            wObj.setValue(modelId, "id", 0)
            wObj.setValue(parentId, "comp_id", 0)
            #
            # --------  ---------
            catName = "pdbx_chem_comp_model_atom"
            if not dataContainer.exists(catName):
                dataContainer.append(
                    DataCategory(catName,
                                 attributeNameList=[
                                     "model_id", "atom_id", "type_symbol",
                                     "charge", "model_Cartn_x",
                                     "model_Cartn_y", "model_Cartn_z",
                                     "ordinal_id"
                                 ]))
            wObj = dataContainer.getObj(catName)
            #
            if myContainer.exists("chem_comp_atom"):
                cObj = myContainer.getObj("chem_comp_atom")
            #
            #  Only write the mapped atoms in case we are missing hydrogens in the mapping
            #
            jj = 0
            for ii in range(cObj.getRowCount()):
                atName = cObj.getValue("atom_id", ii)
                atType = cObj.getValue("type_symbol", ii)
                if atName not in fitXyzMapD:
                    unMappedTypeD[atType] += 1
                    continue
                fitXyz = fitXyzMapD[atName]
                #
                # fCharge = cObj.getValue("charge", ii)
                #
                wObj.setValue(modelId, "model_id", jj)
                wObj.setValue(atName, "atom_id", jj)
                wObj.setValue(atType, "type_symbol", jj)
                #
                wObj.setValue(fitXyz.atFormalCharge, "charge", jj)
                wObj.setValue("%.4f" % fitXyz.x, "model_Cartn_x", jj)
                wObj.setValue("%.4f" % fitXyz.y, "model_Cartn_y", jj)
                wObj.setValue("%.4f" % fitXyz.z, "model_Cartn_z", jj)
                wObj.setValue(jj + 1, "ordinal_id", jj)
                jj += 1
            #
            # Add the unmapped atoms ...
            # AlignAtomUnMapped = namedtuple("AlignAtomUnMapped", "fitId fitAtIdx fitAtNo fitAtType fitAtName fitNeighbors")
            ii = wObj.getRowCount()
            for jj, uTup in enumerate(fitAtomUnMappedL):
                refAtomName = hAtomPrefix + str(jj)
                wObj.setValue(modelId, "model_id", ii)
                wObj.setValue(refAtomName, "atom_id", ii)
                wObj.setValue(uTup.fitAtType, "type_symbol", ii)
                wObj.setValue(uTup.fitAtFormalCharge, "charge", ii)
                wObj.setValue("%.4f" % uTup.x, "model_Cartn_x", ii)
                wObj.setValue("%.4f" % uTup.y, "model_Cartn_y", ii)
                wObj.setValue("%.4f" % uTup.z, "model_Cartn_z", ii)
                wObj.setValue(ii + 1, "ordinal_id", ii)
            # --------  ---------
            catName = "pdbx_chem_comp_model_bond"
            if not dataContainer.exists(catName):
                dataContainer.append(
                    DataCategory(catName,
                                 attributeNameList=[
                                     "model_id", "atom_id_1", "atom_id_2",
                                     "value_order", "ordinal_id"
                                 ]))
            wObj = dataContainer.getObj(catName)
            #
            if myContainer.exists("chem_comp_bond"):
                cObj = myContainer.getObj("chem_comp_bond")
            #
            jj = 0
            for ii in range(cObj.getRowCount()):
                at1 = cObj.getValue("atom_id_1", ii)
                if at1 not in fitXyzMapD:
                    continue
                at2 = cObj.getValue("atom_id_2", ii)
                if at2 not in fitXyzMapD:
                    continue
                bType = cObj.getValue("value_order", ii)
                #
                wObj.setValue(modelId, "model_id", jj)
                wObj.setValue(at1, "atom_id_1", jj)
                wObj.setValue(at2, "atom_id_2", jj)
                wObj.setValue(bType, "value_order", jj)
                wObj.setValue(jj + 1, "ordinal_id", jj)
                jj += 1
            #
            ii = wObj.getRowCount()
            for jj, uTup in enumerate(fitAtomUnMappedL):
                at1 = hAtomPrefix + str(jj)
                for nAt in uTup.fitNeighbors:
                    at2 = fitAtMapD[nAt]
                    wObj.setValue(modelId, "model_id", ii)
                    wObj.setValue(at1, "atom_id_1", ii)
                    wObj.setValue(at2, "atom_id_2", ii)
                    wObj.setValue("SING", "value_order", ii)
                    wObj.setValue(ii + 1, "ordinal_id", ii)

            # --------  ---------
            catName = "pdbx_chem_comp_model_descriptor"
            if not dataContainer.exists(catName):
                dataContainer.append(
                    DataCategory(
                        catName,
                        attributeNameList=["model_id", "type", "descriptor"]))
            wObj = dataContainer.getObj(catName)
            #
            ii = 0
            wObj.setValue(modelId, "model_id", ii)
            wObj.setValue("SMILES", "type", ii)
            wObj.setValue(fitFD["SMILES"], "descriptor", ii)
            ii += 1
            wObj.setValue(modelId, "model_id", ii)
            wObj.setValue("SMILES_CANONICAL", "type", ii)
            wObj.setValue(fitFD["SMILES_STEREO"], "descriptor", ii)
            ii += 1
            wObj.setValue(modelId, "model_id", ii)
            wObj.setValue("InChI", "type", ii)
            wObj.setValue(fitFD["InChI"], "descriptor", ii)
            ii += 1
            wObj.setValue(modelId, "model_id", ii)
            wObj.setValue("InChIKey", "type", ii)
            wObj.setValue(fitFD["InChIKey"], "descriptor", ii)
            #
            # --------  ---------
            if matchD["queryId"] is not None:
                catName = "pdbx_chem_comp_model_reference"
                if not dataContainer.exists(catName):
                    dataContainer.append(
                        DataCategory(catName,
                                     attributeNameList=[
                                         "model_id", "db_name", "db_code"
                                     ]))
                wObj = dataContainer.getObj(catName)
                ii = 0
                wObj.setValue(modelId, "model_id", ii)
                wObj.setValue("COD", "db_name", ii)
                wObj.setValue(matchD["queryId"], "db_code", ii)
            #
            featureD = {}
            v = matchD["rValue"]
            vS = str(v)
            if v is not None and len(vS) > 0:
                featureD["r_factor"] = "%.3f" % float(v)
            #
            v = matchD["diffrnTemp"]
            vS = str(v)
            # remove string artifacts from temperature string ...
            if v is not None and len(vS) > 0:
                tV = vS.upper()
                try:
                    if tV.endswith("DEG.C"):
                        tV = tV.replace("AT", "")
                        tV = tV.replace("DEG.C", "")
                        tV = float(tV.strip())
                        tV = tV + 273.15
                    else:
                        tV = tV.replace("AT", "")
                        tV = tV.replace("K", "")
                        tV = float(tV.strip())
                    featureD["experiment_temperature"] = tV
                except Exception as e:
                    logger.exception(
                        "Temperature conversion fails for %s (%r) with %s",
                        modelId, vS, tV)
            #
            v = matchD["publicationDOI"]
            vS = str(v)
            if v is not None and len(vS) > 0:
                featureD["publication_doi"] = v
            #
            v = matchD["version"]
            vS = str(v)
            if v is not None and len(vS) > 0:
                featureD["cod_version"] = v
            #
            if matchD["radiationSource"] and "neutron" in matchD[
                    "radiationSource"]:
                featureD["neutron_radiation_experiment"] = True
            if matchD["hasDisorder"] in ["Y"]:
                featureD["has_disorder"] = True
            #
            if len(unMappedTypeD) == 1 and "H" in unMappedTypeD:
                logger.info("model %r heavy_atoms_only", modelId)
                featureD["heavy_atoms_only"] = True
            else:
                featureD["all_atoms_have_sites"] = True
            # --------  ---------
            catName = "pdbx_chem_comp_model_feature"
            if not dataContainer.exists(catName):
                dataContainer.append(
                    DataCategory(catName,
                                 attributeNameList=[
                                     "model_id", "feature_name",
                                     "feature_value"
                                 ]))
            wObj = dataContainer.getObj(catName)
            #
            fKeyList = [
                "experiment_temperature", "publication_doi", "r_factor",
                "csd_version"
            ]
            ii = 0
            for fKey in fKeyList:
                if fKey in featureD:
                    wObj.setValue(modelId, "model_id", ii)
                    wObj.setValue(fKey, "feature_name", ii)
                    wObj.setValue(str(featureD[fKey]), "feature_value", ii)
                    ii += 1

            #
            boolKeyList = [
                "has_disorder", "neutron_radiation_experiment",
                "heavy_atoms_only", "all_atoms_have_sites"
            ]
            for fKey in boolKeyList:
                if fKey in featureD:
                    if featureD[fKey]:
                        wObj.setValue(modelId, "model_id", ii)
                        wObj.setValue(fKey, "feature_name", ii)
                        wObj.setValue("Y", "feature_value", ii)
                        ii += 1
            #

            if variantType:
                wObj.setValue(modelId, "model_id", ii)
                wObj.setValue(variantType + "_match", "feature_name", ii)
                wObj.setValue("Y", "feature_value", ii)
                ii += 1

            # --------  ---------
            catName = "pdbx_chem_comp_model_audit"
            if not dataContainer.exists(catName):
                dataContainer.append(
                    DataCategory(
                        catName,
                        attributeNameList=["model_id", "action_type", "date"]))
            wObj = dataContainer.getObj(catName)
            #
            ii = 0
            wObj.setValue(modelId, "model_id", ii)
            wObj.setValue("Initial release", "action_type", ii)
            wObj.setValue(self.__getToday(), "date", ii)
            # wObj.setValue('RCSB', 'processing_site',  ii)
            # wObj.setValue('JDW', 'annotator', ii)
            # wObj.setValue('?', 'details', ii)
            #
            mU = MarshalUtil(workPath=self.__cachePath)
            ok = mU.doExport(modelPath, [dataContainer], fmt="mmcif")
            return ok, variantType
        except Exception as e:
            logger.exception("Failing for %r with %s", targetId, str(e))
        return False, ""
예제 #5
0
    def __buildCif(self, rD, containerName="vrpt"):
        """ Construct a mmCIF data category objects for the input
            extracted data.

        Args:
            rD (dict): extracted data organized by category.
            containerName (str) : data block name

        Returns:
            containers (list):  data container list
        """
        #

        curContainer = DataContainer(containerName)
        for elName in rD:
            catName = elName
            if (not rD[elName]) or (not self.__attribD[catName]) or (
                    catName in ["programs"]):
                continue
            hasOrdinal = "ordinal" in self.__attribD[catName]
            rowList = rD[elName]
            # Find the unique attribute content across the rowlist and the ordinal value
            atS = set()
            for ii, rowD in enumerate(rowList, 1):
                if hasOrdinal:
                    rowD["ordinal"] = ii
                if "icode" in rowD:
                    rowD["icode"] = str(rowD["icode"]).strip()
                if "altcode" in rowD:
                    rowD["altcode"] = str(rowD["altcode"]).strip()
                atS.update(rowD.keys())
            attributeNameList = list(atS)
            #
            # Set a reasonable order for these attributes
            #
            sD = {ky: self.__atOrdD[ky] for ky in attributeNameList}
            srtAtL = [
                tup[0]
                for tup in sorted(sD.items(), key=operator.itemgetter(1))
            ]
            logger.debug("Category %s sorted attributes %r", catName, srtAtL)

            aCat = DataCategory(catName, srtAtL, rowList)
            curContainer.append(aCat)
        #
        # Adjust schema names -
        #
        atD = self.__dictionaryMap["attributes"]
        for catName in curContainer.getObjNameList():
            catObj = curContainer.getObj(catName)
            atNameList = catObj.getAttributeList()
            mapD = {}
            mapCatName = self.__dictionaryMap["categories"][
                catName] if catName in self.__dictionaryMap[
                    "categories"] else catName
            for atName in atNameList:
                mapD[atName] = atD[(catName, atName)]["at"] if (
                    catName, atName) in atD else atName
            catObj.renameAttributes(mapD)
            catObj.setName(mapCatName)
        #
        # Map provenance items from programs.properties -
        #
        catObj = curContainer.getObj("program")
        if catObj and catObj.hasAttribute("properties"):
            for iRow in range(catObj.getRowCount()):
                pV = catObj.getValue("properties", iRow)
                pVL = [v.strip() for v in pV.split(",")]
                nL = [
                    self.__atMap[ky] if ky in self.__atMap else ky
                    for ky in pVL
                ]
                catObj.setValue(",".join(nL), "properties", iRow)
                # logger.info("Row %r properties %r" % (iRow, pV))
        #
        return [curContainer]
예제 #6
0
class mmCIFUtil:
    """Using pdbx mmCIF utility to parse mmCIF file"""
    def __init__(self, verbose=False, log=sys.stderr, filePath=None):  # pylint: disable=unused-argument
        # self.__verbose = verbose
        self.__lfh = log
        self.__filePath = filePath
        self.__dataList = []
        self.__dataMap = {}
        self.__container = None
        self.__blockID = None
        self.__read()
        #

    def __read(self):
        if not self.__filePath:
            return
        #
        try:
            ifh = open(self.__filePath, "r")
            pRd = PdbxReader(ifh)
            pRd.read(self.__dataList)
            ifh.close()
            if self.__dataList:
                self.__container = self.__dataList[0]
                self.__blockID = self.__container.getName()
                idx = 0
                for container in self.__dataList:
                    self.__dataMap[container.getName()] = idx
                    idx += 1
                #
            #
        except Exception as e:
            self.__lfh.write("Read %s failed %s.\n" %
                             (self.__filePath, str(e)))
        #

    def GetBlockID(self):
        """Return first block ID"""
        return self.__blockID

    def GetValueAndItemByBlock(self, blockName, catName):
        """Get category values and item names"""
        dList = []
        iList = []
        if blockName not in self.__dataMap:
            return dList, iList
        #
        catObj = self.__dataList[self.__dataMap[blockName]].getObj(catName)
        if not catObj:
            return dList, iList
        #
        iList = catObj.getAttributeList()
        rowList = catObj.getRowList()
        for row in rowList:
            tD = {}
            for idxIt, itName in enumerate(iList):
                if row[idxIt] != "?" and row[idxIt] != ".":
                    tD[itName] = row[idxIt]
            #
            if tD:
                dList.append(tD)
            #
        #
        return dList, iList

    def GetValueAndItem(self, catName):
        dList, iList = self.GetValueAndItemByBlock(self.__blockID, catName)
        return dList, iList

    def GetValue(self, catName):
        """Get category values based on category name 'catName'. The results are stored
        in a list of dictionaries with item name as key
        """
        dList, _iList = self.GetValueAndItemByBlock(self.__blockID, catName)
        return dList

    def GetSingleValue(self, catName, itemName):
        """Get the first value of item name 'itemName' from 'itemName' item in 'catName' category."""
        text = ""
        dlist = self.GetValue(catName)
        if dlist:
            if itemName in dlist[0]:
                text = dlist[0][itemName]
        return text
        #

    def UpdateSingleRowValue(self, catName, itemName, row, value):
        """Update value in single row"""
        catObj = self.__container.getObj(catName)
        if catObj is None:
            return
        #
        catObj.setValue(value, itemName, row)

    def UpdateMultipleRowsValue(self, catName, itemName, value):
        """Update value in multiple rows"""
        catObj = self.__container.getObj(catName)
        if catObj is None:
            return
        #
        rowNo = catObj.getRowCount()
        for row in range(0, rowNo):
            catObj.setValue(value, itemName, row)
        #

    def AddBlock(self, blockID):
        """Add Data Block"""
        self.__container = DataContainer(blockID)
        self.__blockID = blockID
        self.__dataMap[blockID] = len(self.__dataList)
        self.__dataList.append(self.__container)

    def AddCategory(self, categoryID, items):
        """Add Category"""
        category = DataCategory(categoryID)
        for item in items:
            category.appendAttribute(item)
        #
        self.__container.append(category)

    def RemoveCategory(self, categoryID):
        return self.__container.remove(categoryID)

    def InsertData(self, categoryID, dataList):
        """"""
        catObj = self.__container.getObj(categoryID)
        if catObj is None:
            return
        #
        for data in dataList:
            catObj.append(data)
        #

    def WriteCif(self, outputFilePath=None):
        """Write out cif file"""
        if not outputFilePath:
            return
        #
        ofh = open(outputFilePath, "w")
        pdbxW = PdbxWriter(ofh)
        pdbxW.write(self.__dataList)
        ofh.close()

    def GetCategories(self):
        return self.__container.getObjNameList()

    def GetAttributes(self, category):
        return self.__container.getObj(category).getAttributeList()

    def category_as_dict(self, category, block=None):
        if block is None:
            block = self.__blockID
        values, attributes = self.GetValueAndItemByBlock(block, category)
        data = [[x[y] if y in x else None for y in attributes] for x in values]
        return {category: {"Items": attributes, "Values": data}}

    def block_as_dict(self, block=None):
        if block is None:
            block = self.__blockID
        data = {}
        for category in self.GetCategories():
            data.update(self.category_as_dict(category, block=block))
        return data