qualia_client_core/wellfair/api/
welfare_work.rs1use super::super::blob_store::BlobStore;
4use super::super::journal::JournalEntry;
5use qualia_cooperative_core::work_item::{
6 build_work_item_envelope, build_work_item_status_envelope, derive_board,
7 parse_work_item_status_summary, parse_work_item_summary, work_item_status_summary,
8 work_item_summary, BoardColumn, WorkItem, WorkItemStatusEvent,
9};
10use wellfare_core::authority_attestation::{
11 authority_attestation_summary, build_authority_attestation_envelope, AgentInCapacity,
12 Authority, AuthorityAttestation, Representation,
13};
14use wellfare_core::welfare_support::{
15 build_assistance_need_envelope, build_government_letter_envelope,
16 build_welfare_stream_envelope, AssistanceNeed, GovernmentLetter, StreamStatus, Urgency,
17 WelfareStream,
18};
19
20use super::*;
21
22impl WebizenHostApi {
23 pub fn add_assistance_need(
26 &mut self,
27 category: &str,
28 description: &str,
29 urgency: Urgency,
30 ) -> Result<JournalEntry, String> {
31 let mut need = AssistanceNeed::new(category, description, Self::now_unix() as u32);
32 need.urgency = urgency;
33 let hash =
34 Self::payload_hash_hex(&serde_json::to_string(&need).map_err(|e| e.to_string())?);
35 let asserted = Self::now_unix() as u32;
36 let envelope = build_assistance_need_envelope(
37 &need,
38 &self.owner_did,
39 &self.author_did,
40 asserted,
41 Some(hash),
42 );
43 let summary = wellfare_core::welfare_support::assistance_need_summary(&need);
44 self.submit_record_with_summary(QAPP_WELFARE, envelope, SOURCE_WELFARE, Some(summary))?;
45 self.finalize_batch().ok();
46 self.latest_journal_entry()
47 }
48
49 pub fn add_welfare_stream(
50 &mut self,
51 program_name: &str,
52 reference: Option<String>,
53 status: StreamStatus,
54 ) -> Result<JournalEntry, String> {
55 let mut stream = WelfareStream::new(program_name, Self::now_unix() as u32);
56 stream.reference = reference.filter(|s| !s.is_empty());
57 stream.status = status;
58 let hash =
59 Self::payload_hash_hex(&serde_json::to_string(&stream).map_err(|e| e.to_string())?);
60 let asserted = Self::now_unix() as u32;
61 let envelope = build_welfare_stream_envelope(
62 &stream,
63 &self.owner_did,
64 &self.author_did,
65 asserted,
66 Some(hash),
67 );
68 let summary = wellfare_core::welfare_support::welfare_stream_summary(&stream);
69 self.submit_record_with_summary(QAPP_WELFARE, envelope, SOURCE_WELFARE, Some(summary))?;
70 self.finalize_batch().ok();
71 self.latest_journal_entry()
72 }
73
74 pub fn add_government_letter(
75 &mut self,
76 sender: &str,
77 subject: &str,
78 action_required: bool,
79 ) -> Result<JournalEntry, String> {
80 let mut letter = GovernmentLetter::new(sender, subject, Self::now_unix() as u32);
81 letter.action_required = action_required;
82 let asserted = Self::now_unix() as u32;
83 let envelope =
84 build_government_letter_envelope(&letter, &self.owner_did, &self.author_did, asserted);
85 let summary = wellfare_core::welfare_support::government_letter_summary(&letter);
86 self.submit_record_with_summary(QAPP_WELFARE, envelope, SOURCE_WELFARE, Some(summary))?;
87 self.finalize_batch().ok();
88 self.latest_journal_entry()
89 }
90
91 #[allow(clippy::too_many_arguments)]
96 pub fn add_authority_attestation(
97 &mut self,
98 authority_type: &str,
99 authority_label: &str,
100 jurisdiction: Option<String>,
101 department: Option<String>,
102 agent_name: Option<String>,
103 agent_capacity: Option<String>,
104 representation: &str,
105 subject: &str,
106 statement: &str,
107 action_required: bool,
108 ) -> Result<JournalEntry, String> {
109 let issued = Self::now_unix() as u32;
110 let authority = Authority::new(authority_type, authority_label);
111 let representation = match representation.to_ascii_lowercase().as_str() {
112 "credential" => Representation::Credential,
113 "pdf_with_embedded_credential" | "both" => Representation::PdfWithEmbeddedCredential,
114 _ => Representation::Pdf,
115 };
116 let mut att = AuthorityAttestation::new(authority, subject, statement, issued)
117 .with_representation(representation)
118 .with_action_required(action_required);
119 if let Some(j) = jurisdiction {
120 att = att.with_jurisdiction(j);
121 }
122 if let Some(d) = department {
123 att = att.with_department(d);
124 }
125 if let (Some(n), Some(c)) = (agent_name, agent_capacity) {
126 att = att.with_agent(AgentInCapacity::new(n, c));
127 }
128 let envelope =
129 build_authority_attestation_envelope(&att, &self.owner_did, &self.author_did, issued);
130 let summary = authority_attestation_summary(&att);
131 self.submit_record_with_summary(QAPP_WELFARE, envelope, SOURCE_WELFARE, Some(summary))?;
132 self.finalize_batch().ok();
133 self.latest_journal_entry()
134 }
135
136 pub fn add_government_letter_attachment(
139 &mut self,
140 sender: &str,
141 subject: &str,
142 action_required: bool,
143 bytes: &[u8],
144 ) -> Result<JournalEntry, String> {
145 let hash = BlobStore::open(&self.storage_root)
146 .and_then(|store| store.put(bytes))
147 .map_err(|e| e.to_string())?;
148 let mut letter = GovernmentLetter::new(sender, subject, Self::now_unix() as u32);
149 letter.action_required = action_required;
150 letter.attachment_blob_hash = Some(hash);
151 let asserted = Self::now_unix() as u32;
152 let envelope =
153 build_government_letter_envelope(&letter, &self.owner_did, &self.author_did, asserted);
154 let summary = wellfare_core::welfare_support::government_letter_summary(&letter);
155 self.submit_record_with_summary(QAPP_WELFARE, envelope, SOURCE_WELFARE, Some(summary))?;
156 self.finalize_batch().ok();
157 self.latest_journal_entry()
158 }
159
160 pub fn list_welfare_records(&self, limit: usize) -> Result<Vec<JournalEntry>, String> {
162 Ok(self
163 .list_health_records(limit)?
164 .into_iter()
165 .filter(|e| {
166 matches!(
167 e.kind.as_str(),
168 "assistance_need" | "welfare_stream" | "government_letter"
169 )
170 })
171 .collect())
172 }
173
174 pub fn add_work_item(&mut self, item: &WorkItem) -> Result<JournalEntry, String> {
182 let asserted = Self::now_unix() as u32;
183 let envelope = build_work_item_envelope(item, &self.owner_did, &self.author_did, asserted);
184 let summary = work_item_summary(item);
185 self.submit_record_with_summary(
186 QAPP_COOPERATIVE,
187 envelope,
188 SOURCE_COOPERATIVE,
189 Some(summary),
190 )?;
191 self.finalize_batch().ok();
192 self.latest_journal_entry()
193 }
194
195 pub fn add_work_item_status(
198 &mut self,
199 event: &WorkItemStatusEvent,
200 ) -> Result<JournalEntry, String> {
201 let asserted = Self::now_unix() as u32;
202 let envelope =
203 build_work_item_status_envelope(event, &self.owner_did, &self.author_did, asserted);
204 let summary = work_item_status_summary(event);
205 self.submit_record_with_summary(
206 QAPP_COOPERATIVE,
207 envelope,
208 SOURCE_COOPERATIVE,
209 Some(summary),
210 )?;
211 self.finalize_batch().ok();
212 self.latest_journal_entry()
213 }
214
215 pub fn list_work_items(&self, limit: usize) -> Result<Vec<JournalEntry>, String> {
216 self.list_journal_by_kind("work_item", limit)
217 }
218
219 pub fn work_item_board(
222 &self,
223 project_id: &str,
224 limit: usize,
225 ) -> Result<Vec<BoardColumn>, String> {
226 let rows = self.list_health_records(limit)?;
227 let mut items = Vec::new();
228 let mut events = Vec::new();
229 for row in rows {
230 let Some(ref summary) = row.summary else {
231 continue;
232 };
233 match row.kind.as_str() {
234 "work_item" => {
235 if let Some(item) = parse_work_item_summary(summary) {
236 if item.project_id == project_id {
237 items.push(item);
238 }
239 }
240 }
241 "work_item_status" => {
242 if let Some(ev) = parse_work_item_status_summary(summary) {
243 events.push(ev);
244 }
245 }
246 _ => {}
247 }
248 }
249 Ok(derive_board(&items, &events))
250 }
251}