seed2lp 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- seed2lp/__init__.py +12 -0
- seed2lp/__main__.py +837 -0
- seed2lp/_version.py +2 -0
- seed2lp/argument.py +717 -0
- seed2lp/asp/atom_for_transfers.lp +7 -0
- seed2lp/asp/community_heuristic.lp +3 -0
- seed2lp/asp/community_search.lp +14 -0
- seed2lp/asp/constraints_targets.lp +15 -0
- seed2lp/asp/definition_atoms.lp +87 -0
- seed2lp/asp/enum-cc.lp +50 -0
- seed2lp/asp/flux.lp +70 -0
- seed2lp/asp/limit_transfers.lp +9 -0
- seed2lp/asp/maximize_flux.lp +2 -0
- seed2lp/asp/maximize_produced_target.lp +7 -0
- seed2lp/asp/minimize.lp +8 -0
- seed2lp/asp/seed-solving.lp +116 -0
- seed2lp/asp/seed_external.lp +1 -0
- seed2lp/asp/show_seeds.lp +2 -0
- seed2lp/asp/show_tranfers.lp +1 -0
- seed2lp/asp/test.lp +61 -0
- seed2lp/clingo_lpx.py +236 -0
- seed2lp/color.py +34 -0
- seed2lp/config.yaml +56 -0
- seed2lp/description.py +424 -0
- seed2lp/file.py +151 -0
- seed2lp/flux.py +365 -0
- seed2lp/linear.py +431 -0
- seed2lp/log_conf.yaml +25 -0
- seed2lp/logger.py +112 -0
- seed2lp/metabolite.py +46 -0
- seed2lp/network.py +1921 -0
- seed2lp/reaction.py +207 -0
- seed2lp/reasoning.py +459 -0
- seed2lp/reasoningcom.py +753 -0
- seed2lp/reasoninghybrid.py +791 -0
- seed2lp/resmod.py +74 -0
- seed2lp/sbml.py +307 -0
- seed2lp/scope.py +124 -0
- seed2lp/solver.py +333 -0
- seed2lp/temp_flux_com.py +74 -0
- seed2lp/utils.py +237 -0
- seed2lp-2.0.0.dist-info/METADATA +404 -0
- seed2lp-2.0.0.dist-info/RECORD +53 -0
- seed2lp-2.0.0.dist-info/WHEEL +5 -0
- seed2lp-2.0.0.dist-info/entry_points.txt +2 -0
- seed2lp-2.0.0.dist-info/licenses/LICENCE.txt +145 -0
- seed2lp-2.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/fba.py +147 -0
- tests/full_network.py +166 -0
- tests/normalization.py +188 -0
- tests/target.py +286 -0
- tests/utils.py +181 -0
seed2lp/description.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# Object Description, herit from Network, added properties:
|
|
2
|
+
# - file (str): Path of input network file (sbml)
|
|
3
|
+
# - out_dir (str): Output directory
|
|
4
|
+
# - details (bool): Reaction Details performed if True.
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import re
|
|
8
|
+
from os import path
|
|
9
|
+
from seed2lp.network import Network
|
|
10
|
+
from . import flux, logger, color
|
|
11
|
+
import warnings
|
|
12
|
+
import difflib
|
|
13
|
+
import seed2lp.sbml as SBML
|
|
14
|
+
import copy
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
SRC_DIR = path.dirname(path.abspath(__file__))
|
|
18
|
+
ASP_SRC_ENUM_CC = path.join(SRC_DIR, 'asp/enum-cc.lp')
|
|
19
|
+
|
|
20
|
+
BISEAU_VIZ = """
|
|
21
|
+
#defined reactant/4.
|
|
22
|
+
#defined product/4.
|
|
23
|
+
#defined reaction/1.
|
|
24
|
+
link(T,R) :- reactant(T,_,R,_).
|
|
25
|
+
link(R,P) :- product(P,_,R,_).
|
|
26
|
+
shape(R,rectangle) :- reaction(R).
|
|
27
|
+
obj_property(edge,arrowhead,vee).
|
|
28
|
+
"""
|
|
29
|
+
BISEAU_VIZ_NOREACTION = """
|
|
30
|
+
link(M,P) :- product(P,_,R,_) ; reactant(M,_,R,_).
|
|
31
|
+
link(P,P) :- product(P,_,R,_) ; not reactant(_,_,R,_).
|
|
32
|
+
link(M,M) :- reactant(M,_,R,_), not product(_,_,R,_).
|
|
33
|
+
obj_property(edge,arrowhead,vee).
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Description(Network):
|
|
39
|
+
def __init__(self, file:str, keep_import_reactions:bool, out_dir:str,
|
|
40
|
+
details:bool=False, visu:bool=False, visu_no_reaction:bool=False,
|
|
41
|
+
write_file:bool=False):
|
|
42
|
+
"""Initialize Object Description, herit from Network
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
file (str): Path of input network file (sbml)
|
|
46
|
+
keep_import_reactions (bool): Import reactions are not removed
|
|
47
|
+
out_dir (str): Output directory
|
|
48
|
+
details (bool, optional): Reaction Details performed if True. Defaults to False.
|
|
49
|
+
visu (bool, optional): Graph of Network performed if True. Defaults to False.
|
|
50
|
+
visu_no_reaction (bool, optional): Graph of Network without the reaction performed if True. Defaults to False.
|
|
51
|
+
write_file (bool, optional): Write corrected network into SBML file if True. Defaults to False.
|
|
52
|
+
"""
|
|
53
|
+
super().__init__(file, keep_import_reactions=keep_import_reactions, write_sbml=write_file)
|
|
54
|
+
self.out_dir = out_dir
|
|
55
|
+
self.details = details
|
|
56
|
+
self.visu = visu
|
|
57
|
+
self.visu_no_reaction = visu_no_reaction
|
|
58
|
+
self.write_file = write_file
|
|
59
|
+
self.convert_to_facts()
|
|
60
|
+
if self.keep_import_reactions:
|
|
61
|
+
self.short_option="import_rxn"
|
|
62
|
+
else:
|
|
63
|
+
self.short_option="rm_rxn"
|
|
64
|
+
self.cobra_details=""
|
|
65
|
+
self.lp_details=""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
######################## METHODS ########################
|
|
69
|
+
def get_details(self):
|
|
70
|
+
"""Reconstruct reaction details from asp facts
|
|
71
|
+
Describe the reaction formula and boundaries
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
print(f"\n\n{color.cyan_dark}############################################")
|
|
75
|
+
print("############################################")
|
|
76
|
+
print(" DETAILS ")
|
|
77
|
+
print("############################################")
|
|
78
|
+
print(f"############################################\n{color.reset}")
|
|
79
|
+
|
|
80
|
+
print(f"\n{color.purple}____________________________________________{color.reset}")
|
|
81
|
+
print("\n LP FACTS ")
|
|
82
|
+
print(f"{color.purple}____________________________________________\n{color.reset}")
|
|
83
|
+
print("Getting details from lp facts ...\n")
|
|
84
|
+
self.details_from_lp()
|
|
85
|
+
print(" ---> done\n")
|
|
86
|
+
|
|
87
|
+
print(f"\n{color.purple}____________________________________________{color.reset}")
|
|
88
|
+
print("\n COBRA ")
|
|
89
|
+
print(f"{color.purple}____________________________________________\n{color.reset}")
|
|
90
|
+
print("Getting details from cobra ...\n")
|
|
91
|
+
self.details_from_cobra()
|
|
92
|
+
print(" ---> done\n")
|
|
93
|
+
|
|
94
|
+
print(f"\n{color.purple}____________________________________________{color.reset}")
|
|
95
|
+
print("\n DIFF ")
|
|
96
|
+
print(f"{color.purple}____________________________________________\n{color.reset}")
|
|
97
|
+
print("Searching diff ...\n")
|
|
98
|
+
self.details_diff()
|
|
99
|
+
print(" ---> done\n")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def details_from_lp(self):
|
|
103
|
+
"""Get the network description from lp facts and save it
|
|
104
|
+
"""
|
|
105
|
+
logger.log.info("Start Getting Details from LP file")
|
|
106
|
+
reactions_composition_df = pd.DataFrame(columns=['reaction', 'metabolite', 'type_metabolite', 'stoichiometry'])
|
|
107
|
+
reaction_df = pd.DataFrame(columns=['reaction', 'low_bound', 'up_bound','is_forward', 'is_reverse', 'is_low_set'])
|
|
108
|
+
only_forward=list()
|
|
109
|
+
full_details=""
|
|
110
|
+
lis_obj=list()
|
|
111
|
+
|
|
112
|
+
lines=re.split("\n", self.facts)
|
|
113
|
+
# Getting the network and the reaction direction
|
|
114
|
+
for line in lines:
|
|
115
|
+
if re.search("reaction",line):
|
|
116
|
+
reaction = re.split("\"", line)[1]
|
|
117
|
+
if "rev_" in reaction:
|
|
118
|
+
reaction = re.split("rev_R_", reaction)[1]
|
|
119
|
+
df=pd.DataFrame(data=[[reaction, 0.0, 0.0, False, True, False]],columns=['reaction', 'low_bound', 'up_bound','is_forward', 'is_reverse', 'is_low_set'])
|
|
120
|
+
reaction_df=pd.concat([reaction_df,df], ignore_index = True)
|
|
121
|
+
else:
|
|
122
|
+
reaction = re.split("R_", reaction)[1]
|
|
123
|
+
try:
|
|
124
|
+
index=reaction_df[reaction_df["reaction"]==reaction].index[0]
|
|
125
|
+
reaction_df.at[index,'is_forward']=True
|
|
126
|
+
except Exception:
|
|
127
|
+
only_forward.append(f"R_{reaction}")
|
|
128
|
+
df=pd.DataFrame(data=[[reaction, 0.0, 0.0, True, False, False]],columns=['reaction', 'low_bound', 'up_bound','is_forward', 'is_reverse', 'is_low_set'])
|
|
129
|
+
reaction_df=pd.concat([reaction_df,df], ignore_index = True)
|
|
130
|
+
elif re.search("bounds",line):
|
|
131
|
+
split_line = re.split(",", line)
|
|
132
|
+
reaction=re.split("\"", split_line[0])[1]
|
|
133
|
+
lower=re.split("\"", split_line[1])[1]
|
|
134
|
+
lower=float(lower)
|
|
135
|
+
upper=re.split("\"", split_line[2])[1]
|
|
136
|
+
upper=float(upper)
|
|
137
|
+
if "rev_" in reaction:
|
|
138
|
+
reaction = re.split("rev_R_", reaction)[1]
|
|
139
|
+
try:
|
|
140
|
+
index=reaction_df[reaction_df["reaction"]==reaction].index[0]
|
|
141
|
+
reaction_df.at[index,'low_bound'] = -upper
|
|
142
|
+
reaction_df.at[index,'is_low_set'] = True
|
|
143
|
+
except Exception:
|
|
144
|
+
df=pd.DataFrame(data=[[reaction, -upper, 0.0, False, True, True]],columns=['reaction', 'low_bound', 'up_bound','is_forward', 'is_reverse', 'is_low_set'])
|
|
145
|
+
reaction_df=pd.concat([reaction_df,df], ignore_index = True)
|
|
146
|
+
reaction_df.at[index,'is_low_set'] = True
|
|
147
|
+
else:
|
|
148
|
+
reaction = re.split("R_", reaction)[1]
|
|
149
|
+
try:
|
|
150
|
+
index=reaction_df[reaction_df["reaction"]==reaction].index[0]
|
|
151
|
+
reaction_df.at[index,'up_bound'] = upper
|
|
152
|
+
if not reaction_df.at[index,'is_low_set']:
|
|
153
|
+
reaction_df.at[index,'low_bound'] = lower
|
|
154
|
+
reaction_df.at[index,'is_low_set'] = True
|
|
155
|
+
except Exception:
|
|
156
|
+
only_forward.append(f"R_{reaction}")
|
|
157
|
+
df=pd.DataFrame(data=[[reaction, lower, upper, True, False, True]],columns=['reaction', 'low_bound', 'up_bound','is_forward', 'is_reverse', 'is_low_set'])
|
|
158
|
+
reaction_df=pd.concat([reaction_df,df], ignore_index = True)
|
|
159
|
+
elif re.search("reactant",line):
|
|
160
|
+
split_line = re.split(",", line)
|
|
161
|
+
reaction=re.split("\"", split_line[2])[1]
|
|
162
|
+
metabolite = re.split("\"", split_line[0])[1]
|
|
163
|
+
stoichiometry=round(float(re.split("\"", split_line[1])[1]),6)
|
|
164
|
+
if "rev_" in reaction:
|
|
165
|
+
reaction = re.split("rev_R_", reaction)[1]
|
|
166
|
+
df=pd.DataFrame(data=[[reaction, metabolite, "product", stoichiometry]],
|
|
167
|
+
columns=['reaction', 'metabolite', 'type_metabolite', 'stoichiometry'])
|
|
168
|
+
reactions_composition_df=pd.concat([reactions_composition_df,df], ignore_index = True)
|
|
169
|
+
elif reaction in only_forward:
|
|
170
|
+
reaction = re.split("R_", reaction)[1]
|
|
171
|
+
df=pd.DataFrame(data=[[reaction, metabolite, "reactant", stoichiometry]],
|
|
172
|
+
columns=['reaction', 'metabolite', 'type_metabolite', 'stoichiometry'])
|
|
173
|
+
reactions_composition_df=pd.concat([reactions_composition_df,df], ignore_index = True)
|
|
174
|
+
elif re.search("product",line):
|
|
175
|
+
split_line = re.split(",", line)
|
|
176
|
+
reaction=re.split("\"", split_line[2])[1]
|
|
177
|
+
metabolite = re.split("\"", split_line[0])[1]
|
|
178
|
+
stoichiometry=round(float(re.split("\"", split_line[1])[1]),6)
|
|
179
|
+
if "rev_" in reaction:
|
|
180
|
+
reaction = re.split("rev_R_", reaction)[1]
|
|
181
|
+
df=pd.DataFrame(data=[[reaction, metabolite, "reactant", stoichiometry]],
|
|
182
|
+
columns=['reaction', 'metabolite', 'type_metabolite', 'stoichiometry'])
|
|
183
|
+
reactions_composition_df=pd.concat([reactions_composition_df,df], ignore_index = True)
|
|
184
|
+
elif reaction in only_forward:
|
|
185
|
+
reaction = re.split("R_", reaction)[1]
|
|
186
|
+
df=pd.DataFrame(data=[[reaction, metabolite, "product", stoichiometry]],
|
|
187
|
+
columns=['reaction', 'metabolite', 'type_metabolite', 'stoichiometry'])
|
|
188
|
+
reactions_composition_df=pd.concat([reactions_composition_df,df], ignore_index = True)
|
|
189
|
+
reaction_df.sort_values(by=['reaction'])
|
|
190
|
+
reactions_composition_df.sort_values(by=['reaction', 'metabolite'])
|
|
191
|
+
self.objectives = lis_obj
|
|
192
|
+
|
|
193
|
+
#reaction_without_reactant=[]
|
|
194
|
+
#reaction_without_product=[]
|
|
195
|
+
# Writting the network description
|
|
196
|
+
for _,row in reaction_df.iterrows():
|
|
197
|
+
reaction=row[0]
|
|
198
|
+
lower=row[1]
|
|
199
|
+
upper=row[2]
|
|
200
|
+
forward=row[3]
|
|
201
|
+
reverse=row[4]
|
|
202
|
+
symbol=""
|
|
203
|
+
compo_line=f'{reaction}:'
|
|
204
|
+
match ([forward, reverse]):
|
|
205
|
+
case [True, False]:
|
|
206
|
+
symbol=" -->"
|
|
207
|
+
if upper == 0:
|
|
208
|
+
symbol=" <--"
|
|
209
|
+
case [False, True]:
|
|
210
|
+
symbol=" <--"
|
|
211
|
+
if lower == 0:
|
|
212
|
+
symbol=" -->"
|
|
213
|
+
case [True, True]:
|
|
214
|
+
symbol=" <=>"
|
|
215
|
+
sub_reaction=reactions_composition_df[reactions_composition_df["reaction"]==reaction]
|
|
216
|
+
sub_reactant=sub_reaction[sub_reaction["type_metabolite"]=="reactant"]
|
|
217
|
+
sub_product=sub_reaction[sub_reaction["type_metabolite"]=="product"]
|
|
218
|
+
it=0
|
|
219
|
+
if sub_reactant.empty:
|
|
220
|
+
#reaction_without_reactant.append(reaction)
|
|
221
|
+
compo_line += " "
|
|
222
|
+
else:
|
|
223
|
+
for r_metabolite in sub_reactant.iterrows():
|
|
224
|
+
it += 1
|
|
225
|
+
if it!=1:
|
|
226
|
+
compo_line += " +"
|
|
227
|
+
stoic=""
|
|
228
|
+
if r_metabolite[1]["stoichiometry"] != 1.0:
|
|
229
|
+
stoic=f' {r_metabolite[1]["stoichiometry"]}'
|
|
230
|
+
compo_line += f'{stoic} {re.sub("^M_", "", r_metabolite[1]["metabolite"])}'
|
|
231
|
+
compo_line += symbol
|
|
232
|
+
it=0
|
|
233
|
+
if sub_product.empty:
|
|
234
|
+
#reaction_without_product.append(reaction)
|
|
235
|
+
compo_line += " "
|
|
236
|
+
else:
|
|
237
|
+
for p_metabolite in sub_product.iterrows():
|
|
238
|
+
it += 1
|
|
239
|
+
if it!=1:
|
|
240
|
+
compo_line += " +"
|
|
241
|
+
stoic=""
|
|
242
|
+
if p_metabolite[1]["stoichiometry"] != 1.0:
|
|
243
|
+
stoic=f' {p_metabolite[1]["stoichiometry"]}'
|
|
244
|
+
compo_line += f'{stoic} {re.sub("^M_", "", p_metabolite[1]["metabolite"])}'
|
|
245
|
+
compo_line += f'\t[{lower}, {upper}]'
|
|
246
|
+
#print(compo_line)
|
|
247
|
+
full_details += f'{compo_line}\n'
|
|
248
|
+
|
|
249
|
+
self.lp_details = path.join(self.out_dir, f"{self.name}_{self.short_option}_details_from_lp.txt")
|
|
250
|
+
#print("REACTION WITHOUT REACTANT")
|
|
251
|
+
#print(reaction_without_reactant)
|
|
252
|
+
#print("REACTION WITHOUT PRODUCT")
|
|
253
|
+
#print(reaction_without_product)
|
|
254
|
+
save(self.lp_details , full_details)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def details_from_cobra(self):
|
|
258
|
+
"""Get the network description from sbml file by using cobra and save it
|
|
259
|
+
"""
|
|
260
|
+
logger.log.info("Start Getting Details from Cobra file")
|
|
261
|
+
warnings.filterwarnings("error")
|
|
262
|
+
model = flux.get_model(self.file)
|
|
263
|
+
if not self.keep_import_reactions:
|
|
264
|
+
flux.stop_flux(model)
|
|
265
|
+
full_details=""
|
|
266
|
+
for reaction in model.reactions:
|
|
267
|
+
full_details += f"{reaction}\t[{reaction.lower_bound}, {reaction.upper_bound}]\n"
|
|
268
|
+
|
|
269
|
+
self.cobra_details = path.join(self.out_dir, f"{self.name}_{self.short_option}_details_from_cobra.txt")
|
|
270
|
+
save(self.cobra_details, full_details)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def details_diff(self):
|
|
274
|
+
"""Compare the Network description from cobra and lp facts
|
|
275
|
+
save the diff information into file
|
|
276
|
+
"""
|
|
277
|
+
logger.log.info("Start checking diff between Cobra and LP Network")
|
|
278
|
+
diff = ""
|
|
279
|
+
with open(self.cobra_details) as cobra_details:
|
|
280
|
+
cobra_details_text = cobra_details.readlines()
|
|
281
|
+
|
|
282
|
+
with open(self.lp_details) as lp_details:
|
|
283
|
+
lp_details_text = lp_details.readlines()
|
|
284
|
+
|
|
285
|
+
# Find the diff:
|
|
286
|
+
for line in difflib.unified_diff(
|
|
287
|
+
cobra_details_text, lp_details_text, fromfile=self.cobra_details,
|
|
288
|
+
tofile=self.lp_details, lineterm=''):
|
|
289
|
+
diff += f'{line}\n'
|
|
290
|
+
|
|
291
|
+
cobra_details.close()
|
|
292
|
+
lp_details.close()
|
|
293
|
+
|
|
294
|
+
diff_path = path.join(self.out_dir, f"{self.name}_{self.short_option}_details_diff.txt")
|
|
295
|
+
save(diff_path, diff)
|
|
296
|
+
|
|
297
|
+
def render_network(self):
|
|
298
|
+
"""From lp facts render the network graph with or without reaction
|
|
299
|
+
"""
|
|
300
|
+
import biseau
|
|
301
|
+
out_file = path.join(self.out_dir, f"{self.name}_{self.short_option}_visu")
|
|
302
|
+
|
|
303
|
+
print(f"\n\n{color.cyan_dark}############################################")
|
|
304
|
+
print("############################################")
|
|
305
|
+
print(" GRAPH ")
|
|
306
|
+
print("############################################")
|
|
307
|
+
print(f"############################################\n{color.reset}")
|
|
308
|
+
if self.visu:
|
|
309
|
+
print(f"\n{color.purple}____________________________________________{color.reset}")
|
|
310
|
+
print(f"\n GRAPH WITH REACTIONS ")
|
|
311
|
+
print(f"{color.purple}____________________________________________{color.reset}\n")
|
|
312
|
+
print("Generating graph ...\n")
|
|
313
|
+
viz = BISEAU_VIZ
|
|
314
|
+
out_file_visu = f"{out_file}.png"
|
|
315
|
+
try:
|
|
316
|
+
img_visu = biseau.compile_to_single_image(self.facts + viz, outfile=out_file_visu)
|
|
317
|
+
img_visu.close()
|
|
318
|
+
print(f'-> Input graph rendered in {out_file_visu}\n')
|
|
319
|
+
except KeyboardInterrupt:
|
|
320
|
+
print(f'-> Aborted! file :{out_file_visu}\n')
|
|
321
|
+
if self.visu_no_reaction:
|
|
322
|
+
print(f"\n{color.purple}____________________________________________{color.reset}")
|
|
323
|
+
print("\n GRAPH WITHOUT REACTIONS ")
|
|
324
|
+
print(f"{color.purple}____________________________________________\n{color.reset}")
|
|
325
|
+
print("Generating graph ...\n")
|
|
326
|
+
viz = BISEAU_VIZ_NOREACTION
|
|
327
|
+
out_file_visu_no_reaction = f"{out_file}_no_reactions.png"
|
|
328
|
+
try:
|
|
329
|
+
img_visu_no_reaction = biseau.compile_to_single_image(self.facts + viz, outfile=out_file_visu_no_reaction)
|
|
330
|
+
img_visu_no_reaction.close()
|
|
331
|
+
print(f'-> Input graph rendered in {out_file_visu_no_reaction}\n')
|
|
332
|
+
except KeyboardInterrupt:
|
|
333
|
+
print(f'-> Aborted! file :{out_file_visu_no_reaction}\n')
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def rewrite_sbml_file(self):
|
|
337
|
+
"""SBML file is written from corrected network
|
|
338
|
+
"""
|
|
339
|
+
print(f"\n\n{color.cyan_dark}############################################")
|
|
340
|
+
print("############################################")
|
|
341
|
+
print(" WRITING SBML FILE ")
|
|
342
|
+
print("############################################")
|
|
343
|
+
print(f"############################################\n{color.reset}")
|
|
344
|
+
|
|
345
|
+
if self.keep_import_reactions:
|
|
346
|
+
logger.log.warning("IMPORT REACTION KEPT")
|
|
347
|
+
else:
|
|
348
|
+
logger.log.warning("IMPORT REACTION REMOVED BY DEFAULT")
|
|
349
|
+
logger.log.warning("If you want to keep import reaction\nuse option -kir / --keep-import-reactions")
|
|
350
|
+
species=self.name
|
|
351
|
+
original_reactions = SBML.get_listOfReactions(self.model[species])
|
|
352
|
+
|
|
353
|
+
# Need Two loop to remove first then two do modification
|
|
354
|
+
# If removing while looping, then the loop misses some reactions
|
|
355
|
+
# because the index of the reaction changes
|
|
356
|
+
rm_reac_message = "Reaction removed:"
|
|
357
|
+
modif_rev_message = "Reaction with tag reversible modified:"
|
|
358
|
+
switch_meta_message = "Reaction with Reactants and Products exchanged:"
|
|
359
|
+
rm_import_message = "Import reaction removed:"
|
|
360
|
+
|
|
361
|
+
is_modif_rev_log = False
|
|
362
|
+
is_switch_meta_log = False
|
|
363
|
+
is_rm_import_log = False
|
|
364
|
+
for reaction in original_reactions:
|
|
365
|
+
reaction_name = reaction.attrib.get("id")
|
|
366
|
+
if reaction_name in self.deleted_reactions:
|
|
367
|
+
self.sbml_remove_reaction(reaction, species)
|
|
368
|
+
rm_reac_message+=f"\n\t- {reaction_name}"
|
|
369
|
+
logger.log.info(rm_reac_message)
|
|
370
|
+
else:
|
|
371
|
+
# Change the reversibility
|
|
372
|
+
is_modif_rev = self.sbml_review_reversibilty(reaction_name, reaction)
|
|
373
|
+
is_modif_rev_log = is_modif_rev_log or is_modif_rev
|
|
374
|
+
if is_modif_rev:
|
|
375
|
+
modif_rev_message+=f"\n\t- {reaction_name}"
|
|
376
|
+
|
|
377
|
+
# switch reactants and products
|
|
378
|
+
is_switch_meta = self.sbml_switch_meta(reaction_name, reaction, species)
|
|
379
|
+
is_switch_meta_log = is_switch_meta_log or is_switch_meta
|
|
380
|
+
if is_switch_meta:
|
|
381
|
+
switch_meta_message+=f"\n\t- {reaction_name}"
|
|
382
|
+
|
|
383
|
+
# remove import reactions
|
|
384
|
+
if not self.keep_import_reactions:
|
|
385
|
+
is_rm_import = self.sbml_remove_import(reaction_name, reaction, species)
|
|
386
|
+
is_rm_import_log = is_rm_import_log or is_rm_import
|
|
387
|
+
if is_rm_import:
|
|
388
|
+
rm_import_message+=f"\n\t- {reaction_name}"
|
|
389
|
+
|
|
390
|
+
#print and log
|
|
391
|
+
if is_modif_rev_log:
|
|
392
|
+
logger.log.warning(modif_rev_message)
|
|
393
|
+
if is_switch_meta_log:
|
|
394
|
+
logger.log.warning(switch_meta_message)
|
|
395
|
+
if is_rm_import_log:
|
|
396
|
+
logger.log.warning(rm_import_message)
|
|
397
|
+
|
|
398
|
+
# Replace list of parameters because we added new specific parameters for the exchange reactions
|
|
399
|
+
self.sbml_review_parameters(species)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def_ns=self.default_namespace.split("=")
|
|
403
|
+
self.sbml[species].set(def_ns[0], def_ns[1].replace('"',''))
|
|
404
|
+
|
|
405
|
+
file_path = path.join(self.out_dir, self.name+".xml")
|
|
406
|
+
str_model = self.sbml_first_line+SBML.etree_to_string(self.sbml[species])
|
|
407
|
+
print(f"File saved at: {file_path}")
|
|
408
|
+
save(file_path, str_model)
|
|
409
|
+
|
|
410
|
+
########################################################
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
######################## FUNCTIONS ########################
|
|
414
|
+
def save(out_file:str, data):
|
|
415
|
+
"""Save file of Network description or graphs
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
out_file (str): Output file path
|
|
419
|
+
data: Graph or Network details data
|
|
420
|
+
"""
|
|
421
|
+
with open(out_file, 'w') as f:
|
|
422
|
+
f.write(data)
|
|
423
|
+
f.close()
|
|
424
|
+
logger.log.info(f"File saved at: {out_file}")
|
seed2lp/file.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from os import path, makedirs, stat, remove
|
|
2
|
+
from json import dump, load
|
|
3
|
+
from csv import writer, reader
|
|
4
|
+
from . import logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def existant_path(inpath:str) -> str:
|
|
8
|
+
"""Argparse type, raising an error if given file does not exists
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
inpath (str): Network input path
|
|
12
|
+
|
|
13
|
+
Raises:
|
|
14
|
+
: Error if the file doesn't exist
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
str: Network input path
|
|
18
|
+
"""
|
|
19
|
+
if not path.exists(inpath):
|
|
20
|
+
raise FileNotFoundError("File {} doesn't exists".format(inpath))
|
|
21
|
+
return inpath
|
|
22
|
+
|
|
23
|
+
def is_valid_dir(dirpath):
|
|
24
|
+
"""Return True if directory exists or can be created (then create it)
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
dirpath (str): path of directory
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
bool: True if dir exists, False otherwise
|
|
31
|
+
"""
|
|
32
|
+
if not path.isdir(dirpath):
|
|
33
|
+
try:
|
|
34
|
+
makedirs(dirpath)
|
|
35
|
+
return dirpath
|
|
36
|
+
except OSError as e:
|
|
37
|
+
logger.log.error(e)
|
|
38
|
+
return None
|
|
39
|
+
else:
|
|
40
|
+
return dirpath
|
|
41
|
+
|
|
42
|
+
def existing_file(filepath):
|
|
43
|
+
"""Return True if file exists
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
filepath (str): path of file
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
bool: True if dir exists, False otherwise
|
|
50
|
+
"""
|
|
51
|
+
if not path.isfile(filepath):
|
|
52
|
+
return False
|
|
53
|
+
else:
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def save(filename:str, directory:str, results, type:str, is_result_temp=False):
|
|
58
|
+
"""Save data into file dependinf on type (json / tsv)
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
filename (str): Filename of saved file
|
|
62
|
+
directory (str): Output directory where to save file
|
|
63
|
+
results: Results data in dictionnary (json) or datatrame (tsv)
|
|
64
|
+
type (str): Type of output fils (json or tsv or txt)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
out_file_path = path.join(directory,filename)
|
|
68
|
+
try:
|
|
69
|
+
match type:
|
|
70
|
+
case 'json':
|
|
71
|
+
out_file_path += '.json'
|
|
72
|
+
with open(out_file_path, 'w') as f:
|
|
73
|
+
dump(results, f, indent="\t")
|
|
74
|
+
f.close()
|
|
75
|
+
case 'tsv':
|
|
76
|
+
if not ".tsv" in out_file_path:
|
|
77
|
+
out_file_path += '.tsv'
|
|
78
|
+
# List is given an we want to append data at the end
|
|
79
|
+
if is_result_temp:
|
|
80
|
+
with open(out_file_path, 'a') as f:
|
|
81
|
+
tsv_output = writer(f, delimiter='\t')
|
|
82
|
+
tsv_output.writerow(results)
|
|
83
|
+
# Dataframe is given, and all results are written at once
|
|
84
|
+
else:
|
|
85
|
+
with open(out_file_path, 'w') as f:
|
|
86
|
+
results.to_csv(out_file_path, sep="\t")
|
|
87
|
+
f.close()
|
|
88
|
+
case 'txt':
|
|
89
|
+
if not ".txt" in out_file_path:
|
|
90
|
+
out_file_path += '.txt'
|
|
91
|
+
with open(out_file_path, "w") as f:
|
|
92
|
+
f.write("\n".join(results))
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.log.error(f"while saving file: {e}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def file_is_empty(file_path:str):
|
|
99
|
+
"""Check if the file is empty
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
file_path (str): Path of file to check
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
bool: True if the file is empty
|
|
106
|
+
"""
|
|
107
|
+
return stat(file_path).st_size==0
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def write_instance_file(instance_file:str, facts:str):
|
|
111
|
+
"""Write and Save instance file
|
|
112
|
+
"""
|
|
113
|
+
with open(instance_file, 'w') as f:
|
|
114
|
+
f.write(facts)
|
|
115
|
+
f.close()
|
|
116
|
+
|
|
117
|
+
def delete(filepath:str):
|
|
118
|
+
"""Delete a file
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
filepath (str): Path of File to delete
|
|
122
|
+
"""
|
|
123
|
+
if path.exists(filepath):
|
|
124
|
+
remove(filepath)
|
|
125
|
+
|
|
126
|
+
def load_json(filepath:str):
|
|
127
|
+
"""Load a json file into a variable
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
filepath (str): Path of json file to load
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
result (dict): Data formated into a dictionnary
|
|
134
|
+
"""
|
|
135
|
+
file = open(filepath)
|
|
136
|
+
result = load(file)
|
|
137
|
+
file.close()
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
def load_tsv(filepath:str):
|
|
141
|
+
"""Load a tsv file into a variable
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
filepath (str): Path of json file to load
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
result (list): Data formated into a list
|
|
148
|
+
"""
|
|
149
|
+
file = open(filepath)
|
|
150
|
+
result = reader(file, delimiter='\t')
|
|
151
|
+
return list(result)
|