1use crate::common::ApiState;
8use crate::common::{ApiError, ApiResult, Json, Path, Query, State};
9use serde::Deserialize;
10use std::collections::HashMap;
11use tracing::warn;
12use utoipa::{IntoParams, ToSchema};
13
14#[utoipa::path(
16 get,
17 path = "/v1/connectome/cortical_areas/list/detailed",
18 tag = "connectome",
19 responses(
20 (status = 200, description = "Detailed cortical areas list", body = HashMap<String, serde_json::Value>),
21 (status = 500, description = "Internal server error")
22 )
23)]
24pub async fn get_cortical_areas_list_detailed(
25 State(state): State<ApiState>,
26) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
27 let connectome_service = state.connectome_service.as_ref();
28 match connectome_service.list_cortical_areas().await {
29 Ok(areas) => {
30 tracing::info!(target: "feagi-api",
31 "[DETAILED-LIST] Returning {} cortical areas", areas.len()
32 );
33
34 let detailed: HashMap<String, serde_json::Value> = areas
35 .into_iter()
36 .map(|area| {
37 tracing::debug!(target: "feagi-api",
38 "[DETAILED-LIST] Area {}: cortical_type='{}', is_mem_type={:?}",
39 area.cortical_id, area.cortical_type,
40 area.properties.get("is_mem_type")
41 );
42
43 let json_value = serde_json::to_value(&area).unwrap_or_default();
44
45 tracing::debug!(target: "feagi-api",
46 "[DETAILED-LIST] Serialized area {} has cortical_type: {}",
47 area.cortical_id, json_value.get("cortical_type").is_some()
48 );
49
50 (area.cortical_id.clone(), json_value)
51 })
52 .collect();
53 Ok(Json(detailed))
54 }
55 Err(e) => Err(ApiError::internal(format!(
56 "Failed to get detailed list: {}",
57 e
58 ))),
59 }
60}
61
62#[utoipa::path(get, path = "/v1/connectome/properties/dimensions", tag = "connectome")]
64pub async fn get_properties_dimensions(
65 State(_state): State<ApiState>,
66) -> ApiResult<Json<(usize, usize, usize)>> {
67 Ok(Json((0, 0, 0)))
70}
71
72#[utoipa::path(get, path = "/v1/connectome/properties/mappings", tag = "connectome")]
74pub async fn get_properties_mappings(
75 State(_state): State<ApiState>,
76) -> ApiResult<Json<HashMap<String, Vec<String>>>> {
77 Ok(Json(HashMap::new()))
79}
80
81#[utoipa::path(get, path = "/v1/connectome/snapshot", tag = "connectome", responses((status = 200, body = HashMap<String, serde_json::Value>)))]
83pub async fn get_snapshot(
84 State(state): State<ApiState>,
85) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
86 let connectome_service = state.connectome_service.as_ref();
87 let areas = connectome_service
88 .list_cortical_areas()
89 .await
90 .map_err(|e| ApiError::internal(format!("{}", e)))?;
91 let regions = connectome_service
92 .list_brain_regions()
93 .await
94 .map_err(|e| ApiError::internal(format!("{}", e)))?;
95 let mut response = HashMap::new();
96 response.insert(
97 "cortical_area_count".to_string(),
98 serde_json::json!(areas.len()),
99 );
100 response.insert(
101 "brain_region_count".to_string(),
102 serde_json::json!(regions.len()),
103 );
104 Ok(Json(response))
105}
106
107#[utoipa::path(get, path = "/v1/connectome/stats", tag = "connectome", responses((status = 200, body = HashMap<String, serde_json::Value>)))]
109pub async fn get_stats(
110 State(state): State<ApiState>,
111) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
112 let analytics_service = state.analytics_service.as_ref();
113 let health = analytics_service
114 .get_system_health()
115 .await
116 .map_err(|e| ApiError::internal(format!("{}", e)))?;
117 let mut response = HashMap::new();
118 response.insert(
119 "neuron_count".to_string(),
120 serde_json::json!(health.neuron_count),
121 );
122 response.insert(
123 "cortical_area_count".to_string(),
124 serde_json::json!(health.cortical_area_count),
125 );
126 Ok(Json(response))
127}
128
129#[utoipa::path(
131 post,
132 path = "/v1/connectome/batch_neuron_operations",
133 tag = "connectome"
134)]
135pub async fn post_batch_neuron_operations(
136 State(_state): State<ApiState>,
137 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
138) -> ApiResult<Json<HashMap<String, i32>>> {
139 let mut response = HashMap::new();
140 response.insert("processed".to_string(), 0);
141 Ok(Json(response))
142}
143
144#[utoipa::path(
146 post,
147 path = "/v1/connectome/batch_synapse_operations",
148 tag = "connectome"
149)]
150pub async fn post_batch_synapse_operations(
151 State(_state): State<ApiState>,
152 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
153) -> ApiResult<Json<HashMap<String, i32>>> {
154 let mut response = HashMap::new();
155 response.insert("processed".to_string(), 0);
156 Ok(Json(response))
157}
158
159#[utoipa::path(get, path = "/v1/connectome/neuron_count", tag = "connectome")]
161pub async fn get_neuron_count(State(state): State<ApiState>) -> ApiResult<Json<i64>> {
162 let analytics = state.analytics_service.as_ref();
163 let health = analytics
164 .get_system_health()
165 .await
166 .map_err(|e| ApiError::internal(format!("{}", e)))?;
167 Ok(Json(health.neuron_count as i64))
168}
169
170#[utoipa::path(get, path = "/v1/connectome/synapse_count", tag = "connectome")]
172pub async fn get_synapse_count(State(_state): State<ApiState>) -> ApiResult<Json<i64>> {
173 Ok(Json(0))
174}
175
176#[utoipa::path(get, path = "/v1/connectome/paths", tag = "connectome")]
178pub async fn get_paths(
179 State(_state): State<ApiState>,
180 Query(_params): Query<HashMap<String, String>>,
181) -> ApiResult<Json<Vec<Vec<String>>>> {
182 Ok(Json(Vec::new()))
183}
184
185#[utoipa::path(get, path = "/v1/connectome/cumulative_stats", tag = "connectome")]
187pub async fn get_cumulative_stats(
188 State(_state): State<ApiState>,
189) -> ApiResult<Json<HashMap<String, i64>>> {
190 let mut response = HashMap::new();
191 response.insert("total_bursts".to_string(), 0);
192 Ok(Json(response))
193}
194
195#[utoipa::path(get, path = "/v1/connectome/area_details", tag = "connectome")]
197pub async fn get_area_details(
198 State(state): State<ApiState>,
199 Query(params): Query<HashMap<String, String>>,
200) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
201 let area_ids_str = params
202 .get("area_ids")
203 .ok_or_else(|| ApiError::invalid_input("area_ids required"))?;
204 let area_ids: Vec<&str> = area_ids_str.split(',').collect();
205 let connectome_service = state.connectome_service.as_ref();
206 let mut details = HashMap::new();
207 for area_id in area_ids {
208 if let Ok(area) = connectome_service.get_cortical_area(area_id).await {
209 details.insert(
210 area_id.to_string(),
211 serde_json::json!({"cortical_id": area.cortical_id}),
212 );
213 }
214 }
215 Ok(Json(details))
216}
217
218#[utoipa::path(post, path = "/v1/connectome/rebuild", tag = "connectome")]
220pub async fn post_rebuild(
221 State(_state): State<ApiState>,
222) -> ApiResult<Json<HashMap<String, String>>> {
223 Ok(Json(HashMap::from([(
224 "message".to_string(),
225 "Not yet implemented".to_string(),
226 )])))
227}
228
229#[utoipa::path(get, path = "/v1/connectome/structure", tag = "connectome")]
231pub async fn get_structure(
232 State(state): State<ApiState>,
233) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
234 let connectome_service = state.connectome_service.as_ref();
235 let areas = connectome_service
236 .list_cortical_areas()
237 .await
238 .map_err(|e| ApiError::internal(format!("{}", e)))?;
239 let mut response = HashMap::new();
240 response.insert("cortical_areas".to_string(), serde_json::json!(areas.len()));
241 Ok(Json(response))
242}
243
244#[utoipa::path(post, path = "/v1/connectome/clear", tag = "connectome")]
246pub async fn post_clear(
247 State(_state): State<ApiState>,
248) -> ApiResult<Json<HashMap<String, String>>> {
249 Ok(Json(HashMap::from([(
250 "message".to_string(),
251 "Not yet implemented".to_string(),
252 )])))
253}
254
255#[utoipa::path(get, path = "/v1/connectome/validation", tag = "connectome")]
257pub async fn get_validation(
258 State(_state): State<ApiState>,
259) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
260 let mut response = HashMap::new();
261 response.insert("valid".to_string(), serde_json::json!(true));
262 Ok(Json(response))
263}
264
265#[utoipa::path(get, path = "/v1/connectome/topology", tag = "connectome")]
267pub async fn get_topology(
268 State(_state): State<ApiState>,
269) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
270 let mut response = HashMap::new();
271 response.insert("layers".to_string(), serde_json::json!(0));
272 Ok(Json(response))
273}
274
275#[utoipa::path(post, path = "/v1/connectome/optimize", tag = "connectome")]
277pub async fn post_optimize(
278 State(_state): State<ApiState>,
279) -> ApiResult<Json<HashMap<String, String>>> {
280 Ok(Json(HashMap::from([(
281 "message".to_string(),
282 "Not yet implemented".to_string(),
283 )])))
284}
285
286#[utoipa::path(get, path = "/v1/connectome/connectivity_matrix", tag = "connectome")]
288pub async fn get_connectivity_matrix(
289 State(_state): State<ApiState>,
290) -> ApiResult<Json<HashMap<String, Vec<Vec<i32>>>>> {
291 let mut response = HashMap::new();
292 response.insert("matrix".to_string(), Vec::new());
293 Ok(Json(response))
294}
295
296#[utoipa::path(post, path = "/v1/connectome/neurons/batch", tag = "connectome")]
298pub async fn post_neurons_batch(
299 State(_state): State<ApiState>,
300 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
301) -> ApiResult<Json<HashMap<String, i32>>> {
302 let mut response = HashMap::new();
303 response.insert("processed".to_string(), 0);
304 Ok(Json(response))
305}
306
307#[utoipa::path(post, path = "/v1/connectome/synapses/batch", tag = "connectome")]
309pub async fn post_synapses_batch(
310 State(_state): State<ApiState>,
311 Json(_ops): Json<Vec<HashMap<String, serde_json::Value>>>,
312) -> ApiResult<Json<HashMap<String, i32>>> {
313 let mut response = HashMap::new();
314 response.insert("processed".to_string(), 0);
315 Ok(Json(response))
316}
317
318#[utoipa::path(
321 get,
322 path = "/v1/connectome/cortical_areas/list/summary",
323 tag = "connectome"
324)]
325pub async fn get_cortical_areas_list_summary(
326 State(state): State<ApiState>,
327) -> ApiResult<Json<Vec<HashMap<String, serde_json::Value>>>> {
328 let connectome_service = state.connectome_service.as_ref();
329 let areas = connectome_service
330 .list_cortical_areas()
331 .await
332 .map_err(|e| ApiError::internal(format!("{}", e)))?;
333 let summary: Vec<HashMap<String, serde_json::Value>> = areas
334 .iter()
335 .map(|a| {
336 let mut map = HashMap::new();
337 map.insert("cortical_id".to_string(), serde_json::json!(a.cortical_id));
338 map.insert("cortical_name".to_string(), serde_json::json!(a.name));
339 map
340 })
341 .collect();
342 Ok(Json(summary))
343}
344
345#[utoipa::path(
347 get,
348 path = "/v1/connectome/cortical_areas/list/transforming",
349 tag = "connectome"
350)]
351pub async fn get_cortical_areas_list_transforming(
352 State(_state): State<ApiState>,
353) -> ApiResult<Json<Vec<String>>> {
354 Ok(Json(Vec::new()))
355}
356
357#[utoipa::path(
359 get,
360 path = "/v1/connectome/cortical_area/{cortical_id}/neurons",
361 tag = "connectome"
362)]
363pub async fn get_cortical_area_neurons(
364 State(state): State<ApiState>,
365 Path(cortical_id): Path<String>,
366) -> ApiResult<Json<Vec<u64>>> {
367 use tracing::debug;
368
369 let neuron_service = state.neuron_service.as_ref();
370
371 let neurons = neuron_service
373 .list_neurons_in_area(&cortical_id, None)
374 .await
375 .map_err(|e| {
376 ApiError::internal(format!(
377 "Failed to get neurons in area {}: {}",
378 cortical_id, e
379 ))
380 })?;
381
382 let neuron_ids: Vec<u64> = neurons.iter().map(|n| n.id).collect();
383
384 debug!(target: "feagi-api", "GET /connectome/cortical_area/{}/neurons - found {} neurons", cortical_id, neuron_ids.len());
385 Ok(Json(neuron_ids))
386}
387
388#[utoipa::path(
390 get,
391 path = "/v1/connectome/{cortical_area_id}/synapses",
392 tag = "connectome"
393)]
394pub async fn get_area_synapses(
395 State(state): State<ApiState>,
396 Path(area_id): Path<String>,
397) -> ApiResult<Json<Vec<HashMap<String, serde_json::Value>>>> {
398 use tracing::debug;
399
400 let connectome_service = state.connectome_service.as_ref();
401 let neuron_service = state.neuron_service.as_ref();
402
403 let area_info = connectome_service
406 .get_cortical_area(&area_id)
407 .await
408 .map_err(|_| ApiError::not_found("CorticalArea", &area_id))?;
409
410 let cortical_idx = area_info.cortical_idx;
411
412 let neurons = neuron_service
414 .list_neurons_in_area(&area_id, None)
415 .await
416 .map_err(|e| ApiError::internal(format!("Failed to get neurons: {}", e)))?;
417
418 tracing::debug!(
419 target: "feagi-api",
420 "Getting synapses for area {} (idx={}): {} neurons",
421 area_id,
422 cortical_idx,
423 neurons.len()
424 );
425
426 warn!(
429 "[API] /v1/connectome/cortical_area/{}/synapses endpoint called - this acquires NPU lock!",
430 area_id
431 );
432 let manager = feagi_brain_development::ConnectomeManager::instance();
433 let manager_lock = manager.read();
434 let npu_arc = manager_lock
435 .get_npu()
436 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
437 let lock_start = std::time::Instant::now();
438 tracing::debug!("[NPU-LOCK] CONNECTOME-API: Acquiring NPU lock for synapse queries");
439 let npu_lock = npu_arc.lock().unwrap();
440 let lock_wait = lock_start.elapsed();
441 tracing::debug!(
442 "[NPU-LOCK] CONNECTOME-API: Lock acquired (waited {:.2}ms)",
443 lock_wait.as_secs_f64() * 1000.0
444 );
445
446 let mut all_synapses = Vec::new();
447 for neuron_info in &neurons {
448 let neuron_id = neuron_info.id as u32;
449 let outgoing = npu_lock.get_outgoing_synapses(neuron_id);
450
451 for (target_id, weight, psp, synapse_type) in outgoing {
452 let mut synapse_obj = HashMap::new();
453 synapse_obj.insert("source_neuron_id".to_string(), serde_json::json!(neuron_id));
454 synapse_obj.insert("target_neuron_id".to_string(), serde_json::json!(target_id));
455 synapse_obj.insert("weight".to_string(), serde_json::json!(weight));
456 synapse_obj.insert("postsynaptic_potential".to_string(), serde_json::json!(psp));
457 synapse_obj.insert("synapse_type".to_string(), serde_json::json!(synapse_type));
458 all_synapses.push(synapse_obj);
459 }
460 }
461
462 debug!(target: "feagi-api", "Found {} synapses from area {}", all_synapses.len(), area_id);
463 Ok(Json(all_synapses))
464}
465
466#[utoipa::path(
468 get,
469 path = "/v1/connectome/cortical_info/{cortical_area}",
470 tag = "connectome"
471)]
472pub async fn get_cortical_info(
473 State(state): State<ApiState>,
474 Path(cortical_area): Path<String>,
475) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
476 let connectome_service = state.connectome_service.as_ref();
477 let area = connectome_service
478 .get_cortical_area(&cortical_area)
479 .await
480 .map_err(|e| ApiError::not_found("area", &format!("{}", e)))?;
481 let mut response = HashMap::new();
482 response.insert(
483 "cortical_id".to_string(),
484 serde_json::json!(area.cortical_id),
485 );
486 response.insert("cortical_name".to_string(), serde_json::json!(area.name));
487 Ok(Json(response))
488}
489
490#[utoipa::path(
492 get,
493 path = "/v1/connectome/stats/cortical/cumulative/{cortical_area}",
494 tag = "connectome"
495)]
496pub async fn get_stats_cortical_cumulative(
497 State(_state): State<ApiState>,
498 Path(_area): Path<String>,
499) -> ApiResult<Json<HashMap<String, i64>>> {
500 let mut response = HashMap::new();
501 response.insert("total_fires".to_string(), 0);
502 Ok(Json(response))
503}
504
505#[utoipa::path(
507 get,
508 path = "/v1/connectome/neuron/{neuron_id}/properties",
509 tag = "connectome"
510)]
511pub async fn get_neuron_properties_by_id(
512 State(state): State<ApiState>,
513 Path(neuron_id): Path<u64>,
514) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
515 let connectome_service = state.connectome_service.as_ref();
516 let props = connectome_service
517 .get_neuron_properties(neuron_id)
518 .await
519 .map_err(ApiError::from)?;
520 Ok(Json(props))
521}
522
523#[utoipa::path(get, path = "/v1/connectome/neuron_properties", tag = "connectome")]
525pub async fn get_neuron_properties_query(
526 State(state): State<ApiState>,
527 Query(params): Query<HashMap<String, String>>,
528) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
529 let neuron_id: u64 = params
530 .get("neuron_id")
531 .ok_or_else(|| ApiError::invalid_input("neuron_id required"))?
532 .parse()
533 .map_err(|_| ApiError::invalid_input("neuron_id must be an integer"))?;
534
535 let connectome_service = state.connectome_service.as_ref();
536 let props = connectome_service
537 .get_neuron_properties(neuron_id)
538 .await
539 .map_err(ApiError::from)?;
540 Ok(Json(props))
541}
542
543#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
544#[into_params(parameter_in = Query)]
545pub struct NeuronPropertiesAtQuery {
546 pub cortical_id: String,
548 pub x: u32,
550 pub y: u32,
552 pub z: u32,
554}
555
556#[utoipa::path(
562 get,
563 path = "/v1/connectome/neuron_properties_at",
564 tag = "connectome",
565 params(NeuronPropertiesAtQuery)
566)]
567pub async fn get_neuron_properties_at_query(
568 State(state): State<ApiState>,
569 Query(params): Query<NeuronPropertiesAtQuery>,
570) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
571 let cortical_id = params.cortical_id;
572 let x = params.x;
573 let y = params.y;
574 let z = params.z;
575
576 let connectome_service = state.connectome_service.as_ref();
578 let area = connectome_service
579 .get_cortical_area(&cortical_id)
580 .await
581 .map_err(|_| ApiError::not_found("CorticalArea", &cortical_id))?;
582
583 let neuron_id_u32: u32 = {
588 let manager = feagi_brain_development::ConnectomeManager::instance();
590 let manager_lock = manager.read();
591 let npu_arc = manager_lock
592 .get_npu()
593 .ok_or_else(|| ApiError::internal("NPU not initialized"))?;
594 let npu_lock = npu_arc.lock().unwrap();
595
596 npu_lock
597 .get_neuron_id_at_coordinate(area.cortical_idx, x, y, z)
598 .ok_or_else(|| {
599 ApiError::not_found(
600 "Neuron",
601 &format!("cortical_id={} x={} y={} z={}", cortical_id, x, y, z),
602 )
603 })?
604 };
605
606 let mut props = connectome_service
607 .get_neuron_properties(neuron_id_u32 as u64)
608 .await
609 .map_err(ApiError::from)?;
610
611 props.insert(
613 "neuron_id".to_string(),
614 serde_json::json!(neuron_id_u32 as u64),
615 );
616 props.insert("cortical_id".to_string(), serde_json::json!(cortical_id));
617 props.insert(
618 "cortical_idx".to_string(),
619 serde_json::json!(area.cortical_idx),
620 );
621
622 Ok(Json(props))
623}
624
625#[utoipa::path(get, path = "/v1/connectome/area_neurons", tag = "connectome")]
627pub async fn get_area_neurons_query(
628 State(_state): State<ApiState>,
629 Query(_params): Query<HashMap<String, String>>,
630) -> ApiResult<Json<Vec<u64>>> {
631 Ok(Json(Vec::new()))
632}
633
634#[utoipa::path(
636 get,
637 path = "/v1/connectome/fire_queue/{cortical_area}",
638 tag = "connectome"
639)]
640pub async fn get_fire_queue_area(
641 State(_state): State<ApiState>,
642 Path(_area): Path<String>,
643) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
644 Ok(Json(HashMap::new()))
645}
646
647#[utoipa::path(get, path = "/v1/connectome/plasticity", tag = "connectome")]
649pub async fn get_plasticity_info(
650 State(_state): State<ApiState>,
651) -> ApiResult<Json<HashMap<String, bool>>> {
652 let mut response = HashMap::new();
653 response.insert("enabled".to_string(), true);
654 Ok(Json(response))
655}
656
657#[utoipa::path(get, path = "/v1/connectome/path", tag = "connectome")]
659pub async fn get_path_query(
660 State(_state): State<ApiState>,
661 Query(_params): Query<HashMap<String, String>>,
662) -> ApiResult<Json<Vec<Vec<String>>>> {
663 Ok(Json(Vec::new()))
664}
665
666#[utoipa::path(get, path = "/v1/connectome/download", tag = "connectome")]
668pub async fn get_download_connectome(
669 State(state): State<ApiState>,
670) -> ApiResult<Json<serde_json::Value>> {
671 let snapshot = state
673 .connectome_service
674 .export_connectome()
675 .await
676 .map_err(ApiError::from)?;
677
678 let json_value = serde_json::to_value(&snapshot)
680 .map_err(|e| ApiError::internal(format!("Failed to serialize connectome: {}", e)))?;
681
682 Ok(Json(json_value))
683}
684
685#[utoipa::path(
687 get,
688 path = "/v1/connectome/download-cortical-area/{cortical_area}",
689 tag = "connectome"
690)]
691pub async fn get_download_cortical_area(
692 State(_state): State<ApiState>,
693 Path(_area): Path<String>,
694) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
695 Ok(Json(HashMap::new()))
696}
697
698#[utoipa::path(post, path = "/v1/connectome/upload", tag = "connectome")]
700pub async fn post_upload_connectome(
701 State(state): State<ApiState>,
702 Json(data): Json<serde_json::Value>,
703) -> ApiResult<Json<HashMap<String, String>>> {
704 let snapshot: feagi_npu_neural::types::connectome::ConnectomeSnapshot =
706 serde_json::from_value(data).map_err(|e| {
707 ApiError::invalid_input(format!("Invalid connectome snapshot format: {}", e))
708 })?;
709
710 state
712 .connectome_service
713 .import_connectome(snapshot)
714 .await
715 .map_err(ApiError::from)?;
716
717 Ok(Json(HashMap::from([(
718 "message".to_string(),
719 "Connectome imported successfully".to_string(),
720 )])))
721}
722
723#[utoipa::path(post, path = "/v1/connectome/upload-cortical-area", tag = "connectome")]
725pub async fn post_upload_cortical_area(
726 State(_state): State<ApiState>,
727 Json(_data): Json<HashMap<String, serde_json::Value>>,
728) -> ApiResult<Json<HashMap<String, String>>> {
729 Ok(Json(HashMap::from([(
730 "message".to_string(),
731 "Upload not yet implemented".to_string(),
732 )])))
733}
734
735#[utoipa::path(
737 get,
738 path = "/v1/connectome/cortical_area/list/types",
739 tag = "connectome",
740 responses(
741 (status = 200, description = "List of cortical types with their cortical IDs and group IDs", body = HashMap<String, serde_json::Value>),
742 (status = 500, description = "Internal server error")
743 )
744)]
745pub async fn get_cortical_area_list_types(
746 State(state): State<ApiState>,
747) -> ApiResult<Json<HashMap<String, serde_json::Value>>> {
748 use std::collections::{HashMap, HashSet};
750
751 let connectome_service = state.connectome_service.as_ref();
752 let areas = connectome_service
753 .list_cortical_areas()
754 .await
755 .map_err(|e| ApiError::internal(format!("Failed to list cortical areas: {}", e)))?;
756
757 fn get_cortical_type_title(subtype: &str) -> String {
759 match subtype {
760 "svi" => "segmented vision".to_string(),
761 "mot" => "motor".to_string(),
762 "bat" => "battery".to_string(),
763 "mis" => "miscellaneous".to_string(),
764 "gaz" => "gaze control".to_string(),
765 "pow" => "power".to_string(),
766 "dea" => "death".to_string(),
767 _ => {
768 if !subtype.is_empty() {
770 let mut chars = subtype.chars();
771 let first = chars.next().unwrap().to_uppercase().collect::<String>();
772 let rest: String = chars.collect();
773 format!("{}{}", first, rest)
774 } else {
775 "unknown".to_string()
776 }
777 }
778 }
779 }
780
781 let mut type_map: HashMap<String, (String, Vec<String>, HashSet<u8>)> = HashMap::new();
783
784 for area in areas {
785 use feagi_structures::genomic::cortical_area::CorticalID;
787 if let Ok(cortical_id_typed) = CorticalID::try_from_base_64(&area.cortical_id) {
788 if let Some(subtype) = cortical_id_typed.extract_subtype() {
790 let entry = type_map.entry(subtype.clone()).or_insert_with(|| {
791 let title = get_cortical_type_title(&subtype);
792 (title, Vec::new(), HashSet::new())
793 });
794
795 entry.1.push(area.cortical_id.clone());
797
798 if let Some(group_id) = cortical_id_typed.extract_group_id() {
800 entry.2.insert(group_id);
801 }
802 }
803 }
804 }
805
806 let mut response: HashMap<String, serde_json::Value> = HashMap::new();
808 for (subtype, (title, mut cortical_ids, group_ids)) in type_map {
809 cortical_ids.sort();
811
812 let mut group_ids_vec: Vec<u8> = group_ids.into_iter().collect();
813 group_ids_vec.sort_unstable();
814
815 response.insert(
816 subtype,
817 serde_json::json!({
818 "title": title,
819 "cortical_ids": cortical_ids,
820 "group_ids": group_ids_vec
821 }),
822 );
823 }
824
825 Ok(Json(response))
826}