Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ python setup.py install

Requires Python >= 3.11.

Need to manually install graphviz: `conda install -c conda-forge graphviz`. If not installed and Seed2LP is launched for a graph visualisation of the network, the tool will raise an error and invite the user to install graphviz.

For below requirements, installing Seed2LP will install all needed packages if not already installed.


Expand Down
4 changes: 3 additions & 1 deletion networks/toys_communities/communities/com1.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
B1
B2
B2
B3
B4
194 changes: 124 additions & 70 deletions seed2lp/__main__.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion seed2lp/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version_info__ = ('2', '0', '0')
__version_info__ = ('2', '1', '0')
__version__ = '.'.join(__version_info__)
2 changes: 1 addition & 1 deletion seed2lp/argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def cli_parser() -> argparse.ArgumentParser:
- guess_check_div: Guess check but also forbids subset of seed as next result in order to reduce intersection of solutions
""",
usage="""
seed2lp community [community_file_text] [sbml_directory] [result directory] \n
seed2lp community [community_file_text] [sbml_directory] [result_directory] \n
"""
)

Expand Down
6 changes: 3 additions & 3 deletions seed2lp/asp/enum-cc.lp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ reaction(R) :- dreaction(R).


% the edges are linking reactants to products of each reaction.
oedge(T,P) :- reactant(T,R) ; product(P,R).
oedge(T,P) :- reactant(T,_,R,_,_,_) ; product(P,_,R,_,_,_).

% the nodes
node(M) :- reactant(M,_).
node(M) :- product(M,_).
node(M) :- reactant(M,_,_,_,_,_).
node(M) :- product(M,_,_,_,_,_).

% #show oedge/2.
% #show node/1.
Expand Down
20 changes: 14 additions & 6 deletions seed2lp/clingo_lpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@
Clingo lpx functions for launching command and extract results.
"""

import subprocess
import subprocess, logging
import json
from resource import getrusage, RUSAGE_CHILDREN
#from resource import getrusage, RUSAGE_CHILDREN
from .utils import repair_json
from . import logger, color
from . import color #logger
try:
from resource import getrusage, RUSAGE_CHILDREN
except ImportError:
RUSAGE_CHILDREN = 0
def getrusage(x):
class ret:
ru_maxrss = 2**20
return ret


def command(files:list, options:list, nb_model:int=0, time_limit:int=0) -> iter:
Expand Down Expand Up @@ -59,15 +67,15 @@ def solve(cmd:list, time_limit:int):
Returns:
proc_output (str), err_output (str), error_code (int), memory (float), is_killed (bool)
"""

logger = logging.getLogger("s2lp")
cmd.append(f'--outf=2')
is_killed=False
try:
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if time_limit:
process.wait(timeout=time_limit+60)
except subprocess.TimeoutExpired:
logger.log.error(f'Timeout: {time_limit/60} min expired')
logger.error(f'Timeout: {time_limit/60} min expired')
process.kill()
process.wait()
is_killed=True
Expand All @@ -78,7 +86,7 @@ def solve(cmd:list, time_limit:int):
if is_killed:
error_code=0
if error_code==1:
logger.log.error(f'Timeout: {time_limit/60} min expired')
logger.error(f'Timeout: {time_limit/60} min expired')
return str(proc_output, 'UTF-8'), err_output, error_code, round(memory,3), is_killed


Expand Down
77 changes: 44 additions & 33 deletions seed2lp/description.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import re
from os import path
from seed2lp.network import Network
from . import flux, logger, color
from . import flux, color # logger,
import warnings
import difflib
import seed2lp.sbml as SBML
Expand All @@ -21,15 +21,15 @@
#defined reactant/4.
#defined product/4.
#defined reaction/1.
link(T,R) :- reactant(T,_,R,_).
link(R,P) :- product(P,_,R,_).
link(T,R) :- reactant(T,_,R,_,_,_).
link(R,P) :- product(P,_,R,_,_,_).
shape(R,rectangle) :- reaction(R).
obj_property(edge,arrowhead,vee).
"""
BISEAU_VIZ_NOREACTION = """
link(M,P) :- product(P,_,R,_) ; reactant(M,_,R,_).
link(P,P) :- product(P,_,R,_) ; not reactant(_,_,R,_).
link(M,M) :- reactant(M,_,R,_), not product(_,_,R,_).
link(M,P) :- product(P,_,R,_,_,_) ; reactant(M,_,R,_,_,_).
link(P,P) :- product(P,_,R,_,_,_) ; not reactant(_,_,R,_,_,_).
link(M,M) :- reactant(M,_,R,_,_,_), not product(_,_,R,_,_,_).
obj_property(edge,arrowhead,vee).
"""

Expand Down Expand Up @@ -102,7 +102,7 @@ def get_details(self):
def details_from_lp(self):
"""Get the network description from lp facts and save it
"""
logger.log.info("Start Getting Details from LP file")
self.logger.info("Start Getting Details from LP file")
reactions_composition_df = pd.DataFrame(columns=['reaction', 'metabolite', 'type_metabolite', 'stoichiometry'])
reaction_df = pd.DataFrame(columns=['reaction', 'low_bound', 'up_bound','is_forward', 'is_reverse', 'is_low_set'])
only_forward=list()
Expand Down Expand Up @@ -251,13 +251,13 @@ def details_from_lp(self):
#print(reaction_without_reactant)
#print("REACTION WITHOUT PRODUCT")
#print(reaction_without_product)
save(self.lp_details , full_details)
self.save(self.lp_details , full_details)


def details_from_cobra(self):
"""Get the network description from sbml file by using cobra and save it
"""
logger.log.info("Start Getting Details from Cobra file")
self.logger.info("Start Getting Details from Cobra file")
warnings.filterwarnings("error")
model = flux.get_model(self.file)
if not self.keep_import_reactions:
Expand All @@ -267,14 +267,14 @@ def details_from_cobra(self):
full_details += f"{reaction}\t[{reaction.lower_bound}, {reaction.upper_bound}]\n"

self.cobra_details = path.join(self.out_dir, f"{self.name}_{self.short_option}_details_from_cobra.txt")
save(self.cobra_details, full_details)
self.save(self.cobra_details, full_details)


def details_diff(self):
"""Compare the Network description from cobra and lp facts
save the diff information into file
"""
logger.log.info("Start checking diff between Cobra and LP Network")
self.logger.info("Start checking diff between Cobra and LP Network")
diff = ""
with open(self.cobra_details) as cobra_details:
cobra_details_text = cobra_details.readlines()
Expand All @@ -292,12 +292,13 @@ def details_diff(self):
lp_details.close()

diff_path = path.join(self.out_dir, f"{self.name}_{self.short_option}_details_diff.txt")
save(diff_path, diff)
self.save(diff_path, diff)

def render_network(self):
"""From lp facts render the network graph with or without reaction
"""
import biseau
check_graphviz()
out_file = path.join(self.out_dir, f"{self.name}_{self.short_option}_visu")

print(f"\n\n{color.cyan_dark}############################################")
Expand Down Expand Up @@ -343,10 +344,10 @@ def rewrite_sbml_file(self):
print(f"############################################\n{color.reset}")

if self.keep_import_reactions:
logger.log.warning("IMPORT REACTION KEPT")
self.logger.warning("IMPORT REACTION KEPT")
else:
logger.log.warning("IMPORT REACTION REMOVED BY DEFAULT")
logger.log.warning("If you want to keep import reaction\nuse option -kir / --keep-import-reactions")
self.logger.warning("IMPORT REACTION REMOVED BY DEFAULT")
self.logger.warning("If you want to keep import reaction\nuse option -kir / --keep-import-reactions")
species=self.name
original_reactions = SBML.get_listOfReactions(self.model[species])

Expand All @@ -366,7 +367,7 @@ def rewrite_sbml_file(self):
if reaction_name in self.deleted_reactions:
self.sbml_remove_reaction(reaction, species)
rm_reac_message+=f"\n\t- {reaction_name}"
logger.log.info(rm_reac_message)
self.logger.info(rm_reac_message)
else:
# Change the reversibility
is_modif_rev = self.sbml_review_reversibilty(reaction_name, reaction)
Expand All @@ -389,11 +390,11 @@ def rewrite_sbml_file(self):

#print and log
if is_modif_rev_log:
logger.log.warning(modif_rev_message)
self.logger.warning(modif_rev_message)
if is_switch_meta_log:
logger.log.warning(switch_meta_message)
self.logger.warning(switch_meta_message)
if is_rm_import_log:
logger.log.warning(rm_import_message)
self.logger.warning(rm_import_message)

# Replace list of parameters because we added new specific parameters for the exchange reactions
self.sbml_review_parameters(species)
Expand All @@ -405,20 +406,30 @@ def rewrite_sbml_file(self):
file_path = path.join(self.out_dir, self.name+".xml")
str_model = self.sbml_first_line+SBML.etree_to_string(self.sbml[species])
print(f"File saved at: {file_path}")
save(file_path, str_model)
self.save(file_path, str_model)

########################################################

def save(self, out_file:str, data):
"""Save file of Network description or graphs

######################## FUNCTIONS ########################
def save(out_file:str, data):
"""Save file of Network description or graphs

Args:
out_file (str): Output file path
data: Graph or Network details data
"""
with open(out_file, 'w') as f:
f.write(data)
f.close()
logger.log.info(f"File saved at: {out_file}")
Args:
out_file (str): Output file path
data: Graph or Network details data
"""
with open(out_file, 'w') as f:
f.write(data)
f.close()
self.logger.info(f"File saved at: {out_file}")


def check_graphviz():
import shutil
if shutil.which("dot") is None:
raise RuntimeError(
f"\n\n{color.red_bright}Graphviz is required and missing from your environment.{color.reset}\n"
f"First install it with:\n"
f" {color.cyan_dark}conda install -c conda-forge graphviz{color.reset}\n"
f"or\n"
f" {color.cyan_dark}sudo apt install graphviz{color.reset}\n"
f"Then retry.\n"
)
9 changes: 6 additions & 3 deletions seed2lp/file.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
from os import path, makedirs, stat, remove
from json import dump, load
from csv import writer, reader
from . import logger
#from . import logger


def existant_path(inpath:str) -> str:
Expand Down Expand Up @@ -29,12 +30,13 @@ def is_valid_dir(dirpath):
Returns:
bool: True if dir exists, False otherwise
"""
logger = logging.getLogger("s2lp")
if not path.isdir(dirpath):
try:
makedirs(dirpath)
return dirpath
except OSError as e:
logger.log.error(e)
logger.error(e)
return None
else:
return dirpath
Expand Down Expand Up @@ -64,6 +66,7 @@ def save(filename:str, directory:str, results, type:str, is_result_temp=False):
type (str): Type of output fils (json or tsv or txt)
"""

logger = logging.getLogger("s2lp")
out_file_path = path.join(directory,filename)
try:
match type:
Expand Down Expand Up @@ -91,7 +94,7 @@ def save(filename:str, directory:str, results, type:str, is_result_temp=False):
with open(out_file_path, "w") as f:
f.write("\n".join(results))
except Exception as e:
logger.log.error(f"while saving file: {e}")
logger.error(f"while saving file: {e}")



Expand Down
Loading