From 0d76c71916788db9533468791a88ce840017f100 Mon Sep 17 00:00:00 2001
From: Peter Schubert <Peter.Schubert@hhu.de>
Date: Fri, 23 Dec 2022 10:48:31 +0100
Subject: [PATCH] import from df implemented

---
 rbaxdf/model/rba_densities.py      |  16 +++-
 rbaxdf/model/rba_enzymes.py        |   6 +-
 rbaxdf/model/rba_macromolecules.py |  35 ++++++++
 rbaxdf/model/rba_metabolism.py     |  65 ++++++++++-----
 rbaxdf/model/rba_model.py          |  32 ++++----
 rbaxdf/model/rba_parameters.py     |  46 ++++++++---
 rbaxdf/model/rba_processes.py      | 126 ++++++++++++++++++++---------
 rbaxdf/model/rba_target_value.py   |  21 +++--
 rbaxdf/model/rba_targets.py        |  36 +++++++--
 rbaxdf/utils/et_utils.py           |  33 --------
 rbaxdf/utils/utils.py              |  78 ++++++++++++++++++
 11 files changed, 356 insertions(+), 138 deletions(-)
 delete mode 100644 rbaxdf/utils/et_utils.py
 create mode 100644 rbaxdf/utils/utils.py

diff --git a/rbaxdf/model/rba_densities.py b/rbaxdf/model/rba_densities.py
index 3d236ff..48ca99c 100644
--- a/rbaxdf/model/rba_densities.py
+++ b/rbaxdf/model/rba_densities.py
@@ -8,6 +8,7 @@ import pandas as pd
 from xml.etree.ElementTree import parse, ElementTree, Element, SubElement, indent
 
 from .rba_target_value import RbaTargetValue
+from rbaxdf.utils.utils import extract_params
 
 
 class RbaDensities:
@@ -26,6 +27,9 @@ class RbaDensities:
         assert root.tag == 'RBADensity'
         self.densities = RbaDensity.import_xml(root.find('listOfTargetDensities'))
 
+    def from_df(self, df):
+        self.densities = RbaDensity.from_df(df)
+
     def export_xml(self, model_dir):
 
         file_name = os.path.join(model_dir, 'density.xml')
@@ -72,7 +76,17 @@ class RbaDensity:
         for target_density in target_densities.findall('targetDensity'):
             cid = target_density.attrib['compartment']
             rba_density = RbaDensity(cid)
-            rba_density.target_value = RbaTargetValue(target_density)
+            rba_density.target_value = RbaTargetValue.from_dict(target_density.attrib)
+            data[cid] = rba_density
+        return data
+
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for cid, row in df.iterrows():
+            rba_density = RbaDensity(cid)
+            target_value = extract_params(row['targetValue'])
+            rba_density.target_value = RbaTargetValue.from_dict(target_value)
             data[cid] = rba_density
         return data
 
diff --git a/rbaxdf/model/rba_enzymes.py b/rbaxdf/model/rba_enzymes.py
index b1f4965..ec7eaf2 100644
--- a/rbaxdf/model/rba_enzymes.py
+++ b/rbaxdf/model/rba_enzymes.py
@@ -8,7 +8,7 @@ import os
 import pandas as pd
 from xml.etree.ElementTree import parse, ElementTree, Element, SubElement, indent
 
-from rbaxdf.utils.et_utils import get_species_refs
+from rbaxdf.utils.utils import get_species_refs_from_xml
 
 
 class RbaEnzymes:
@@ -102,8 +102,8 @@ class RbaEnzyme:
 
             machinery_composition = enzyme.find('machineryComposition')
             if machinery_composition is not None:
-                rba_enzyme.mach_reactants = get_species_refs(machinery_composition.find('listOfReactants'))
-                rba_enzyme.mach_products = get_species_refs(machinery_composition.find('listOfProducts'))
+                rba_enzyme.mach_reactants = get_species_refs_from_xml(machinery_composition.find('listOfReactants'))
+                rba_enzyme.mach_products = get_species_refs_from_xml(machinery_composition.find('listOfProducts'))
             data[eid] = rba_enzyme
         return data
 
diff --git a/rbaxdf/model/rba_macromolecules.py b/rbaxdf/model/rba_macromolecules.py
index b4fc92c..663b7c5 100644
--- a/rbaxdf/model/rba_macromolecules.py
+++ b/rbaxdf/model/rba_macromolecules.py
@@ -32,6 +32,10 @@ class RbaMacromolecules:
         self.components = RbaComponent.import_xml(root.find('listOfComponents'))
         self.macromolecules = RbaMacromolecule.import_xml(root.find('listOfMacromolecules'))
 
+    def from_df(self, df):
+        self.components = RbaComponent.from_df(df)
+        self.macromolecules = RbaMacromolecule.from_df(df)
+
     def export_xml(self, model_dir):
         file_name = os.path.join(model_dir, self.type + '.xml')
         root = Element(type2tag[self.type])
@@ -58,6 +62,9 @@ class RbaMacromolecules:
         df.index.name = self.type
         return df
 
+    def validate(self, component_ids):
+        return True
+
 
 class RbaComponent:
 
@@ -79,6 +86,18 @@ class RbaComponent:
             data[cid] = rba_component
         return data
 
+    @staticmethod
+    def from_df(df):
+        data = {}
+        components = [col for col in df.columns if col != 'compartment']
+        for cid in components:
+            rba_component = RbaComponent(cid)
+            rba_component.name = df.at['name', cid]
+            rba_component.type = df.at['type', cid]
+            rba_component.weight = df.at['weight', cid]
+            data[cid] = rba_component
+        return data
+
     def export_xml(self):
         attribs = {'id': self.id, 'name': self.name, 'type': self.type, 'weight': str(self.weight)}
         return Element('component', attribs)
@@ -111,6 +130,22 @@ class RbaMacromolecule:
             data[mmid] = rba_macromolecule
         return data
 
+    @staticmethod
+    def from_df(df):
+        data = {}
+        components = [col for col in df.columns if col != 'compartment']
+        macromolecules = [idx for idx in df.index if idx not in ['name', 'type', 'weight']]
+        for mmid in macromolecules:
+            rba_macromolecule = RbaMacromolecule(mmid)
+            rba_macromolecule.compartment = df.at[mmid, 'compartment']
+            for component in components:
+                stoic = df.at[mmid, component]
+                if np.isfinite(stoic) and stoic > 0.0:
+                    rba_macromolecule.composition[component] = stoic
+            data[mmid] = rba_macromolecule
+
+        return data
+
     def export_xml(self):
         attribs = {'id': self.id, 'compartment': self.compartment}
         macromolecule = Element('macromolecule', attribs)
diff --git a/rbaxdf/model/rba_metabolism.py b/rbaxdf/model/rba_metabolism.py
index 4760c43..30f7a1d 100644
--- a/rbaxdf/model/rba_metabolism.py
+++ b/rbaxdf/model/rba_metabolism.py
@@ -7,7 +7,7 @@ import os
 import pandas as pd
 from xml.etree.ElementTree import parse, ElementTree, Element, SubElement, indent
 
-from rbaxdf.utils.et_utils import get_species_refs
+from rbaxdf.utils.utils import get_species_refs_from_xml, get_species_refs_from_str
 
 
 class RbaMetabolism:
@@ -32,6 +32,11 @@ class RbaMetabolism:
         self.species = RbaSpecies.import_xml(root.find('listOfSpecies'))
         self.reactions = RbaReaction.import_xml(root.find('listOfReactions'))
 
+    def from_df(self, df_c, df_s, df_r):
+        self.compartments = RbaCompartment.from_df(df_c)
+        self.species = RbaSpecies.from_df(df_s)
+        self.reactions = RbaReaction.from_df(df_r)
+
     def export_xml(self, model_dir):
         file_name = os.path.join(model_dir, 'metabolism.xml')
         root = Element('RBAMetabolism')
@@ -52,23 +57,14 @@ class RbaMetabolism:
         indent(tree)
         tree.write(file_name)
 
-    def to_df(self, m_type):
-        df = None
-        if m_type == 'compartments':
-            df = pd.DataFrame([item.to_dict() for item in self.compartments.values()])
-            df.index.name = 'index'
-
-        elif m_type == 'species':
-            df = pd.DataFrame([item.to_dict() for item in self.species.values()])
-            df.set_index('species', inplace=True)
-
-        elif m_type == 'reactions':
-            df = pd.DataFrame([item.to_dict() for item in self.reactions.values()])
-            df.set_index('reaction', inplace=True)
-
-        else:
-            print(f'wrong metabolism type: {m_type}')
-        return df
+    def to_df(self):
+        df_c = pd.DataFrame([item.to_dict() for item in self.compartments.values()])
+        df_c.index.name = 'index'
+        df_s = pd.DataFrame([item.to_dict() for item in self.species.values()])
+        df_s.set_index('species', inplace=True)
+        df_r = pd.DataFrame([item.to_dict() for item in self.reactions.values()])
+        df_r.set_index('reaction', inplace=True)
+        return df_c, df_s, df_r
 
     def validate(self, component_ids):
         valid = True
@@ -100,6 +96,15 @@ class RbaCompartment:
             data[cid] = rba_compartment
         return data
 
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for _, row in df.iterrows():
+            cid = row['compartment']
+            rba_compartment = RbaCompartment(cid)
+            data[cid] = rba_compartment
+        return data
+
     def export_xml(self):
         return Element('compartment', {'id': self.id})
 
@@ -125,6 +130,15 @@ class RbaSpecies:
             data[sid] = rba_species
         return data
 
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for sid, row in df.iterrows():
+            rba_species = RbaSpecies(sid)
+            rba_species.boundary_condition = row['boundaryCondition']
+            data[sid] = rba_species
+        return data
+
     def export_xml(self):
         attribs = {'id': self.id, 'boundaryCondition':  str(self.boundary_condition).lower()}
         return Element('species', attribs)
@@ -150,8 +164,19 @@ class RbaReaction:
             rba_reaction = RbaReaction(rid)
             if reaction.attrib['reversible'].lower() == 'false':
                 rba_reaction.reversible = False
-            rba_reaction.reactants = get_species_refs(reaction.find('listOfReactants'))
-            rba_reaction.products = get_species_refs(reaction.find('listOfProducts'))
+            rba_reaction.reactants = get_species_refs_from_xml(reaction.find('listOfReactants'))
+            rba_reaction.products = get_species_refs_from_xml(reaction.find('listOfProducts'))
+            data[rid] = rba_reaction
+        return data
+
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for rid, row in df.iterrows():
+            rba_reaction = RbaReaction(rid)
+            rba_reaction.reversible = row['reversible']
+            rba_reaction.reactants = get_species_refs_from_str(row['reactants'])
+            rba_reaction.products = get_species_refs_from_str(row['products'])
             data[rid] = rba_reaction
         return data
 
diff --git a/rbaxdf/model/rba_model.py b/rbaxdf/model/rba_model.py
index 205d192..cc206dc 100644
--- a/rbaxdf/model/rba_model.py
+++ b/rbaxdf/model/rba_model.py
@@ -63,22 +63,27 @@ class RbaModel:
     def to_df(self):
         m_dict = {}
         if self.is_model is True:
-            m_dict['rnas'] = self.rnas.to_df()
+            m_dict['compartments'], m_dict['species'], m_dict['reactions'] = self.metabolism.to_df()
             m_dict['dna'] = self.dna.to_df()
+            m_dict['rnas'] = self.rnas.to_df()
             m_dict['proteins'] = self.proteins.to_df()
             m_dict['enzymes'] = self.enzymes.to_df()
             m_dict['densities'] = self.densities.to_df()
             m_dict['targets'] = self.targets.to_df()
-            m_dict['compartments'] = self.metabolism.to_df('compartments')
-            m_dict['species'] = self.metabolism.to_df('species')
-            m_dict['reactions'] = self.metabolism.to_df('reactions')
-            m_dict['functions'] = self.parameters.to_df('functions')
-            m_dict['aggregates'] = self.parameters.to_df('aggregates')
-            m_dict['processes'] = self.processes.to_df('processes')
-            m_dict['processing_maps'] = self.processes.to_df('processingMaps')
-
+            m_dict['functions'], m_dict['aggregates'] = self.parameters.to_df()
+            m_dict['processes'], m_dict['processing_maps'] = self.processes.to_df()
         return m_dict
 
+    def from_df(self, m_dict):
+        self.metabolism.from_df(m_dict['compartments'], m_dict['species'], m_dict['reactions'])
+        self.dna.from_df(m_dict['dna'])
+        self.rnas.from_df(m_dict['rnas'])
+        self.proteins.from_df(m_dict['proteins'])
+        self.densities.from_df(m_dict['densities'])
+        self.targets.from_df(m_dict['targets'])
+        self.parameters.from_df(m_dict['functions'], m_dict['aggregates'])
+        self.processes.from_df(m_dict['processes'], m_dict['processing_maps'])
+
     def to_excel(self):
         xlsx_name = os.path.join(self.model_dir, 'model') + '.xlsx'
         m_dict = self.to_df()
@@ -100,8 +105,7 @@ class RbaModel:
 
         with pd.ExcelWriter(xlsx_name) as writer:
             for name, df in m_dict.items():
-                keep_index = False if df.index.name == 'index' else True
-                df.to_excel(writer, sheet_name=name, index=keep_index)
+                df.to_excel(writer, sheet_name=name)
             print(f'model exported to {xlsx_name}')
 
     def validate(self):
@@ -113,8 +117,6 @@ class RbaModel:
                          'aggregates': set(self.parameters.aggregates)}
 
         valid = True
-        components = {'parameters', 'metabolism',
-                      'processes', 'enzymes', 'densities', 'targets'}
         for component in components:
             valid = valid and getattr(self, component).validate(component_ids)
         print(f'model valid status: {valid}')
@@ -140,7 +142,6 @@ class RbaModel:
 
         unused_parameters = parameters.difference(ref_parameters)
         unused_molecules = molecules.difference(ref_molecules)
-
         unused = 0
         if len(unused_parameters) > 0:
             print(f'{len(unused_parameters)} unused parameters:', unused_parameters)
@@ -151,8 +152,5 @@ class RbaModel:
         if unused == 0:
             print('no unused parameters/molecules')
 
-    def from_df(self):
-        pass
-
     def from_excel(self):
         pass
diff --git a/rbaxdf/model/rba_parameters.py b/rbaxdf/model/rba_parameters.py
index 1a6f35e..629fb16 100644
--- a/rbaxdf/model/rba_parameters.py
+++ b/rbaxdf/model/rba_parameters.py
@@ -7,6 +7,8 @@ import os
 import pandas as pd
 from xml.etree.ElementTree import parse, ElementTree, Element, SubElement, indent
 
+from rbaxdf.utils.utils import extract_params
+
 
 class RbaParameters:
 
@@ -26,6 +28,10 @@ class RbaParameters:
         self.functions = RbaFunction.import_xml(root.find('listOfFunctions'))
         self.aggregates = RbaAggregate.import_xml(root.find('listOfAggregates'))
 
+    def from_df(self, df_f, df_a):
+        self.functions = RbaFunction.from_df(df_f)
+        self.aggregates = RbaAggregate.from_df(df_a)
+
     def export_xml(self, model_dir):
 
         file_name = os.path.join(model_dir, 'parameters.xml')
@@ -50,17 +56,12 @@ class RbaParameters:
             value_info = self.aggregates[value].value_info
         return value_info
 
-    def to_df(self, p_type):
-        df = None
-        if p_type == 'functions':
-            df = pd.DataFrame([item.to_dict() for item in self.functions.values()])
-            df.set_index('function', inplace=True)
-        elif p_type == 'aggregates':
-            df = pd.DataFrame([item.to_dict() for item in self.aggregates.values()])
-            df.set_index('aggregate', inplace=True)
-        else:
-            print(f'wrong parameter type: {p_type}')
-        return df
+    def to_df(self):
+        df_f = pd.DataFrame([item.to_dict() for item in self.functions.values()])
+        df_f.set_index('function', inplace=True)
+        df_a = pd.DataFrame([item.to_dict() for item in self.aggregates.values()])
+        df_a.set_index('aggregate', inplace=True)
+        return df_f, df_a
 
     def validate(self, component_ids):
         valid = True
@@ -107,6 +108,18 @@ class RbaFunction:
             data[fid] = rba_function
         return data
 
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for fid, row in df.iterrows():
+            rba_function = RbaFunction(fid)
+            rba_function.type = row['type']
+            rba_function.variable = row['variable']
+            rba_function.parameters = extract_params(row['parameters'])
+            rba_function.set_value_info()
+            data[fid] = rba_function
+        return data
+
     def export_xml(self):
         attribs = {'id': self.id, 'type': self.type, 'variable': self.variable}
         function = Element('function', attribs)
@@ -164,6 +177,17 @@ class RbaAggregate:
             data[aid] = rba_aggregate
         return data
 
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for aid, row in df.iterrows():
+            rba_aggregate = RbaAggregate(aid)
+            rba_aggregate.type = row['type']
+            rba_aggregate.functions = [item.strip() for item in row['functions'].split(',')]
+            rba_aggregate.set_value_info()
+            data[aid] = rba_aggregate
+        return data
+
     def export_xml(self):
         attribs = {'id': self.id, 'type': self.type}
         aggregate = Element('aggregate', attribs)
diff --git a/rbaxdf/model/rba_processes.py b/rbaxdf/model/rba_processes.py
index c193228..83c2f8a 100644
--- a/rbaxdf/model/rba_processes.py
+++ b/rbaxdf/model/rba_processes.py
@@ -9,7 +9,7 @@ import pandas as pd
 from xml.etree.ElementTree import parse, ElementTree, Element, SubElement, indent
 
 from .rba_target_value import RbaTargetValue
-from rbaxdf.utils.et_utils import get_species_refs
+from rbaxdf.utils.utils import get_species_refs_from_xml, get_species_refs_from_str, extract_params
 
 
 class RbaProcesses:
@@ -31,6 +31,10 @@ class RbaProcesses:
         self.processes = RbaProcess.import_xml(root.find('listOfProcesses'))
         self.processing_maps = RbaProcessingMap.import_xml(root.find('listOfProcessingMaps'))
 
+    def from_df(self, df_p, df_pm):
+        self.processes = RbaProcess.from_df(df_p)
+        self.processing_maps = RbaProcessingMap.from_df(df_pm)
+
     def export_xml(self, model_dir):
 
         file_name = os.path.join(model_dir, 'processes.xml')
@@ -48,29 +52,25 @@ class RbaProcesses:
         indent(tree)
         tree.write(file_name)
 
-    def to_df(self, p_type):
-        df = None
-        if p_type == 'processes':
-            df = pd.DataFrame([item.to_dict() for item in self.processes.values()])
-            df.set_index('process', inplace=True)
-        elif p_type == 'processingMaps':
-            data = []
-            for pmid, pmap in self.processing_maps.items():
-                pm_dict = pmap.to_dict()
-                if ((len(pm_dict['constantProcessing'].get('reactants', {})) > 0) or
-                        (len(pm_dict['constantProcessing'].get('products', {})) > 0)):
-                    data.append([pmid, 'constantProcessing', np.nan, pm_dict['constantProcessing']['reactants'],
-                                 pm_dict['constantProcessing']['products']])
-                for component, comp_proc in pm_dict['componentProcessings'].items():
-                    data.append([pmid, component, comp_proc['machineryCost'], comp_proc['reactants'],
-                                 comp_proc['products']])
-
-            df = pd.DataFrame(data, columns=['processingMap', 'component', 'machineryCost',
-                                             'reactants', 'products'])
-            df.set_index('processingMap', inplace=True)
-        else:
-            print(f'wrong parameter type: {p_type}')
-        return df
+    def to_df(self):
+        df_p = pd.DataFrame([item.to_dict() for item in self.processes.values()])
+        df_p.set_index('process', inplace=True)
+        data = []
+        for pmid, pmap in self.processing_maps.items():
+            pm_dict = pmap.to_dict()
+            if ((len(pm_dict['constantProcessing'].get('reactants', {})) > 0) or
+                    (len(pm_dict['constantProcessing'].get('products', {})) > 0)):
+                data.append([pmid, 'constantProcessing', np.nan, pm_dict['constantProcessing']['reactants'],
+                             pm_dict['constantProcessing']['products']])
+            for component, comp_proc in pm_dict['componentProcessings'].items():
+                data.append([pmid, component, comp_proc['machineryCost'], comp_proc['reactants'],
+                             comp_proc['products']])
+
+        df_pm = pd.DataFrame(data, columns=['processingMap', 'component', 'machineryCost',
+                                            'reactants', 'products'])
+        df_pm.set_index('processingMap', inplace=True)
+
+        return df_p, df_pm
 
     def validate(self, component_ids):
         valid = True
@@ -161,9 +161,9 @@ class RbaProcess:
             machinery = process.find('machinery')
             if machinery is not None:
                 composition = machinery.find('machineryComposition')
-                reactants = get_species_refs(composition.find('listOfReactants'))
-                products = get_species_refs(composition.find('listOfProducts'))
-                capacity = RbaTargetValue(machinery.find('capacity'))
+                reactants = get_species_refs_from_xml(composition.find('listOfReactants'))
+                products = get_species_refs_from_xml(composition.find('listOfProducts'))
+                capacity = RbaTargetValue.from_dict(machinery.find('capacity').attrib)
                 rba_process.machinery = {'capacity': capacity, 'reactants': reactants,
                                          'products': products}
             processings = process.find('processings')
@@ -172,18 +172,46 @@ class RbaProcess:
                 if productions is not None:
                     processing = productions.find('processing')
                     processing_map = processing.attrib['processingMap']
-                    p_set = processing.attrib['set']
-                    inputs = processing.find('listOfInputs')
-                    p_inputs = [sref.attrib['species'] for sref in inputs.findall('speciesReference')]
-                    rba_process.productions = {'processingMap': processing_map, 'set': p_set, 'inputs': p_inputs}
+                    set_type = processing.attrib['set']
+                    lo_inputs = processing.find('listOfInputs')
+                    inputs = [sref.attrib['species'] for sref in lo_inputs.findall('speciesReference')]
+                    rba_process.productions = {'processingMap': processing_map, 'set': set_type, 'inputs': inputs}
                 degradations = processings.find('listOfDegradations')
                 if degradations is not None:
                     processing = degradations.find('processing')
                     processing_map = processing.attrib['processingMap']
-                    p_set = processing.attrib['set']
-                    inputs = processing.find('listOfInputs')
-                    p_inputs = [sref.attrib['species'] for sref in inputs.findall('speciesReference')]
-                    rba_process.degradations = {'processingMap': processing_map, 'set': p_set, 'inputs': p_inputs}
+                    set_type = processing.attrib['set']
+                    lo_inputs = processing.find('listOfInputs')
+                    inputs = [sref.attrib['species'] for sref in lo_inputs.findall('speciesReference')]
+                    rba_process.degradations = {'processingMap': processing_map, 'set': set_type, 'inputs': inputs}
+
+            data[pid] = rba_process
+        return data
+
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for pid, row in df.iterrows():
+            rba_process = RbaProcess(pid)
+            rba_process.name = row['name']
+            capacity_dict = extract_params(row['machineryCapacity'])
+            capacity = RbaTargetValue.from_dict(capacity_dict)
+            reactants = get_species_refs_from_str(row['machineryReactants'])
+            products = get_species_refs_from_str(row['machineryProducts'])
+            rba_process.machinery = {'capacity': capacity, 'reactants': reactants,
+                                     'products': products}
+
+            processing_map = row['productionProcessingMap']
+            set_type = row['productionSet']
+            inputs = [item.strip() for item in row['productionInputs'].split(',')]
+            if type(processing_map) is str and len(processing_map) > 0:
+                rba_process.productions = {'processingMap': processing_map, 'set': set_type, 'inputs': inputs}
+
+            processing_map = row['degradationProcessingMap']
+            set_type = row['degradationSet']
+            inputs = [item.strip() for item in row['degradationInputs'].split(',')]
+            if type(processing_map) is str and len(processing_map) > 0:
+                rba_process.degradations = {'processingMap': processing_map, 'set': set_type, 'inputs': inputs}
 
             data[pid] = rba_process
         return data
@@ -274,16 +302,36 @@ class RbaProcessingMap:
 
             const_proc = processing_map.find('constantProcessing')
             if const_proc is not None:
-                rba_pmap.constant_processing['reactants'] = get_species_refs(const_proc.find('listOfReactants'))
-                rba_pmap.constant_processing['products'] = get_species_refs(const_proc.find('listOfProducts'))
+                rba_pmap.constant_processing['reactants'] = get_species_refs_from_xml(
+                    const_proc.find('listOfReactants'))
+                rba_pmap.constant_processing['products'] = get_species_refs_from_xml(const_proc.find('listOfProducts'))
 
             comp_procs = processing_map.find('listOfComponentProcessings')
             if comp_procs is not None:
                 for comp_proc in comp_procs.findall('componentProcessing'):
                     component = comp_proc.attrib['component']
                     cost = float(comp_proc.get('machineryCost', '0'))
-                    reactants = get_species_refs(comp_proc.find('listOfReactants'))
-                    products = get_species_refs(comp_proc.find('listOfProducts'))
+                    reactants = get_species_refs_from_xml(comp_proc.find('listOfReactants'))
+                    products = get_species_refs_from_xml(comp_proc.find('listOfProducts'))
+                    rba_pmap.component_processings[component] = {'cost': cost, 'reactants': reactants,
+                                                                 'products': products}
+            data[pmid] = rba_pmap
+        return data
+
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for pmid, group in df.groupby('processingMap'):
+            rba_pmap = RbaProcessingMap(pmid)
+            for _, row in group.iterrows():
+                component = row['component']
+                reactants = get_species_refs_from_str(row['reactants'])
+                products = get_species_refs_from_str(row['products'])
+                if component == 'constantProcessing':
+                    rba_pmap.constant_processing['reactants'] = reactants
+                    rba_pmap.constant_processing['products'] = products
+                else:
+                    cost = row['machineryCost']
                     rba_pmap.component_processings[component] = {'cost': cost, 'reactants': reactants,
                                                                  'products': products}
             data[pmid] = rba_pmap
diff --git a/rbaxdf/model/rba_target_value.py b/rbaxdf/model/rba_target_value.py
index e5ee386..8b470f1 100644
--- a/rbaxdf/model/rba_target_value.py
+++ b/rbaxdf/model/rba_target_value.py
@@ -6,13 +6,20 @@ Peter Schubert, CCB, HHU Duesseldorf, December 2022
 
 class RbaTargetValue:
 
-    def __init__(self, target):
-        self.lower_bound = target.attrib.get('lowerBound')
-        self.upper_bound = target.attrib.get('upperBound')
-        self.value = target.attrib.get('value')
-        if self.value is not None:
-            self.lower_bound = None
-            self.upper_bound = None
+    def __init__(self):
+        self.lower_bound = None
+        self.upper_bound = None
+        self.value = None
+
+    @staticmethod
+    def from_dict(value_dict):
+        target_value = RbaTargetValue()
+        if 'value' in value_dict:
+            target_value.value = value_dict['value']
+        else:
+            target_value.lower_bound = value_dict.get('lowerBound')
+            target_value.upper_bound = value_dict.get('upperBound')
+        return target_value
 
     def to_dict(self):
         target_values = {}
diff --git a/rbaxdf/model/rba_targets.py b/rbaxdf/model/rba_targets.py
index fadf2e7..4778076 100644
--- a/rbaxdf/model/rba_targets.py
+++ b/rbaxdf/model/rba_targets.py
@@ -7,7 +7,8 @@ import os
 import pandas as pd
 from xml.etree.ElementTree import parse, ElementTree, Element, SubElement, indent
 
-from rbaxdf.utils.et_utils import get_target_species, get_target_reactions
+from rbaxdf.utils.utils import get_target_species_from_xml, get_target_reactions_from_xml, extract_params
+from .rba_target_value import RbaTargetValue
 
 
 class RbaTargets:
@@ -27,6 +28,9 @@ class RbaTargets:
 
         self.target_groups = RbaTargetGroup.import_xml(root.find('listOfTargetGroups'))
 
+    def from_df(self, df):
+        self.target_groups = RbaTargetGroup.from_df(df)
+
     def export_xml(self, model_dir):
         file_name = os.path.join(model_dir, 'targets.xml')
         root = Element('RBATargets')
@@ -98,12 +102,30 @@ class RbaTargetGroup:
         data = {}
         for target_group in target_groups.findall('targetGroup'):
             tgid = target_group.attrib.get('id', '')
-            rba_target = RbaTargetGroup(tgid)
-            rba_target.concentrations = get_target_species(target_group.find('listOfConcentrations'))
-            rba_target.production_fluxes = get_target_species(target_group.find('listOfProductionFluxes'))
-            rba_target.degradation_fluxes = get_target_species(target_group.find('listOfDegradationFluxes'))
-            rba_target.reaction_fluxes = get_target_reactions(target_group.find('listOfReactionFluxes'))
-            data[tgid] = rba_target
+            rba_tg = RbaTargetGroup(tgid)
+            rba_tg.concentrations = get_target_species_from_xml(target_group.find('listOfConcentrations'))
+            rba_tg.production_fluxes = get_target_species_from_xml(target_group.find('listOfProductionFluxes'))
+            rba_tg.degradation_fluxes = get_target_species_from_xml(target_group.find('listOfDegradationFluxes'))
+            rba_tg.reaction_fluxes = get_target_reactions_from_xml(target_group.find('listOfReactionFluxes'))
+            data[tgid] = rba_tg
+        return data
+
+    @staticmethod
+    def from_df(df):
+        data = {}
+        for tgid, group in df.groupby('targetGroup'):
+            rba_tg = RbaTargetGroup(tgid)
+            for _, row in group.iterrows():
+                target_value = extract_params(row['targetValue'])
+                if row['targetType'] == 'concentrations':
+                    rba_tg.concentrations[row['target']] = RbaTargetValue.from_dict(target_value)
+                if row['targetType'] == 'productionFluxes':
+                    rba_tg.production_fluxes[row['target']] = RbaTargetValue.from_dict(target_value)
+                if row['targetType'] == 'degradationFluxes':
+                    rba_tg.degradation_fluxes[row['target']] = RbaTargetValue.from_dict(target_value)
+                if row['targetType'] == 'reactionFluxes':
+                    rba_tg.reaction_fluxes[row['target']] = RbaTargetValue.from_dict(target_value)
+            data[tgid] = rba_tg
         return data
 
     def export_xml(self):
diff --git a/rbaxdf/utils/et_utils.py b/rbaxdf/utils/et_utils.py
deleted file mode 100644
index b364f88..0000000
--- a/rbaxdf/utils/et_utils.py
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-from rbaxdf.model.rba_target_value import RbaTargetValue
-
-
-def get_target_species(ts_parent):
-    data = {}
-    if ts_parent is not None:
-        for target in ts_parent.findall('targetSpecies'):
-            species = target.attrib['species']
-            target_value = RbaTargetValue(target)
-            data[species] = target_value
-    return data
-
-
-def get_target_reactions(tr_parent):
-    data = {}
-    if tr_parent is not None:
-        for target in tr_parent.findall('targetReaction'):
-            reaction = target.attrib['reaction']
-            target_values = RbaTargetValue(target)
-            data[reaction] = target_values
-    return data
-
-
-def get_species_refs(srefs_parent):
-    srefs = {}
-    if srefs_parent is not None:
-        for sref in srefs_parent.findall('speciesReference'):
-            sid = sref.attrib['species']
-            stoic = float(sref.attrib['stoichiometry'])
-            srefs[sid] = stoic
-    return srefs
diff --git a/rbaxdf/utils/utils.py b/rbaxdf/utils/utils.py
new file mode 100644
index 0000000..2c90795
--- /dev/null
+++ b/rbaxdf/utils/utils.py
@@ -0,0 +1,78 @@
+
+
+from rbaxdf.model.rba_target_value import RbaTargetValue
+
+
+def get_target_species_from_xml(ts_parent):
+    data = {}
+    if ts_parent is not None:
+        for target in ts_parent.findall('targetSpecies'):
+            species = target.attrib['species']
+            target_value = RbaTargetValue.from_dict(target.attrib)
+            data[species] = target_value
+    return data
+
+
+def get_target_reactions_from_xml(tr_parent):
+    data = {}
+    if tr_parent is not None:
+        for target in tr_parent.findall('targetReaction'):
+            reaction = target.attrib['reaction']
+            target_values = RbaTargetValue.from_dict(target.attrib)
+            data[reaction] = target_values
+    return data
+
+
+def get_species_refs_from_xml(srefs_parent):
+    srefs = {}
+    if srefs_parent is not None:
+        for sref in srefs_parent.findall('speciesReference'):
+            sid = sref.attrib['species']
+            stoic = float(sref.attrib['stoichiometry'])
+            srefs[sid] = stoic
+    return srefs
+
+
+def get_species_refs_from_str(srefs_str):
+    srefs = {}
+    for sref in srefs_str.split(';'):
+        params = extract_params(sref)
+        if 'species' in params and 'stoic' in params:
+            srefs[params['species']] = float(params['stoic'])
+    return srefs
+
+
+def extract_params(record):
+    """Extract parameters from a record.
+
+    A single record consists of comma separated key-value pairs.
+    Example: 'key1=val1, key2=val2, ...' is converted to
+    {key1: val1, key2: val2, ...}
+
+    :param record: key '=' value pairs separated by ","
+    :type record: str
+    :returns: key-values pairs extracted from record
+    :rtype: dict
+    """
+    params = {}
+    for kv_pair in record_generator(record, sep=','):
+        if '=' in kv_pair:
+            k, v = kv_pair.split('=')
+            params[k.strip()] = v.strip()
+    return params
+
+
+def record_generator(records_str, sep=';'):
+    """Generator to extract individual records from a string of records.
+
+    :param records_str: containing records separated by sep
+    :type records_str: str
+    :param sep: seperator used to separate records
+    :type sep: str (default: ';')
+    :returns: key-values pairs extracted from record
+    :rtype: dict
+    """
+    if type(records_str) == str:
+        for record in records_str.split(sep):
+            if len(record.strip()) > 0:
+                yield record.strip()
-- 
GitLab