qualia_client_core/wellfair/api/
mod.rs1use super::policy::PolicyDecisionService;
2use super::vault::VaultService;
3use ed25519_dalek::SigningKey;
4use wellfare_core::projects::Contribution;
5
6const QAPP_SHELL: &str = "wellfair-shell";
7const QAPP_LIFE: &str = "wellfair-life";
8const QAPP_WELLBEING: &str = "wellfair-wellbeing";
9const QAPP_FINANCE: &str = "wellfair-finance";
10const QAPP_PROJECTS: &str = "wellfair-projects";
11const QAPP_CREDENTIALS: &str = "wellfair-credentials";
12const QAPP_CLINICAL: &str = "wellfair-clinical";
13const QAPP_WELFARE: &str = "wellfair-welfare";
14const SOURCE_PERSONAL: &str = "wellfair:personal";
15const SOURCE_LIFE: &str = "wellfair:life";
16const SOURCE_WELLBEING: &str = "wellfair:wellbeing";
17const SOURCE_FINANCE: &str = "wellfair:finance";
18const SOURCE_PROJECTS: &str = "wellfair:projects";
19const SOURCE_CREDENTIALS: &str = "wellfair:credentials";
20const SOURCE_CLINICAL: &str = "wellfair:clinical";
21const SOURCE_WELFARE: &str = "wellfair:welfare";
22const QAPP_COOPERATIVE: &str = "qualia-cooperative";
23const SOURCE_COOPERATIVE: &str = "qualia:cooperative";
24const QAPP_GUARDIANSHIP: &str = "wellfair-guardianship";
25const SOURCE_GUARDIANSHIP: &str = "wellfair:guardianship";
26
27fn contribution_from_summary(
31 id: String,
32 summary: &str,
33 occurred_at_unix: u32,
34) -> Option<Contribution> {
35 let v: serde_json::Value = serde_json::from_str(summary).ok()?;
36 Some(Contribution {
37 id,
38 project_id: v
39 .get("project_id")
40 .and_then(|x| x.as_str())
41 .unwrap_or_default()
42 .to_string(),
43 contributor_did: v
44 .get("contributor_did")
45 .and_then(|x| x.as_str())
46 .unwrap_or_default()
47 .to_string(),
48 description: String::new(),
49 effort_minutes: v
50 .get("effort_minutes")
51 .and_then(|x| x.as_u64())
52 .unwrap_or(0) as u32,
53 capital_cents: v.get("capital_cents").and_then(|x| x.as_u64()).unwrap_or(0),
54 roi_multiplier: v
55 .get("roi_multiplier")
56 .and_then(|x| x.as_f64())
57 .map(|f| f as f32)
58 .unwrap_or(1.0),
59 privacy_level: Default::default(),
60 occurred_at_unix,
61 predecessor_id: None,
62 })
63}
64
65pub(crate) fn library_summary(e: &super::hypermedia_store::LibraryEntry) -> serde_json::Value {
67 serde_json::json!({
68 "asset_uri": e.asset_uri,
69 "media_type": e.media_type,
70 "topics": e.topics,
71 "projects": e.projects,
72 "purposes": e.purposes,
73 "place": e.place,
74 "occurred_at": e.occurred_at,
75 "lat": e.lat,
76 "lon": e.lon,
77 "flags": e.flags,
78 "ingested_unix": e.ingested_unix,
79 "excerpt": e.excerpt,
80 "sensitivity": e.sensitivity,
81 "section": e.section,
82 "commons_visibility": e.commons_visibility,
83 "is_secret": e.is_secret(),
84 "cml_signals": e.cml_signals,
85 "cml_concept_count": e.cml_concept_count,
86 "cml_n3_chars": e.cml_n3.len(),
87 "quin_count": e.quins.len(),
88 "cof_segment_count": e.cof_segment_count,
89 "cof_segment_index": e.cof_segment_index,
90 "cof_profile": e.cof_profile,
91 "cof_html_chars": e.cof_html.len(),
92 "has_cof": !e.cof_html.is_empty(),
93 })
94}
95
96#[derive(Debug, Clone, Default)]
101pub struct ManualFacets {
102 pub occurred_at: Option<i64>,
103 pub place_label: Option<String>,
104 pub lat: Option<f32>,
105 pub lon: Option<f32>,
106 pub projects: Vec<String>,
107 pub purposes: Vec<String>,
108 pub sensitivity: Option<String>,
110 pub section: Option<String>,
112 pub commons_visibility: Option<String>,
114}
115
116impl ManualFacets {
117 fn is_empty(&self) -> bool {
118 self.occurred_at.is_none()
119 && self.place_label.is_none()
120 && self.lat.is_none()
121 && self.projects.is_empty()
122 && self.purposes.is_empty()
123 && self.sensitivity.is_none()
124 && self.section.is_none()
125 && self.commons_visibility.is_none()
126 }
127}
128
129fn decode_hex(s: &str) -> Result<Vec<u8>, String> {
132 let s = s.trim();
133 if s.len() % 2 != 0 {
134 return Err("odd-length hex".to_string());
135 }
136 let val = |c: u8| -> Result<u8, String> {
137 match c {
138 b'0'..=b'9' => Ok(c - b'0'),
139 b'a'..=b'f' => Ok(c - b'a' + 10),
140 b'A'..=b'F' => Ok(c - b'A' + 10),
141 _ => Err(format!("non-hex byte {:#x}", c)),
142 }
143 };
144 let b = s.as_bytes();
145 let mut out = Vec::with_capacity(b.len() / 2);
146 let mut i = 0;
147 while i < b.len() {
148 out.push((val(b[i])? << 4) | val(b[i + 1])?);
149 i += 2;
150 }
151 Ok(out)
152}
153
154pub fn parse_anatomy_model(s: &str) -> Result<wellfare_core::anatomy::AnatomyModel, String> {
156 match s.trim().to_ascii_lowercase().as_str() {
157 "male" | "m" | "xy" => Ok(wellfare_core::anatomy::AnatomyModel::Male),
158 "female" | "f" | "xx" => Ok(wellfare_core::anatomy::AnatomyModel::Female),
159 _ => Err(format!(
160 "unknown anatomy model '{s}' (expected male/female)"
161 )),
162 }
163}
164
165pub struct WebizenHostApi {
167 vault: VaultService,
168 policy: PolicyDecisionService,
169 signing_key: SigningKey,
170 owner_did: String,
171 author_did: String,
172 storage_root: std::path::PathBuf,
173}
174
175mod accountability;
176mod anatomy;
177mod host_core;
178mod library;
179pub use library::{
181 library_stats_at, list_library_section_at, query_library_faceted_at, search_library_at,
182 search_library_text_at, search_library_time_at,
183};
184mod agency;
185mod backup_clinical;
186mod coop;
187mod disclosure;
188mod encryption;
189mod guardianship;
190mod pwa;
191mod sanctuary_vault;
192mod sync;
193mod types;
194mod welfare_work;
195
196pub use types::*;