nmcp-precomputed 3.0.4__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,273 @@
1
+ import argparse
2
+ import logging
3
+ import threading
4
+
5
+ from .data import RemoteDataClient, PrecomputedEntry
6
+ from .precomputed import ensure_bucket_folders, create_from_data, extract_neuron_properties, SkeletonComponents
7
+
8
+ logging.basicConfig(level=logging.WARNING)
9
+ logging.getLogger("nmcp").setLevel(logging.DEBUG)
10
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ process_interval: int = 10 # seconds
15
+ heartbeat_interval: int = 3600 # seconds
16
+
17
+ heartbeat_count_limit: int = int(heartbeat_interval / process_interval)
18
+ heartbeat_current_count: int = 0
19
+
20
+
21
+ def load_specimen_space_reconstruction(client: RemoteDataClient, pending: PrecomputedEntry):
22
+ reconstruction_id = pending.reconstructionId
23
+ skeleton_id = pending.skeletonId
24
+
25
+ data = client.get_specimen_space_reconstruction_data(reconstruction_id)
26
+
27
+ try:
28
+ # Extract properties once from header
29
+ properties = extract_neuron_properties(data)
30
+
31
+ axon_components = SkeletonComponents.create(data["axon"])
32
+
33
+ dendrite_components = SkeletonComponents.create(data["dendrite"])
34
+
35
+ return axon_components, dendrite_components, properties
36
+
37
+ except Exception as ex:
38
+ logger.error(f"error loading specimen space reconstruction {reconstruction_id}: {ex}")
39
+ return None
40
+
41
+
42
+ def load_reconstruction(client: RemoteDataClient, pending: PrecomputedEntry):
43
+ header_data = client.get_reconstruction_header(pending.reconstructionId)
44
+
45
+ reconstruction_id = pending.reconstructionId
46
+ skeleton_id = pending.skeletonId
47
+
48
+ if header_data is None or reconstruction_id is None or skeleton_id is None:
49
+ return None
50
+
51
+ logger.info(f"{reconstruction_id} with skeleton id {skeleton_id} has header data available")
52
+
53
+ try:
54
+ # Extract properties once from header
55
+ properties = extract_neuron_properties(header_data)
56
+
57
+ # Process axon data in true chunks
58
+ logger.info(f"retrieving axon data in chunks for {reconstruction_id}")
59
+ axon_components = None
60
+ chunk_size = 25000
61
+ axon_offset = 0
62
+ axon_total_points = 0
63
+
64
+ while True:
65
+ logger.debug(f"fetching axon chunk at offset {axon_offset} with size {chunk_size}")
66
+ axon_result = client.get_axon_chunks(
67
+ reconstruction_id,
68
+ chunk_size=chunk_size,
69
+ offset=axon_offset,
70
+ limit=chunk_size
71
+ )
72
+
73
+ if not axon_result or not axon_result["data"]:
74
+ logger.debug("no more axon data available")
75
+ break
76
+
77
+ chunk_points = axon_result["data"]
78
+ chunk_count = len(chunk_points)
79
+
80
+ if axon_components is None:
81
+ # First chunk - create new SkeletonComponents
82
+ logger.debug(f"creating axon components with {chunk_count} points")
83
+ axon_components = SkeletonComponents.create(chunk_points)
84
+ else:
85
+ # Subsequent chunks - append to existing
86
+ logger.debug(f"appending {chunk_count} points to axon components")
87
+ axon_components.append(chunk_points)
88
+
89
+ axon_total_points += chunk_count
90
+
91
+ # Check if we got less than requested (end of data)
92
+ if chunk_count < chunk_size:
93
+ logger.debug(f"received {chunk_count} < {chunk_size}, end of axon data")
94
+ break
95
+
96
+ axon_offset += chunk_count
97
+
98
+ if axon_components is not None:
99
+ logger.info(f"assembled axon with {axon_total_points} total points")
100
+
101
+ # Process dendrite data in true chunks
102
+ logger.info(f"retrieving dendrite data in chunks for {reconstruction_id}")
103
+ dendrite_components = None
104
+
105
+ dendrite_offset = 0
106
+ dendrite_total_points = 0
107
+
108
+ while True:
109
+ logger.debug(f"fetching dendrite chunk at offset {dendrite_offset} with size {chunk_size}")
110
+ dendrite_result = client.get_dendrite_chunks(
111
+ reconstruction_id,
112
+ chunk_size=chunk_size,
113
+ offset=dendrite_offset,
114
+ limit=chunk_size
115
+ )
116
+
117
+ if not dendrite_result or not dendrite_result["data"]:
118
+ logger.debug("no more dendrite data available")
119
+ break
120
+
121
+ chunk_points = dendrite_result["data"]
122
+ chunk_count = len(chunk_points)
123
+
124
+ if dendrite_components is None:
125
+ # First chunk - create new SkeletonComponents
126
+ logger.debug(f"creating dendrite components with {chunk_count} points")
127
+ dendrite_components = SkeletonComponents.create(chunk_points)
128
+ else:
129
+ # Subsequent chunks - append to existing
130
+ logger.debug(f"appending {chunk_count} points to dendrite components")
131
+ dendrite_components.append(chunk_points)
132
+
133
+ dendrite_total_points += chunk_count
134
+
135
+ # Check if we got less than requested (end of data)
136
+ if chunk_count < chunk_size:
137
+ logger.debug(f"received {chunk_count} < {chunk_size}, end of dendrite data")
138
+ break
139
+
140
+ dendrite_offset += chunk_count
141
+
142
+ if dendrite_components is not None:
143
+ logger.info(f"assembled dendrite with {dendrite_total_points} total points")
144
+
145
+ return axon_components, dendrite_components, properties
146
+
147
+ except Exception as ex:
148
+ logger.error(f"error loading reconstruction {reconstruction_id}: {ex}")
149
+ return None
150
+
151
+
152
+ def save_specimen_precomputed(output, skeleton_id, properties, axon_components, dendrite_components):
153
+ logger.info(f"creating specimen space precomputed skeleton {skeleton_id}")
154
+ create_from_data(axon_components, dendrite_components, properties, f"{output}/specimen", skeleton_id)
155
+
156
+
157
+ def save_atlas_precomputed(output, skeleton_id, properties, axon_components, dendrite_components):
158
+ # Create the full reconstruction (both axon and dendrite)
159
+ logger.info(f"creating full reconstruction for skeleton {skeleton_id}")
160
+ create_from_data(axon_components, dendrite_components, properties, f"{output}/full", skeleton_id)
161
+
162
+ # Create axon-only reconstruction
163
+ logger.info(f"creating axon-only reconstruction for skeleton {skeleton_id}")
164
+ create_from_data(axon_components, None, properties, f"{output}/axon", skeleton_id)
165
+
166
+ # Create dendrite-only reconstruction
167
+ logger.info(f"creating dendrite-only reconstruction for skeleton {skeleton_id}")
168
+ create_from_data(None, dendrite_components, properties, f"{output}/dendrite", skeleton_id)
169
+
170
+
171
+ def process_pending(client: RemoteDataClient, output: str):
172
+ global heartbeat_current_count, heartbeat_count_limit
173
+
174
+ processed_atlas = process_atlas_pending(client, output)
175
+ processed_specimen = process_specimen_pending(client, output)
176
+
177
+ if processed_atlas or processed_specimen:
178
+ heartbeat_current_count = 0
179
+ else:
180
+ heartbeat_current_count += 1
181
+ if heartbeat_current_count >= heartbeat_count_limit:
182
+ logger.info("There are no pending precomputed entries")
183
+ heartbeat_current_count = 0
184
+
185
+ t1 = threading.Timer(process_interval, process_pending, (client, output))
186
+ t1.start()
187
+
188
+
189
+ def process_specimen_pending(client: RemoteDataClient, output: str) -> bool:
190
+ try:
191
+ pending = client.find_specimen_space_pending()
192
+
193
+ if len(pending) > 0:
194
+ logger.info(f"{len(pending)} pending specimen space precomputed entries")
195
+
196
+ for pend in pending:
197
+ try:
198
+ reconstruction = load_specimen_space_reconstruction(client, pend)
199
+
200
+ if reconstruction is None:
201
+ client.mark_specimen_failed_load(pend.id)
202
+ continue
203
+
204
+ axon_components, dendrite_components, properties = reconstruction
205
+
206
+ save_specimen_precomputed(output, pend.skeletonId, properties, axon_components,
207
+ dendrite_components)
208
+
209
+ client.mark_specimen_generated(pend.id)
210
+ except Exception as ex:
211
+ logger.error("error", None, ex, True)
212
+ client.mark_specimen_failed_generate(pend.id)
213
+
214
+ return True
215
+ except Exception as ex:
216
+ logger.error("process error", None, ex, True)
217
+
218
+ return False
219
+
220
+
221
+ def process_atlas_pending(client: RemoteDataClient, output: str) -> bool:
222
+ try:
223
+ pending = client.find_atlas_pending()
224
+
225
+ if len(pending) > 0:
226
+ logger.info(f"{len(pending)} pending atlas precomputed entries")
227
+
228
+ for pend in pending:
229
+ try:
230
+ reconstruction = load_reconstruction(client, pend)
231
+
232
+ if reconstruction is None:
233
+ client.mark_atlas_failed_load(pend.id)
234
+ continue
235
+
236
+ axon_components, dendrite_components, properties = reconstruction
237
+
238
+ save_atlas_precomputed(output, pend.skeletonId, properties, axon_components,
239
+ dendrite_components)
240
+
241
+ client.mark_atlas_generated(pend.id)
242
+ except Exception as ex:
243
+ logger.error("error", None, ex, True)
244
+ client.mark_atlas_failed_generate(pend.id)
245
+
246
+ return True
247
+ except Exception as ex:
248
+ logger.error("process error", None, ex, True)
249
+
250
+ return False
251
+
252
+
253
+ def main(url: str, auth_key: str, output: str):
254
+ logger.info(f"starting data client for url: {url}")
255
+ logger.info(f"output base url: {output}")
256
+
257
+ ensure_bucket_folders(output)
258
+
259
+ client = RemoteDataClient(url, auth_key)
260
+
261
+ process_pending(client, output)
262
+
263
+
264
+ if __name__ == '__main__':
265
+ parser = argparse.ArgumentParser()
266
+
267
+ parser.add_argument("-u", "--url", help="URL of the GraphQL service")
268
+ parser.add_argument("-a", "--authkey", help="authorization header for GraphQL service")
269
+ parser.add_argument("-o", "--output", help="the output cloud volume location")
270
+
271
+ args = parser.parse_args()
272
+
273
+ main(args.url, args.authkey, args.output)
@@ -0,0 +1,21 @@
1
+ import argparse
2
+ import logging
3
+
4
+ from .precomputed import remove_skeleton
5
+
6
+ logging.basicConfig(level=logging.WARNING)
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser()
11
+
12
+ parser.add_argument("-o", "--output", help="the output cloud volume location")
13
+ parser.add_argument("-s", "--skeleton", help="the is of the skeleton to remove", type=int)
14
+
15
+ args = parser.parse_args()
16
+
17
+ remove_skeleton(args.skeleton, args.output)
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: nmcp-precomputed
3
+ Version: 3.0.4
4
+ Summary: Neuron Morphology Community Portal Precomputed Toolbox
5
+ License: MIT
6
+ Requires-Python: <3.11,>=3.10
7
+ Requires-Dist: allensdk
8
+ Requires-Dist: cloud-files
9
+ Requires-Dist: cloud-volume
10
+ Requires-Dist: gql[requests]
11
+ Requires-Dist: numpy<1.24
12
+ Requires-Dist: pandas
13
+ Provides-Extra: publish
14
+ Requires-Dist: build; extra == 'publish'
15
+ Requires-Dist: twine; extra == 'publish'
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest==8.2.0; extra == 'test'
@@ -0,0 +1,19 @@
1
+ nmcp/__init__.py,sha256=77zzZ079YOR9DYewHmWMd0Z7qKHt3fya2aDyTc2d02s,386
2
+ nmcp/__main__.py,sha256=Sr015AWnIyp3eyb1Yz2x1YFmIFhZM5Py5XHEhdTOCEk,399
3
+ nmcp/from_json.py,sha256=U-YBM5UXEOzM001dnopqGChomfLYa8RORJXNxfVsdDE,685
4
+ nmcp/from_service.py,sha256=nM0WNDZZm-t0Xa0_vZxCcFVBPm9UyqVvsZihKZmMFo0,844
5
+ nmcp/list_skeletons.py,sha256=UJ7AhBaM8oqGn4IXX7sI2ff0k_EMM7AlGhxzB-f_w3w,433
6
+ nmcp/precomputed_worker.py,sha256=tFEzBLFKeXOcLh7kWa_CsIGo0WHf1Oydjdbu8EFytHc,10130
7
+ nmcp/remove_skeleton.py,sha256=VCyVO_jiAFz94L_LglNXLH5Zkjwn6YgedizLu1YULU4,470
8
+ nmcp/data/__init__.py,sha256=RUdff4AivqMyOdMy2S1atG8yXF9VEXD1Yx6P2R19l6Y,97
9
+ nmcp/data/precomputed_entry.py,sha256=y7VCR5ZetQdoWmH7034n6bEeSyzcXpwjrAeB0YBNxZU,183
10
+ nmcp/data/remote_data_client.py,sha256=sqyr95St8stwYPlZYdpElrY1GI2rUDG4GnmTHJa--aE,15585
11
+ nmcp/precomputed/__init__.py,sha256=v-t9pM8MF2rErRz5IEG4ZngH669VIBBFq9C2DKZypwY,406
12
+ nmcp/precomputed/nmcp_precomputed.py,sha256=3Id2OAG12hlq2yHaOdMR1f6xyBad7iiv83yTKeyhifs,8080
13
+ nmcp/precomputed/nmcp_skeleton.py,sha256=d3FnKPCwLnW43KMJIHGhVrjF9504gINrf_7mdwqiLfY,4646
14
+ nmcp/precomputed/segment_info.py,sha256=WyqHvicK2tQ1iAd2BWaNFop8Zl46hxhVBsLssskZcuI,2165
15
+ nmcp/precomputed/segment_property.py,sha256=s2cUIoBJBmX0eyKS0oyQjIYD4bPL68ABo8eSPqC43BY,943
16
+ nmcp/precomputed/segment_tag_property.py,sha256=Ict-wMtfH0ej3V6dOWi8ndxDqFs64J4d_mJy43jcYrs,2553
17
+ nmcp_precomputed-3.0.4.dist-info/METADATA,sha256=ktMUxjnep73NaNVqU3hi0ChflFHWhvXkzMZcoC-UrnA,496
18
+ nmcp_precomputed-3.0.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
19
+ nmcp_precomputed-3.0.4.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any