1+ /*
2+ * Copyright 2025 LiteFarm.org
3+ * This file is part of LiteFarm.
4+ *
5+ * LiteFarm is free software: you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation, either version 3 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * LiteFarm is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
14+ */
15+
16+ -- @description: Return new JSONB = base + (key→payload) if payload IS NOT NULL. A helper function to build the export data object
17+ CREATE OR REPLACE FUNCTION jsonb_merge (
18+ base JSONB,
19+ key TEXT ,
20+ payload JSONB
21+ ) RETURNS JSONB
22+ LANGUAGE sql AS $$
23+ SELECT CASE
24+ WHEN payload IS NULL THEN base
25+ ELSE base || jsonb_build_object(key, payload)
26+ END;
27+ $$;
28+
29+ -- @description: Build a JSONB payload containing every user- and farm-scoped record for export
30+ CREATE OR REPLACE FUNCTION export_user_data ()
31+ RETURNS JSONB
32+ LANGUAGE plpgsql
33+ AS $$
34+ DECLARE
35+ target_user_id VARCHAR := ' user_id_here' ;
36+ user_data user_data_collections;
37+ farm_data farm_data_collections;
38+ farm_count INTEGER ;
39+
40+ task_tables RECORD;
41+ task_prod_tables RECORD;
42+ pmp_tables RECORD;
43+ mp_tables RECORD;
44+ plan_repetition_tables RECORD;
45+ figure_tables RECORD;
46+ animal_tables RECORD;
47+ animal_batch_tables RECORD;
48+ location_tables RECORD;
49+ secondary_tables RECORD;
50+ farm_tables RECORD;
51+ created_by_tables RECORD;
52+ user_tables RECORD;
53+
54+ farm_json JSONB;
55+ table_data JSONB;
56+ full_export JSONB := ' {}' ::jsonb;
57+ BEGIN
58+ user_data := get_user_data(target_user_id);
59+
60+ FOREACH farm_data IN ARRAY user_data .farms LOOP
61+ farm_json := ' {}' ::jsonb;
62+
63+ -- ==== TASK PRODUCT-SCOPED TABLES ====
64+ FOR task_prod_tables IN SELECT * FROM get_task_product_tables() LOOP
65+ EXECUTE format(
66+ ' SELECT json_agg(to_jsonb(t)) FROM %I t WHERE t.task_products_id = ANY($1)' ,
67+ task_prod_tables .table_name
68+ )
69+ INTO table_data
70+ USING farm_data .task_products_ids ;
71+
72+ farm_json := jsonb_merge(farm_json, task_prod_tables .table_name , table_data);
73+ END LOOP;
74+
75+ -- ==== TASK‐SCOPED TABLES ====
76+ FOR task_tables IN SELECT * FROM get_task_tables() LOOP
77+ EXECUTE format(
78+ ' SELECT json_agg(to_jsonb(t)) FROM %I t WHERE t.task_id = ANY($1)' ,
79+ task_tables .table_name
80+ )
81+ INTO table_data
82+ USING farm_data .task_ids ;
83+
84+ farm_json := jsonb_merge(farm_json, task_tables .table_name , table_data);
85+ END LOOP;
86+
87+ -- ==== PLANTING MANAGEMENT PLAN TABLES ====
88+ FOR pmp_tables IN SELECT * FROM get_pmp_tables() LOOP
89+ EXECUTE format(
90+ ' SELECT json_agg(to_jsonb(p)) FROM %I p ' ||
91+ ' WHERE p.planting_management_plan_id = ANY($1)' ,
92+ pmp_tables .table_name
93+ )
94+ INTO table_data
95+ USING farm_data .pmp_ids ;
96+
97+ farm_json := jsonb_merge(farm_json, pmp_tables .table_name , table_data);
98+ END LOOP;
99+
100+ -- ==== MANAGEMENT PLAN TABLES ====
101+ FOR mp_tables IN SELECT * FROM get_management_plan_tables() LOOP
102+ EXECUTE format(
103+ ' SELECT json_agg(to_jsonb(m)) FROM %I m ' ||
104+ ' WHERE m.management_plan_id = ANY($1)' ,
105+ mp_tables .table_name
106+ )
107+ INTO table_data
108+ USING farm_data .management_plan_ids ;
109+
110+ farm_json := jsonb_merge(farm_json, mp_tables .table_name , table_data);
111+ END LOOP;
112+
113+ -- ==== MANAGEMENT PLAN GROUP TABLES ====
114+ FOR plan_repetition_tables IN SELECT * FROM get_mp_repetition_tables() LOOP
115+ EXECUTE format(
116+ ' SELECT json_agg(to_jsonb(r)) FROM %I r ' ||
117+ ' WHERE r.management_plan_group_id = ANY($1)' ,
118+ plan_repetition_tables .table_name
119+ )
120+ INTO table_data
121+ USING farm_data .management_plan_group_ids ;
122+
123+ farm_json := jsonb_merge(farm_json, plan_repetition_tables .table_name , table_data);
124+ END LOOP;
125+
126+ -- ==== ANIMALS ====
127+ FOR animal_tables IN SELECT * FROM get_animal_tables() LOOP
128+ EXECUTE format(
129+ ' SELECT json_agg(to_jsonb(a)) FROM %I a ' ||
130+ ' WHERE a.animal_id = ANY($1)' ,
131+ animal_tables .table_name
132+ )
133+ INTO table_data
134+ USING farm_data .animal_ids ;
135+
136+ farm_json := jsonb_merge(farm_json, animal_tables .table_name , table_data);
137+ END LOOP;
138+
139+ FOR animal_batch_tables IN SELECT * FROM get_animal_batch_tables() LOOP
140+ EXECUTE format(
141+ ' SELECT json_agg(to_jsonb(ab)) FROM %I ab ' ||
142+ ' WHERE ab.animal_batch_id = ANY($1)' ,
143+ animal_batch_tables .table_name
144+ )
145+ INTO table_data
146+ USING farm_data .animal_batch_ids ;
147+
148+ farm_json := jsonb_merge(farm_json, animal_batch_tables .table_name , table_data);
149+ END LOOP;
150+
151+ -- ==== LOCATIONS ====
152+ FOR figure_tables IN SELECT * FROM get_figure_tables() LOOP
153+ EXECUTE format(
154+ ' SELECT json_agg(to_jsonb(f)) FROM %I f ' ||
155+ ' WHERE f.figure_id = ANY($1)' ,
156+ figure_tables .table_name
157+ )
158+ INTO table_data
159+ USING farm_data .figure_ids ;
160+
161+ farm_json := jsonb_merge(farm_json, figure_tables .table_name , table_data);
162+ END LOOP;
163+
164+ FOR location_tables IN SELECT * FROM get_location_export_tables() LOOP
165+ EXECUTE format(
166+ ' SELECT json_agg(to_jsonb(l)) FROM %I l ' ||
167+ ' WHERE l.location_id = ANY($1)' ,
168+ location_tables .table_name
169+ )
170+ INTO table_data
171+ USING farm_data .location_ids ;
172+
173+ farm_json := jsonb_merge(farm_json, location_tables .table_name , table_data);
174+ END LOOP;
175+
176+ -- ==== TABLES REQUIRING SECONDARY JOIN FOR FARM_ID ====
177+ FOR secondary_tables IN SELECT * FROM get_secondary_tables() LOOP
178+ EXECUTE format(
179+ ' SELECT json_agg(to_jsonb(c)) ' ||
180+ ' FROM %I c JOIN %I p ON c.%I = p.%I ' ||
181+ ' WHERE p.farm_id = $1' ,
182+ secondary_tables .child_table ,
183+ secondary_tables .join_table ,
184+ secondary_tables .join_key ,
185+ secondary_tables .join_key
186+ )
187+ INTO table_data
188+ USING farm_data .farm_id ;
189+
190+ farm_json := jsonb_merge(farm_json, secondary_tables .child_table , table_data);
191+ END LOOP;
192+
193+ -- ==== REMAINING FARM-SCOPED TABLES ====
194+ FOR farm_tables IN SELECT * FROM get_farm_export_tables() LOOP
195+ EXECUTE format(
196+ ' SELECT json_agg(to_jsonb(fm)) ' ||
197+ ' FROM %I fm WHERE fm.farm_id = $1' ,
198+ farm_tables .table_name
199+ )
200+ INTO table_data
201+ USING farm_data .farm_id ;
202+
203+ farm_json := jsonb_merge(farm_json, farm_tables .table_name , table_data);
204+ END LOOP;
205+
206+ -- append this farm into the "farms" array
207+ full_export := jsonb_merge(
208+ full_export,
209+ ' farms' ,
210+ COALESCE(full_export- > ' farms' ,' []' ::jsonb) || farm_json
211+ );
212+
213+ END LOOP;
214+
215+ -- ==== USER DATA NOT SPECIFIC TO FARM ====
216+ FOR created_by_tables IN SELECT * FROM get_created_by_user_tables() LOOP
217+ EXECUTE format(
218+ ' SELECT json_agg(to_jsonb(cb)) ' ||
219+ ' FROM %I cb WHERE cb.created_by_user_id = $1' ,
220+ created_by_tables .table_name
221+ )
222+ INTO table_data
223+ USING target_user_id;
224+
225+ full_export := jsonb_merge(full_export, created_by_tables .table_name , table_data);
226+ END LOOP;
227+
228+ FOR user_tables IN SELECT * FROM get_user_scoped_tables() LOOP
229+ EXECUTE format(
230+ ' SELECT json_agg(to_jsonb(u)) ' ||
231+ ' FROM %I u WHERE u.user_id = $1' ,
232+ user_tables .table_name
233+ )
234+ INTO table_data
235+ USING target_user_id;
236+
237+ full_export := jsonb_merge(full_export, user_tables .table_name , table_data);
238+ END LOOP;
239+
240+ -- Format export object
241+ RETURN jsonb_build_object(
242+ ' user_id' , target_user_id,
243+ ' exported_at' , to_char(now(), ' YYYY-MM-DD"T"HH24:MI:SS"Z"' ),
244+ ' export_type' , ' all_user_data' ,
245+ ' data' , full_export
246+ );
247+
248+ END $$;
249+
250+ SELECT export_user_data() as export;
0 commit comments