Commit da319595 authored by VIGNET Pierre's avatar VIGNET Pierre
Browse files

Merge branch 'pypi_packaging'

parents 01d3ef37 fa6a6c27
......@@ -31,6 +31,16 @@ You can install these dependencies with the following command:
sudo apt-get install python-gtksourceview2 python2.7-dev libxml2-dev libxslt1-dev \
libxslt1-dev libgraphviz-dev pkg-config python-glade2 python-gtk2 python-tk
On some old systems you may have to install also `python-pip`.
When installing pygraphviz on some systems, you may have this error:
_graphviz.so: undefined symbol: Agundirected
...which is solved by:
pip install --upgrade pygraphviz --install-option="--include-path=/usr/include/graphviz" \
--install-option="--library-path=/usr/lib/graphviz/"
### Red Hat-like systems (Fedora/CentOS)
* python-devel
......
......@@ -45,7 +45,8 @@ def launch_researchs(args):
def launch_sort(args):
"""Parse a solution file and sort all frontier places in alphabetical order.
"""Parse a solution file or a directory of solution files,
and sort all frontier places in alphabetical order.
"""
# Module import
......@@ -63,12 +64,30 @@ def parse_trajectories(args):
params = args_to_param(args)
params['output'] = params['output'] if params['output'][-1] == '/' \
else params['output'] + '/'
solution_repr.main(
solution_repr.parse_trajectories_main(
params['output'],
params['chart_file'],
params['sol_file']
)
def sol_digging(args):
"""Convert all events for all solutions in a complete MAC file
and write them in a separate file.
This is a function to quickly search all transition attributes involved
in a solution.
"""
# Module import
import solution_repr
params = args_to_param(args)
params['output'] = params['output'] if params['output'][-1] == '/' \
else params['output'] + '/'
solution_repr.sol_digging_main(
params['output'],
params['chart_file'],
params['sol_file']
)
def model_comp(args):
"""Model consistency checking.
......@@ -230,7 +249,8 @@ def main():
parser_solutions_sort = subparsers.add_parser('sort_solutions',
help=launch_sort.__doc__)
parser_solutions_sort.add_argument('sol_file',
help="Solution file (output of 'compute_macs' command).")
help="Solution file or directory with solution files " + \
"(output of 'compute_macs' command).")
parser_solutions_sort.set_defaults(func=launch_sort)
......@@ -244,13 +264,31 @@ def main():
parser_trajectories.add_argument('chart_file',
help="bcx model file.")
parser_trajectories.add_argument('sol_file',
help="Solution file (output of 'compute_macs' command).")
help="Complete solution file (output of 'compute_macs' command).")
parser_trajectories.add_argument('--output', action=ReadableDir,
nargs='?', default='graphs/',
help="Output directory for graphml files.")
parser_trajectories.set_defaults(func=parse_trajectories)
# subparser: Representation of the trajectories of MACs in a complete file.
# Model file (xml : cadbiom language)
# Solution file (cam_complete)
parser_sol_digging = subparsers.add_parser(
'sol_digging',
help=sol_digging.__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser_sol_digging.add_argument('chart_file',
help="bcx model file.")
parser_sol_digging.add_argument('sol_file',
help="Complete solution file or directory with solution files " + \
"(output of 'compute_macs' command).")
parser_sol_digging.add_argument('--output', action=ReadableDir,
nargs='?', default='decompiled_solutions/',
help="Output directory for decompiled solutions files.")
parser_sol_digging.set_defaults(func=sol_digging)
# subparser: Merge solutions to a csv file
# Solution file (cam)
# Output (csv)
......
......@@ -31,6 +31,8 @@ import networkx as nx
import itertools as it
import re
import json
import os
import glob
from logging import DEBUG
# Remove matplotlib dependency
# It is used on demand during the drawing of a graph
......@@ -126,6 +128,8 @@ def load_solutions(file):
continue
elif line[0] != '%':
if sol == line:
# TODO: why this condition ?
# => multiple lines with the same solution ?
# Same frontier places
yield sol, sol_steps[sol]
......@@ -197,7 +201,7 @@ def get_transitions(file, all_places=False):
# and a node (__start__?) for this case.
trans.event = 'SCC-' + trans.ori.name
events = {trans.event: trans.condition}
elif re.match('_h_[0-9_\.]+', trans.event):
elif re.match('_h[\w]+', trans.event):
# 1 event (with 1 clock)
events = {trans.event: trans.condition}
else:
......@@ -652,8 +656,14 @@ def build_graph(solution, steps, transitions):
edges = list() # Normal transitions without condition
# Get all nodes in all transitions (origin & ext)
all_transitions = (transitions[step_event] for step_event in it.chain(*steps))
transitions_ori_ext = (tuple((trans[0], trans[1])) for trans in it.chain(*all_transitions))
try:
all_transitions = \
(transitions[step_event] for step_event in it.chain(*steps))
transitions_ori_ext = \
(tuple((trans[0], trans[1])) for trans in it.chain(*all_transitions))
except KeyError:
print("/!\One event is not in the given model file... Please check it.")
raise
all_nodes = set(it.chain(*transitions_ori_ext)) | set(frontier_places)
# Parse all conditions in transitions;
......@@ -697,23 +707,41 @@ def draw_graph(output_dir, solution, solution_index, G,
- white: middle edges,
- blue: transition edges
:param arg1: Solution string (mostly a set of frontier places).
:param arg2: Index of the solution in the Cadbiom result file
:param arg1: Output directory for graphml files.
:param arg2: Solution string (mostly a set of frontier places).
:param arg3: Index of the solution in the Cadbiom result file
(used to distinguish exported filenames).
:param arg3: Networkx graph object.
:param arg4: Nodes corresponding to transitions with conditions.
:param arg5: All nodes in the model
:param arg6: Edges between transition node and nodes in condition
:param arg7: Normal transitions without condition
:param arg4: Networkx graph object.
:param arg5: Nodes corresponding to transitions with conditions.
List of tuples: event, node
:param arg6: All nodes in the model
:param arg7: Edges between transition node and nodes in condition
:param arg8: Normal transitions without condition
:param arg9: If True, an image is exported by matplotlib (optional).
:type arg1: <str>
:type arg2: <int> or <str>
:type arg3: <networkx.classes.digraph.DiGraph>
:type arg4: <list>
:type arg2: <str>
:type arg3: <int> or <str>
:type arg4: <networkx.classes.digraph.DiGraph>
:type arg5: <list>
:type arg6: <list>
:type arg7: <list>
:type arg7: <list>
:type arg8: <boolean>
"""
creation_date = dt.datetime.now().strftime("%H-%M-%S")
# Save & show
if not matplotlib_export:
# Save the graph without matplotlib requirement
# PS: inhibitors will still have not the attribute 'color' = 'white'
nx.write_graphml(
G,
"{}{}_{}_{}.graphml".format(
output_dir, creation_date, solution_index, solution[:75]
)
)
return
# Drawing ##################################################################
# draw_circular(G, **kwargs) On a circle.
# draw_random(G, **kwargs) Uniformly at random in the unit square.
......@@ -721,19 +749,18 @@ def draw_graph(output_dir, solution, solution_index, G,
# draw_spring(G, **kwargs) Fruchterman-Reingold force-directed algorithm.
# draw_shell(G, **kwargs) Concentric circles.
# draw_graphviz(G[, prog]) Draw networkx graph with graphviz layout.
unzip = lambda l: list(zip(*l))
# Get a list of transition nodes (without dictionnary of attributes)
transition_nodes_names = [node[0] for node in transition_nodes]
pos = nx.circular_layout(G)
# Legend of conditions in transition nodes
if matplotlib_export:
f = plt.figure(1)
ax = f.add_subplot(1,1,1)
text = '\n'.join(
node_dict['label'] for node_dict in unzip(transition_nodes)[1]
)
ax.text(0, 0, text, style='italic', fontsize=10,
bbox={'facecolor':'white', 'alpha':0.5, 'pad':10})
f = plt.figure(1)
ax = f.add_subplot(1,1,1)
text = '\n'.join(transition_nodes_names)
ax.text(0, 0, text, style='italic', fontsize=10,
bbox={'facecolor': 'white', 'alpha': 0.5, 'pad': 10})
# Draw nodes:
# - red: frontier places (in solution variable),
......@@ -751,17 +778,11 @@ def draw_graph(output_dir, solution, solution_index, G,
else:
return 'white'
# Get a list of transition nodes (without dictionnary of attributes)
unziped_transition_nodes = unzip(transition_nodes)[0]
# Color nodes
colors = [color_map(node) for node in G.nodes_iter()]
if matplotlib_export:
nx.draw(G, pos=pos, with_labels=True,
node_color=colors, node_size=1000, alpha=0.5, ax=ax)
else:
nx.draw(G, pos=pos, with_labels=True,
node_color=colors, node_size=1000, alpha=0.5)
nx.draw(G, pos=pos, with_labels=True,
node_color=colors, node_size=1000, alpha=0.5,
ax=ax)
# Draw edges involved in transitions with conditions
edges_colors = [edge[2]['color'] for edge in edges_in_cond]
......@@ -774,20 +795,11 @@ def draw_graph(output_dir, solution, solution_index, G,
nx.draw_networkx_edge_labels(G, pos, edges_labels, label_pos=0.3)
# Save & show
date = dt.datetime.now().strftime("%H-%M-%S")
if matplotlib_export:
plt.legend()
plt.savefig(
output_dir + date + '_' + solution[:75] + ".svg",
format="svg")
plt.show()
nx.write_graphml(
G,
"{}{}_{}_{}.graphml".format(
output_dir, date, solution_index, solution[:75]
)
)
plt.legend()
plt.savefig(
output_dir + creation_date + '_' + solution[:75] + ".svg",
format="svg")
plt.show()
def process_solutions(output_dir, sol_steps, transitions):
......@@ -872,13 +884,12 @@ def test_main():
def sol_digging(sol_steps, transitions):
"""Get steps and all transitions and write all data for each step in a file.
This is an exemple to quickly search all transition attributes involved in a
complete MAC.
def sol_digging(decompiled_filename, sol_steps, transitions):
"""Convert all events for all solutions in a complete MAC file
and write them in a separate file.
.. note:: Output file: output.txt
This is a function to quickly search all transition attributes involved
in a solution.
:param arg1: List of steps involved in a solution. See load_solutions().
:param arg2: A dictionnary of events as keys, and transitions as values.
......@@ -898,21 +909,23 @@ def sol_digging(sol_steps, transitions):
for trans in step_event:
# ori="JUN_nucl_gene" ext="JUN_nucl" event="_h_391"
line = 'ori="{}" ext="{}" event="{}" condition="{}"'.format(
line = 'ori="{}" ext="{}" event="{}" condition="{}"\n'.format(
trans[0],
trans[1],
trans[2]['label'].split('[')[0],
trans[2]['condition']
)
fd.write(line + '\n')
fd.write(line)
with open("output.txt", "w") as fd:
with open(decompiled_filename, 'w') as fd:
# For each sol
for sol, steps in sol_steps:
# Solution header
fd.write(sol + '\n')
# For each step
for step in steps:
for event in step:
......@@ -926,11 +939,49 @@ def sol_digging(sol_steps, transitions):
# Step separation
fd.write('\n')
# Sol separation
fd.write('\n====================================================\n')
def main(output_dir, model_file, solution_file):
def sol_digging_main(output_dir, model_file, solution_path):
"""Entry point for sol_digging
.. note:: This functions tests if the solution_path is a directory
or just a file.
"""
# Get transitions from the model
model_transitions = get_transitions(model_file)
if os.path.isfile(solution_path):
# The given path is a solution file
# Add _decomp to the solution filename
filename, file_extension = os.path.splitext(solution_file)
sol_digging(
output_dir + filename + '_decomp' + file_extension,
load_solutions(solution_file),
model_transitions
)
elif os.path.isdir(solution_path):
# The given path is a directory
solution_path = solution_path if solution_path[-1] == '/' \
else solution_path + '/'
# Decompilation of all files in the directory
for file_number, solution_file in \
enumerate(glob.glob(solution_path + '*cam_complete.txt'), 1):
# Add _decomp to the solution filename
filename, file_extension = os.path.splitext(solution_file)
sol_digging(
output_dir + filename + '_decomp' + file_extension,
load_solutions(solution_file),
model_transitions
)
LOGGER.info("Files processed: " + str(file_number))
def parse_trajectories_main(output_dir, model_file, solution_file):
"""Entry point for parse_trajectories"""
process_solutions(
......
......@@ -21,15 +21,32 @@
# Dyliss team
# IRISA Campus de Beaulieu
# 35042 RENNES Cedex, FRANCE
"""This module provides some functions to do some analyzes on the output
files of Cadbiom.
"""
from __future__ import unicode_literals
from __future__ import print_function
import os
import glob
from collections import Counter
from collections import defaultdict
import csv
## Handle output files #########################################################
def get_solutions(file_descriptor):
"""return a tuple of the original line + the stripped line containing the solution"""
"""Generator of solution lines and corresponding stripped lines.
.. note: Do not return events ! Just sets of frontier places.
:param: Opened file.
:type: <file>
:return: Line (without '\n') and stripped line (with '\' replaced by ' '
(except for final '\t')).
:rtype: <tuple <str>, <str>>
"""
for line in file_descriptor:
# Remove possible \t separator from first line (frontier solution)
......@@ -40,161 +57,69 @@ def get_solutions(file_descriptor):
if stripped_line == '':
continue
# Remove events or other lines
if stripped_line[0] not in ('%', '=', ' '):
# print(stripped_line)
# Sort in lower case, remove ' ' empty elements
yield line, stripped_line
## Sort functions ##############################################################
def sort_solutions(path):
"""
"""
assert os.path.isfile(path) or os.path.isdir(path)
if os.path.isdir(path):
path = path if path[-1] == '/' else path + '/'
[sort_solutions_in_file(file) for file in glob.glob(path + '*cam*')]
else:
sort_solutions_in_file(path)
def sort_solutions_in_file(filepath):
"""Sort all solutions in the given file in alphabetical order.
.. warning:: The file is modified in place.
def sort_solutions_in_file(file):
"""Sort all solutions in alphabetical order in place."""
:param: Filepath to be opened and in which solutions will be sorted.
:arg: <str>
"""
solutions = dict()
with open(file, 'r+') as fd:
with open(filepath, 'r+') as fd:
# Get old line as key and ordered line as value
for line, stripped_line in get_solutions(fd):
# Sort in lower case, remove ' ' empty elements
solutions[line] = \
" ".join(sorted([place for place in stripped_line.split(' ')
if place != ' '], key=lambda s: s.lower()))
# Rewind
# Rewind the whole file
fd.seek(0)
# Load all the content
file_text = fd.read()
# Replace old sols with the new
# Replace old sols with the new ones
for original_sol, sorted_sol in solutions.items():
file_text = file_text.replace(original_sol, sorted_sol)
# print(file_text)
# Rewind
# Rewind the whole file
fd.seek(0)
# Write all text in place
# Write all text in the current opened file
fd.write(file_text)
################################################################################
def get_cam_lines(file):
# Return cam lines
with open(file, 'r+') as fd:
return {stripped_line for _, stripped_line in get_solutions(fd)}
def make_matrix(path):
import glob
from collections import Counter
if path[-1] != '/':
path += '/'
genes = 'COL1A1_gene', 'MMP2_gene', 'MMP9_gene', 'TGFB1_gene', 'TIMP1_gene', 'decorin_gene'
i = 0
for file in glob.glob(path + '*cam.txt'):
i += 1
patterns = set()
for cam in get_cam_lines(file):
patterns.add(frozenset({gene for gene in genes if gene in cam}))
print('file:', file)
print(patterns, len(patterns))
# cnter_of_patterns = Counter(patterns)
# print(cnter_of_patterns, len(cnter_of_patterns))
raw_input('pause')
print("nb files", i)
if __name__ == "__main__":
make_matrix('./docker_results/')
exit()
import glob
from collections import Counter
total_cams = list()
# Get each cams for each file
for file in glob.glob('./docker_results/*cam.txt'):
temp_cams = get_cam_lines(file)
total_cams += list(temp_cams)
# Print the number of cams for the given file
print(file, len(temp_cams))
# Verification of duplicated cams (number > 1)
print([k for k, v in Counter(total_cams).items() if v != 1])
print("total cams:", len(total_cams))
print("len counter", len(Counter(total_cams)))
# Check new results vs old results for SRP9 cams
old_cams = get_cam_lines('/media/DATA/Projets/dyliss_tgf/cadbiom/data/pid_and_clock_no_perm_p21corrected_start_SRP9_complete.txt')
new_cams = get_cam_lines('./bite/Whole NCI-PID database translated into CADBIOM formalism(and)_SRP9_cam.txt')
print("Anciennes", len(old_cams)) # 317
print("Nouvelles", len(new_cams)) # 557
print("Intersection", len(old_cams & new_cams)) # 221
diff = old_cams - new_cams
print("Anciennes non retrouvées", len(diff)) # 96
print("Nouvelles en plus", len(new_cams - old_cams)) # 336
# print("Anciennes non retrouvées", diff)
# Get list of lists of frontier places in old cams not found this time
diff_pb = [problematic_cam.split(' ') for problematic_cam in diff] # Len: 96
def sort_solutions(path):
"""Entry point for sorting solutions.
# Common frontier places in problematic cams
common_frontier_places = set(diff_pb[0]).intersection(*diff_pb)
# print("Places communes aux solutions non retrouvées", common_frontier_places)
print("Nombre de places communes aux solutions non retrouvées", len(common_frontier_places)) # 14
Parse a solution file and sort all frontier places in alphabetical order.
# Résutlat:
set([u'SRF_nucl', u'ERK1_2', u'STAT5_cy', u'IL2Rgamma_JAK3_intToMb',
u'IL2Rbeta_JAK1_LCK_intToMb', u'JNK1_2', u'MEKK1', u'ELK1_nucl',
u'FOS_cy_gene', u'SRP9_gene', u'IL2_glycosylation_exCellRegion',
u'ceramide_start', u'JUN_gene', u'IL2Ralpha_intToMb_gene'])
:param: Filepath or directory path containing Cadbiom solutions.
:type: <str>
"""
# changement de nom dans chaque solution de l'ancien fichier: ceramide_start > ceramide
new_ancient_cams = {cam.replace('ceramide_start', 'ceramide') for cam in old_cams}
print("Anciennes retouchees", len(new_ancient_cams)) # 317 (normal)
print("Anciennes non retrouvées", len(new_ancient_cams - new_cams)) # 0
# => tout a été retrouvé
# Check valid input file/directory
assert os.path.isfile(path) or os.path.isdir(path)
if os.path.isdir(path):
# Recursive search of *cam* files
# (cam.txt, cam_complete.txt, cam_step.txt)
path = path if path[-1] == '/' else path + '/'
[sort_solutions_in_file(file) for file in glob.glob(path + '*cam*')]
else:
sort_solutions_in_file(path)
################################################################################
......@@ -31,7 +31,7 @@ from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
from sys import version_info
__PACKAGE_VERSION__ = "0.1.2"
__PACKAGE_VERSION__ = "0.1.3"
__LIBRARY_VERSION__ = "0.1.0"
deps = []
......
......@@ -101,6 +101,11 @@ class Charter(object):
# common clipboard
self.clipboard = ChartClipboard()
# gui
# Force images in Menus and buttons
# Since Gtk default config forces images to be removed
settings = gtk.settings_get_default()
settings.props.gtk_button_images = True