cellmaps-utils 0.10.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.
@@ -0,0 +1,369 @@
1
+ import json
2
+ import os
3
+ import shutil
4
+ import uuid
5
+ from datetime import date
6
+ import re
7
+ import logging
8
+ import cellmaps_utils
9
+ from cellmaps_utils.basecmdtool import BaseCommandLineTool
10
+ from cellmaps_utils.exceptions import CellMapsError
11
+ from cellmaps_utils import constants
12
+ from cellmaps_utils.provenance import ProvenanceUtil
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class CRISPRDataLoader(BaseCommandLineTool):
18
+ """
19
+ Creates RO-Crate of CRISPR data from
20
+ raw CRISPR data files
21
+ """
22
+ COMMAND = 'crisprconverter'
23
+
24
+ def __init__(self, theargs,
25
+ provenance_utils=ProvenanceUtil()):
26
+ """
27
+ Constructor
28
+
29
+ :param theargs: Command line arguments that at minimum need
30
+ to have the following attributes:
31
+ :type theargs: :py:class:`~python.argparse.Namespace`
32
+ """
33
+ super().__init__()
34
+ self._outdir = os.path.abspath(theargs.outdir)
35
+ self._name = theargs.name
36
+ self._organization_name = theargs.organization_name
37
+ self._project_name = theargs.project_name
38
+ self._release = theargs.release
39
+ self._cell_line = theargs.cell_line
40
+ self._tissue = theargs.tissue
41
+ self._treatment = theargs.treatment
42
+ self._author = theargs.author
43
+ self._gene_set = theargs.gene_set
44
+ self._h5ad = theargs.h5ad
45
+ self._dataset = theargs.dataset
46
+ self._skipcopy = theargs.skipcopy
47
+ self._num_perturb_guides = theargs.num_perturb_guides
48
+ self._num_non_target_ctrls = theargs.num_non_target_ctrls
49
+ self._num_screen_targets = theargs.num_screen_targets
50
+ self._provenance_utils = provenance_utils
51
+ self._softwareid = None
52
+ self._input_data_dict = theargs.__dict__
53
+
54
+ def run(self):
55
+ """
56
+ Runs the process of CRISPR data loading into a RO-Crate.
57
+ It includes generating the output directory,
58
+ linking and registering h5ad file and registering the
59
+ computation and software used in the process.
60
+
61
+ :return:
62
+ """
63
+ self._generate_rocrate_dir_path()
64
+ if os.path.exists(self._outdir):
65
+ raise CellMapsError(self._outdir + ' already exists')
66
+
67
+ logger.debug('Creating directory ' + str(self._outdir))
68
+ os.makedirs(self._outdir, mode=0o755)
69
+
70
+ keywords = [self._project_name, self._release,
71
+ self._cell_line, self._treatment, 'CRISPR',
72
+ self._tissue, self._dataset]
73
+
74
+ if self._gene_set is not None:
75
+ keywords.append(self._gene_set)
76
+
77
+ description = ' '.join(keywords)
78
+
79
+ info_dict = {
80
+ constants.DATASET_NAME: self._name,
81
+ constants.DATASET_ORGANIZATION_NAME: self._organization_name,
82
+ constants.DATASET_PROJECT_NAME: self._project_name,
83
+ constants.DATASET_RELEASE: self._release,
84
+ constants.DATASET_CELL_LINE: self._cell_line,
85
+ constants.DATASET_TREATMENT: self._treatment,
86
+ constants.DATASET_TISSUE: self._tissue,
87
+ constants.DATASET_AUTHOR: self._author,
88
+ constants.DATASET_GENE_SET: self._gene_set,
89
+ constants.DATASET_COLLECTION_SET: self._dataset
90
+ }
91
+ self.save_dataset_info_to_json(self._outdir, info_dict,
92
+ constants.DATASET_INFO_FILE)
93
+
94
+ self._provenance_utils.register_rocrate(self._outdir,
95
+ name=self._name,
96
+ organization_name=self._organization_name,
97
+ project_name=self._project_name,
98
+ description=description,
99
+ keywords=keywords,
100
+ guid=self._get_fairscape_id())
101
+ gen_dsets = []
102
+
103
+ gen_dsets.extend(self._link_and_register_h5ad(keywords=keywords,
104
+ description=description))
105
+ self._register_software(keywords=keywords, description=description)
106
+ self._register_computation(generated_dataset_ids=gen_dsets,
107
+ description=description,
108
+ keywords=keywords)
109
+ self._copy_over_crispr_readme()
110
+ return 0
111
+
112
+ def _get_fairscape_id(self):
113
+ """
114
+ Creates a unique id
115
+ :return:
116
+ """
117
+ return str(uuid.uuid4()) + ':' + os.path.basename(self._outdir)
118
+
119
+ def _get_dataset_description(self):
120
+ """
121
+ Provides a description of the dataset based on its type (1channel or subset).
122
+
123
+ :return: A string description of the dataset.
124
+ :rtype: str
125
+ """
126
+ if self._dataset.lower() == '1channel':
127
+ return 'FASTQs file were obtain by concatenating 3 NGS run over 7 sequencing lanes'
128
+ elif self._dataset.lower() == 'subset':
129
+ return 'Subset run'
130
+ return ''
131
+
132
+ def _link_and_register_h5ad(self, description='',
133
+ keywords=[]):
134
+ """
135
+ Processes expression file by optionally copying it to the output directory and
136
+ registering the file in the
137
+ RO-Crate metadata.
138
+
139
+ :param description: A base description for the files being processed.
140
+ :type description: str
141
+ :param keywords: A list of keywords associated with these files for metadata purposes.
142
+ :type keywords: list
143
+ :return: A list of dataset identifiers for the registered expression files.
144
+ :rtype: list
145
+ """
146
+ dset_ids = []
147
+
148
+ dest_file = os.path.join(self._outdir, constants.PERTURBATION_FILE)
149
+ if self._skipcopy is True:
150
+ open(dest_file, 'a').close()
151
+ else:
152
+ self._link_or_copy(self._h5ad, dest_file)
153
+ file_desc = description + ' file'
154
+ file_keywords = keywords.copy()
155
+ file_keywords.extend(['file'])
156
+ dset_id = self._provenance_utils.register_dataset(rocrate_path=self._outdir, source_file=dest_file,
157
+ skip_copy=True,
158
+ data_dict={'name': 'Single pooled crispr screens analysis',
159
+ 'description': file_desc,
160
+ 'keywords': file_keywords,
161
+ 'data-format': 'h5ad',
162
+ 'author': self._author,
163
+ 'version': self._release,
164
+ 'date-published': date.today().strftime(
165
+ '%Y-%m-%d')},
166
+ guid=self._get_fairscape_id())
167
+ dset_ids.append(dset_id)
168
+
169
+ return dset_ids
170
+
171
+ def _link_or_copy(self, src, dest):
172
+ """
173
+ Attempts to hardlink src to dest and if
174
+ that fails perform a regular copy
175
+
176
+ :param src:
177
+ :param dest:
178
+ :return:
179
+ """
180
+ try:
181
+ os.link(src, dest)
182
+ except OSError as e:
183
+ logger.warning('Falling back to copy because unable to '
184
+ 'hardlink ' + src + ' to ' +
185
+ dest + ' : ' + str(e))
186
+ shutil.copy(src, dest)
187
+
188
+ def _create_token_replacement_map(self):
189
+ """
190
+ Generates a map of tokens to their respective replacement values for use in modifying the CRISPR readme file.
191
+
192
+ :return: A dictionary where each key is a token to replace and each value is the replacement string.
193
+ :rtype: dict
194
+ """
195
+ return {'@@H5AD@@': constants.PERTURBATION_FILE,
196
+ '@@CELL_LINE@@': self._cell_line,
197
+ '@@TREATMENT@@': self._treatment,
198
+ '@@NUM_SCREEN_TARGETS_AND_GENE_SET@@': str(self._num_screen_targets) +
199
+ ' ' + self._gene_set,
200
+ '@@NUM_NON_TARGET_CTRLS@@': str(self._num_non_target_ctrls),
201
+ '@@NUM_PERTURB_GUIDES@@': str(self._num_perturb_guides)}
202
+
203
+ def _copy_over_crispr_readme(self):
204
+ """
205
+ Copies over crispr_readme.txt
206
+
207
+ """
208
+ crispr_readme = os.path.join(os.path.dirname(__file__),
209
+ 'crispr_readme.txt')
210
+
211
+ tokenmap = self._create_token_replacement_map()
212
+ result_readme = os.path.join(self._outdir, 'readme.txt')
213
+ with open(result_readme, 'w') as fout:
214
+ fout.write(self._dataset + ' ' +
215
+ self._get_dataset_description() +
216
+ '\n\n')
217
+ with open(crispr_readme, 'r') as f:
218
+ for line in f:
219
+ line_to_write = self._replace_readme_tokens(line,
220
+ tokenmap=tokenmap)
221
+ fout.write(line_to_write)
222
+
223
+ def _replace_readme_tokens(self, line, tokenmap=None):
224
+ """
225
+ Replaces tokens in a line of the CRISPR readme file with their corresponding values from the token
226
+ replacement map.
227
+
228
+ :param line: The current line from the readme file to process.
229
+ :type line: str
230
+ :param tokenmap: A map of tokens and their replacement values.
231
+ :type tokenmap: dict
232
+ :return: The line with tokens replaced by their respective values.
233
+ :rtype: str
234
+ """
235
+ for token in tokenmap.keys():
236
+ if token in line:
237
+ return line.replace(token, tokenmap[token])
238
+ return line
239
+
240
+ def _generate_rocrate_dir_path(self):
241
+ """
242
+ Generates the directory path for the RO-Crate based on project name, gene set, cell line, treatment type,
243
+ tissue, dataset type, and release version.
244
+ """
245
+ dir_name = self._project_name.lower() + '_'
246
+ if self._gene_set is not None:
247
+ dir_name += self._gene_set.lower() + '_'
248
+ dir_name += self._cell_line.lower() + '_'
249
+ dir_name += re.sub(r'[^a-zA-Z0-9\w\n\.]', '_', self._tissue.lower()) + '_'
250
+ dir_name += self._treatment.lower() + '_crispr_'
251
+ if self._dataset.lower() != '':
252
+ dir_name += self._dataset.lower() + '_'
253
+ dir_name += self._release.lower()
254
+
255
+ dir_name = dir_name.replace(' ', '_')
256
+ self._outdir = os.path.join(self._outdir, dir_name)
257
+
258
+ def _register_computation(self, generated_dataset_ids=[],
259
+ description='',
260
+ keywords=[]):
261
+ """
262
+ Registers the computation.
263
+ # Todo: added in used dataset, software and what is being generated
264
+ :return:
265
+ """
266
+ logger.debug('Getting id of input rocrate')
267
+ comp_keywords = keywords.copy()
268
+ comp_keywords.extend(['computation'])
269
+ description = description + ' run of ' + cellmaps_utils.__name__
270
+ self._provenance_utils.register_computation(self._outdir,
271
+ name='CRISPR',
272
+ run_by=str(self._provenance_utils.get_login()),
273
+ command=str(self._input_data_dict),
274
+ description=description,
275
+ keywords=comp_keywords,
276
+ used_software=[self._softwareid],
277
+ generated=generated_dataset_ids,
278
+ guid=self._get_fairscape_id())
279
+
280
+ def _register_software(self, description='',
281
+ keywords=[]):
282
+ """
283
+ Registers this tool
284
+
285
+ :raises CellMapsImageEmbeddingError: If fairscape call fails
286
+ """
287
+ software_keywords = keywords.copy()
288
+ software_keywords.extend(['tools', cellmaps_utils.__name__])
289
+ software_description = description + ' ' + \
290
+ cellmaps_utils.__description__
291
+ self._softwareid = self._provenance_utils.register_software(self._outdir,
292
+ name=cellmaps_utils.__name__,
293
+ description=software_description,
294
+ author=cellmaps_utils.__author__,
295
+ version=cellmaps_utils.__version__,
296
+ file_format='py',
297
+ keywords=software_keywords,
298
+ url=cellmaps_utils.__repo_url__,
299
+ guid=self._get_fairscape_id())
300
+
301
+ def add_subparser(subparsers):
302
+ """
303
+ Adds a subparser for the CRISPR data loader command.
304
+
305
+ :return:
306
+ """
307
+ desc = """
308
+
309
+ Version {version}
310
+
311
+ {cmd} Loads CRISPR data into a RO-Crate by creating a
312
+ directory, copying over relevant and using FAIRSCAPE CLI to
313
+ register the data files in the directory known as an RO-Crate
314
+
315
+ """.format(version=cellmaps_utils.__version__,
316
+ cmd=CRISPRDataLoader.COMMAND)
317
+
318
+ parser = subparsers.add_parser(CRISPRDataLoader.COMMAND,
319
+ help='Loads CRISPR data into a RO-Crate',
320
+ description=desc,
321
+ formatter_class=constants.ArgParseFormatter)
322
+ parser.add_argument('outdir',
323
+ help='Directory where RO-Crate will be created')
324
+ parser.add_argument('--skipcopy', action='store_true',
325
+ help='If set, --h5ad file will not be copied, '
326
+ 'but instead a 0 byte file will be placed in the RO-Crate as a placeholder. '
327
+ 'It is up to the caller to manually move/copy the files over before distribution')
328
+ parser.add_argument('--h5ad', required=True,
329
+ help='Path to h5ad file')
330
+ parser.add_argument('--author', default='Mali Lab',
331
+ help='Author that created this data')
332
+ parser.add_argument('--name', default='CRISPR',
333
+ help='Name of this run, needed for FAIRSCAPE')
334
+ parser.add_argument('--organization_name', default='Mali Lab',
335
+ help='Name of organization running this tool, needed '
336
+ 'for FAIRSCAPE. Usually set to lab')
337
+ parser.add_argument('--project_name', default='CM4AI',
338
+ help='Name of project running this tool, '
339
+ 'needed for FAIRSCAPE. Usually set to '
340
+ 'funding source')
341
+ parser.add_argument('--release', required=True,
342
+ help='Version of release. '
343
+ 'For example: 0.1 alpha')
344
+ parser.add_argument('--treatment', default='untreated',
345
+ choices=['paclitaxel', 'vorinostat', 'untreated'],
346
+ help='Treatment of sample.')
347
+ parser.add_argument('--dataset', required=True,
348
+ choices=['1channel', 'subset', '4channel'],
349
+ help='Collection set')
350
+ parser.add_argument('--cell_line', default='MDA-MB-468',
351
+ choices=['MDA-MB-468', 'KOLF2.1J'],
352
+ help='Name of cell line. For example MDA-MB-468')
353
+ parser.add_argument('--gene_set', choices=['chromatin', 'metabolic'],
354
+ default='chromatin',
355
+ help='Gene set for dataset')
356
+ parser.add_argument('--tissue', choices=['undifferentiated', 'neuron',
357
+ 'cardiomyocytes', ''],
358
+ default='breast; mammary gland',
359
+ help='Tissue for dataset. Since the default --cell_line '
360
+ 'is MDA-MB-468, this value is set to the tissue '
361
+ 'for that cell line')
362
+ parser.add_argument('--num_perturb_guides', default='6',
363
+ help='Number of guides per perturbation')
364
+ parser.add_argument('--num_non_target_ctrls', default='109',
365
+ help='Number of non targeting controls')
366
+ parser.add_argument('--num_screen_targets', default='108',
367
+ help='Number of screen targets')
368
+ return parser
369
+
@@ -0,0 +1,285 @@
1
+ import logging
2
+ import os.path
3
+
4
+ from ndex2.cx2 import CX2Network, RawCX2NetworkFactory
5
+ import ndex2.constants as constants
6
+
7
+ from cellmaps_utils import hcx_utils
8
+ from cellmaps_utils.exceptions import CellMapsError
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class InteractomeToDDOTConverter:
14
+
15
+ def __init__(self, output_dir, interactome_path, ddot_file_name='interactome_ddot.txt'):
16
+ """
17
+ Initializes the converter with the path to the interactome data in CX2 format, output directory, and file name.
18
+
19
+ :param output_dir: Directory where the output file will be saved
20
+ :type output_dir: str
21
+ :param interactome_path: Path to the interactome file
22
+ :type interactome_path: str
23
+ :param ddot_file_name: Name of the output file to write DDOT formatted data
24
+ :type ddot_file_name: str
25
+ """
26
+ self._interactome_path = interactome_path
27
+ self._output_file = os.path.join(output_dir, ddot_file_name)
28
+
29
+ def generate_ddot_format_file(self):
30
+ """
31
+ Reads an interactome from a specified path and writes it out in DDOT format.
32
+ The output includes nodes and their interactions.
33
+ """
34
+ factory = RawCX2NetworkFactory()
35
+ interactome = factory.get_cx2network(self._interactome_path)
36
+ with open(self._output_file, 'w') as f:
37
+ for edge_id, edge_values in interactome.get_edges().items():
38
+ source = edge_values[constants.EDGE_SOURCE]
39
+ target = edge_values[constants.EDGE_TARGET]
40
+ if constants.EDGE_INTERACTION_EXPANDED in edge_values[constants.ASPECT_VALUES]:
41
+ interaction = edge_values[constants.ASPECT_VALUES][constants.EDGE_INTERACTION_EXPANDED]
42
+ f.write(f"{source}\t{target}\t{interaction}\n")
43
+ else:
44
+ f.write(f"{source}\t{target}\n")
45
+
46
+
47
+ class DDOTToInteractomeConverter:
48
+
49
+ def __init__(self, output_dir, interactome_ddot_path, interactome_file_name='interactome.cx2'):
50
+ """
51
+ Initializes the converter to transform a DDOT formatted file to an CX2 format.
52
+
53
+ :param output_dir: Directory where the output file will be saved
54
+ :type output_dir: str
55
+ :param interactome_ddot_path: Path to the DDOT formatted file
56
+ :type interactome_ddot_path: str
57
+ :param interactome_file_name: Name of the output file for the interactome
58
+ :type interactome_file_name: str
59
+ """
60
+ self._interactome_ddot_path = interactome_ddot_path
61
+ self._output_file = os.path.join(output_dir, interactome_file_name)
62
+
63
+ def generate_interactome_file(self):
64
+ """
65
+ Converts a DDOT formatted file to an interactome CX2 network file. It parses the DDOT file,
66
+ constructs nodes and edges in the interactome, and saves the output.
67
+ """
68
+ interactome = CX2Network()
69
+ with open(self._interactome_ddot_path, 'r') as f:
70
+ for line in f:
71
+ parts = line.strip().split('\t')
72
+ id_source_node = interactome.lookup_node_id_by_name(parts[0])
73
+ source_node = id_source_node if id_source_node is not None else interactome.add_node(
74
+ attributes={'name': parts[0]})
75
+ id_target_node = interactome.lookup_node_id_by_name(parts[1])
76
+ target_node = id_target_node if id_target_node is not None else interactome.add_node(
77
+ attributes={'name': parts[1]})
78
+ id_edge = interactome.add_edge(source=source_node, target=target_node)
79
+ if len(parts) >= 3:
80
+ interactome.add_edge_attribute(id_edge, constants.EDGE_INTERACTION_EXPANDED, parts[3])
81
+ interactome.write_as_raw_cx2(self._output_file)
82
+
83
+
84
+ class HierarchyToDDOTConverter:
85
+
86
+ def __init__(self, output_dir, hierarchy_path, ontology_file_name='ontology.ont'):
87
+ """
88
+ Initializes the converter to transform a hierarchy data file in CX2 format into a DDOT ontology file.
89
+
90
+ :param output_dir: Directory where the output ontology file will be saved
91
+ :type output_dir: str
92
+ :param hierarchy_path: Path to the hierarchy data file
93
+ :type hierarchy_path: str
94
+ :param ontology_file_name: Name of the output ontology file
95
+ :type ontology_file_name: str
96
+ """
97
+ self._hierarchy_path = hierarchy_path
98
+ self._output_file = os.path.join(output_dir, ontology_file_name)
99
+
100
+ def generate_ontology_ddot_file(self):
101
+ """
102
+ Converts a hierarchy network file into a DDOT ontology file format. This method extracts hierarchy
103
+ edges and node information to create an ontology representation suitable for DDOT applications.
104
+ """
105
+ factory = RawCX2NetworkFactory()
106
+ hierarchy = factory.get_cx2network(self._hierarchy_path)
107
+ with open(self._output_file, 'w') as f:
108
+ for _, edge_values in hierarchy.get_edges().items():
109
+ source_id = edge_values[constants.EDGE_SOURCE]
110
+ source = hierarchy.get_node(source_id)[constants.ASPECT_VALUES].get(constants.NODE_NAME_EXPANDED,
111
+ source_id)
112
+ target_id = edge_values[constants.EDGE_TARGET]
113
+ target = hierarchy.get_node(target_id)[constants.ASPECT_VALUES].get(constants.NODE_NAME_EXPANDED,
114
+ target_id)
115
+ f.write(f"{source}\t{target}\tdefault\n")
116
+ for node_id, node_values in hierarchy.get_nodes().items():
117
+ node_attr = node_values[constants.ASPECT_VALUES]
118
+ node = node_attr.get(constants.NODE_NAME_EXPANDED, node_id)
119
+ genes = node_attr.get('CD_MemberList', '').split(' ')
120
+ for gene in genes:
121
+ f.write(f"{node}\t{gene}\tgene\n")
122
+
123
+
124
+ class DDOTToHierarchyConverter:
125
+
126
+ def __init__(self, output_dir, ontology_ddot_path, parent_ddot_path=None, parent_ndex_url=None,
127
+ host='ndexbio.org', parent_uuid=None, ndex_user=None, ndex_password=None,
128
+ hierarchy_filename='hierarchy.cx2', interactome_filename='hierarchy_parent.cx2'):
129
+ """
130
+ Initializes the converter to create a hierarchy in CX2 from DDOT ontology file.
131
+
132
+ :param output_dir: Directory where the output files will be saved
133
+ :type output_dir: str
134
+ :param ontology_ddot_path: Path to the DDOT formatted ontology file
135
+ :type ontology_ddot_path: str
136
+ :param parent_ddot_path: Optional path to the parent interactome DDOT file
137
+ :type parent_ddot_path: str, optional
138
+ :param parent_ndex_url: Optional URL to the parent interactome in NDEx
139
+ :type parent_ndex_url: str, optional
140
+ :param host: Hostname for the NDEx server
141
+ :type host: str
142
+ :param parent_uuid: UUID of the parent network in NDEx
143
+ :type parent_uuid: str, optional
144
+ :param ndex_user: Username for NDEx server authentication
145
+ :type ndex_user: str, optional
146
+ :param ndex_password: Password for NDEx server authentication
147
+ :type ndex_password: str, optional
148
+ :param hierarchy_filename: Name for the output hierarchy file
149
+ :type hierarchy_filename: str
150
+ :param interactome_filename: Name for the output interactome file (parent network)
151
+ :type interactome_filename: str
152
+ """
153
+ self._output_dir = output_dir
154
+ self._hierarchy_file = os.path.join(output_dir, hierarchy_filename)
155
+ self._interactome_filename = interactome_filename
156
+ self._ontology_ddot_path = ontology_ddot_path
157
+ if parent_ndex_url is None and parent_ddot_path is None:
158
+ raise CellMapsError("Specifying either url of parent interactome in NDEx, or edge list of interactome"
159
+ " is required!")
160
+ if parent_ddot_path is not None and (parent_ndex_url is not None or parent_uuid is not None):
161
+ raise CellMapsError("You need to specify parent network as edge list in file, or as network in NDEx, "
162
+ "not both!")
163
+ self._parent_url = parent_ndex_url
164
+ self._parent_edgelist = parent_ddot_path
165
+ self._host = host
166
+ self._uuid = parent_uuid
167
+ self._username = ndex_user
168
+ self._password = ndex_password
169
+
170
+ def generate_hierarchy_hcx_file(self):
171
+ """
172
+ Constructs a hierarchy network from a DDOT formatted ontology file, utilizing a parent interactome.
173
+ The hierarchy is enriched with hierarchical context (HCX) information and saved as CX2.
174
+ """
175
+ interactome = hcx_utils.get_interactome(self._host, self._uuid, self._username, self._password,
176
+ self._parent_edgelist)
177
+ hierarchy = self._get_hierarchy(interactome)
178
+ hcx_utils.add_hcx_network_annotations(hierarchy, interactome, self._output_dir, self._interactome_filename,
179
+ self._host, self._uuid)
180
+ hierarchy.write_as_raw_cx2(self._hierarchy_file)
181
+
182
+ def _get_hierarchy(self, interactome):
183
+ """
184
+ Constructs the hierarchy based on the DDOT edge list and node list and uses interactome to add HCX information.
185
+
186
+ :param interactome: The interactome network.
187
+ :type interactome: CX2Network
188
+ :return: A CX2Network object representing the hierarchy.
189
+ :rtype: CX2Network
190
+ """
191
+ hierarchy = CX2Network()
192
+
193
+ with open(self._ontology_ddot_path, 'r') as f:
194
+ for line in f:
195
+ source, target, edge_type = line.strip().split('\t')
196
+ if 'gene' in edge_type.lower():
197
+ source_id = hierarchy.lookup_node_id_by_name(source)
198
+ if source_id is not None:
199
+ node_attr = hierarchy.get_node(source_id)[constants.ASPECT_VALUES]
200
+ target_id = interactome.lookup_node_id_by_name(target)
201
+ if 'CD_MemberList' not in node_attr:
202
+ hierarchy.add_node_attribute(source_id, 'CD_MemberList', target)
203
+ hierarchy.add_node_attribute(source_id, 'HCX::members', [target_id])
204
+ else:
205
+ hierarchy.add_node_attribute(source_id, 'CD_MemberList',
206
+ node_attr['CD_MemberList'] + ' ' + target)
207
+ node_attr['HCX::members'].append(target_id)
208
+ hierarchy.add_node_attribute(source_id, 'HCX::members', node_attr['HCX::members'])
209
+ else:
210
+ source_id = hierarchy.lookup_node_id_by_name(source)
211
+ if source_id is None:
212
+ source_id = hierarchy.add_node(attributes={constants.NODE_NAME_EXPANDED: source})
213
+ target_id = hierarchy.lookup_node_id_by_name(target)
214
+ if target_id is None:
215
+ target_id = hierarchy.add_node(attributes={constants.NODE_NAME_EXPANDED: target})
216
+ hierarchy.add_edge(source=source_id, target=target_id)
217
+
218
+ self._update_memberlist(hierarchy=hierarchy, interactome=interactome)
219
+ root_nodes = hcx_utils.get_root_nodes(hierarchy)
220
+ hcx_utils.add_isroot_node_attribute(hierarchy, root_nodes=root_nodes)
221
+
222
+ return hierarchy
223
+
224
+ def _update_memberlist(self, hierarchy=None, interactome=None):
225
+ """
226
+ Iterates through all nodes of network updating the CD_MemberList and HCX::members by
227
+ aggregating all the gene lists from the node and its children
228
+
229
+ :param hierarchy:
230
+ :type hierarchy: :py:class:`~ndex2.cx2.CX2Network`
231
+ """
232
+ for node_id, node_obj in hierarchy.get_nodes().items():
233
+ children_nodes = self._get_children_of_node(hierarchy, node_id)
234
+ children_nodes.add(node_id)
235
+ member_list, member_ids = self._get_memberlists_from_nodes(hierarchy, interactome, children_nodes)
236
+ hierarchy.add_node_attribute(node_id, 'CD_MemberList', ' '.join(member_list))
237
+ hierarchy.add_node_attribute(node_id, 'HCX::members', member_ids)
238
+
239
+ def _get_children_of_node(self, network=None, node_id=None):
240
+ """
241
+ Gets children of node as set. It is assumed the child node is the target
242
+ of the edge.
243
+
244
+ :param network:
245
+ :type network: :py:class:`~ndex2.cx2.CX2Network`
246
+ :param node_id: node whose children should be found
247
+ :return: id of children nodes
248
+ :rtype: set
249
+ """
250
+ child_set = set()
251
+ for edge_id, edge_obj in network.get_edges().items():
252
+ if edge_obj[constants.EDGE_SOURCE] == node_id:
253
+ if edge_obj[constants.EDGE_TARGET] not in child_set:
254
+ child_set.add(edge_obj[constants.EDGE_TARGET])
255
+ child_set.update(self._get_children_of_node(network=network,
256
+ node_id=edge_obj[constants.EDGE_TARGET]))
257
+ return child_set
258
+
259
+ @staticmethod
260
+ def _get_memberlists_from_nodes(hierarchy=None, interactome=None, node_set=None):
261
+ """
262
+ Gets set of genes from all nodes in **node_set**
263
+
264
+ :param hierarchy:
265
+ :type hierarchy: :py:class:`~ndex2.cx2.CX2Network`
266
+ :param interactome:
267
+ :type interactome: :py:class:`~ndex2.cx2.CX2Network`
268
+ :param node_set:
269
+ :type node_set: set
270
+ :return: gene_set
271
+ :rtype: list
272
+ :return: hcx_set
273
+ :rtype: list
274
+ """
275
+ gene_set = set()
276
+ hcx_set = set()
277
+ for node_id in node_set:
278
+ member_list = hierarchy.get_node(node_id).get(constants.ASPECT_VALUES, {}).get('CD_MemberList', None)
279
+ if member_list is None:
280
+ continue
281
+ member_set = set(member_list.split(' '))
282
+ gene_set.update(member_set)
283
+ for member in list(member_set):
284
+ hcx_set.add(interactome.lookup_node_id_by_name(member))
285
+ return list(gene_set), list(hcx_set)
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ class CellMapsError(Exception):
5
+ """
6
+ Base exception for CellMapsUtils
7
+ """
8
+ pass
9
+
10
+
11
+ class CellMapsProvenanceError(CellMapsError):
12
+ """
13
+ Base exception for provenance errors
14
+ """
15
+ pass