stat2025 commited on
Commit
80b2254
·
verified ·
1 Parent(s): a7c5afa

Upload Code.gs

Browse files
Files changed (1) hide show
  1. apps-script/Code.gs +137 -35
apps-script/Code.gs CHANGED
@@ -21,6 +21,7 @@ const HEADERS = [
21
  "رابط مجلد المنشأة",
22
  "عدد الصور",
23
  "مفتاح العينة",
 
24
  ];
25
 
26
  function doGet(event) {
@@ -28,7 +29,7 @@ function doGet(event) {
28
  const params = (event && event.parameter) || {};
29
  const action = String(params.action || "health");
30
  if (action === "health") {
31
- return jsonResponse({ ok: true, service: "ICS2 Documentation", version: 2 });
32
  }
33
 
34
  if (action === "status") {
@@ -36,7 +37,7 @@ function doGet(event) {
36
  return jsonResponse({
37
  ok: true,
38
  generatedAt: new Date().toISOString(),
39
- records: getDocumentationRecords(false),
40
  });
41
  }
42
 
@@ -72,40 +73,11 @@ function doPost(event) {
72
 
73
  const sheet = getSheet();
74
  ensureHeaders(sheet);
75
- const existingIds = sheet
76
- .getRange(1, 1, Math.max(sheet.getLastRow(), 1), 1)
77
- .getDisplayValues()
78
- .flat();
79
- if (existingIds.includes(request.documentationId)) {
80
- return jsonResponse({ ok: true, duplicate: true, documentationId: request.documentationId });
81
  }
82
 
83
- const folder = getEvidenceFolder(request);
84
- const photoUrls = request.photos.map((photo, index) => savePhoto(folder, photo, request, index));
85
- sheet.appendRow([
86
- request.documentationId,
87
- new Date(),
88
- safeText(request.researcher),
89
- safeText(request.establishmentName),
90
- safeText(request.commercialRecord),
91
- safeText(request.contractNumber) || "لا يوجد رقم عقد",
92
- safeText(request.city),
93
- safeText(request.fieldStatus),
94
- safeText(request.statement),
95
- photoUrls.join("\n"),
96
- folder.getUrl(),
97
- photoUrls.length,
98
- safeText(request.sampleKey),
99
- ]);
100
-
101
- return jsonResponse({
102
- ok: true,
103
- documentationId: request.documentationId,
104
- sampleKey: safeText(request.sampleKey),
105
- documentedAt: new Date().toISOString(),
106
- folderUrl: folder.getUrl(),
107
- photoUrls,
108
- });
109
  } catch (error) {
110
  return jsonResponse({ ok: false, error: String(error.message || error) });
111
  } finally {
@@ -115,6 +87,132 @@ function doPost(event) {
115
  }
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  function validateResearcherAccess(accessCode) {
119
  const expected = PropertiesService.getScriptProperties().getProperty("ACCESS_CODE") || "20302030";
120
  if (String(accessCode || "") !== expected) throw new Error("غير مصرح بالوصول.");
@@ -160,6 +258,7 @@ function getDocumentationRecords(includeDetails) {
160
  fieldStatus: safeText(row[7]),
161
  photoCount: Number(row[11]) || splitUrls(row[9]).length,
162
  sampleKey: safeText(row[12]),
 
163
  };
164
  if (!includeDetails) return base;
165
  return Object.assign(base, {
@@ -168,7 +267,9 @@ function getDocumentationRecords(includeDetails) {
168
  folderUrl: safeText(row[10]),
169
  });
170
  })
171
- .sort((a, b) => String(b.documentedAt).localeCompare(String(a.documentedAt)));
 
 
172
  }
173
 
174
  function splitUrls(value) {
@@ -193,6 +294,7 @@ function ensureHeaders(sheet) {
193
  return;
194
  }
195
  if (!current[12]) sheet.getRange(1, 13).setValue(HEADERS[12]);
 
196
  }
197
 
198
  function getEvidenceFolder(request) {
 
21
  "رابط مجلد المنشأة",
22
  "عدد الصور",
23
  "مفتاح العينة",
24
+ "آخر تعديل",
25
  ];
26
 
27
  function doGet(event) {
 
29
  const params = (event && event.parameter) || {};
30
  const action = String(params.action || "health");
31
  if (action === "health") {
32
+ return jsonResponse({ ok: true, service: "ICS2 Documentation", version: 4 });
33
  }
34
 
35
  if (action === "status") {
 
37
  return jsonResponse({
38
  ok: true,
39
  generatedAt: new Date().toISOString(),
40
+ records: getDocumentationRecords(true),
41
  });
42
  }
43
 
 
73
 
74
  const sheet = getSheet();
75
  ensureHeaders(sheet);
76
+ if (request.action === "updateEvidence") {
77
+ return jsonResponse(updateEvidence(sheet, request));
 
 
 
 
78
  }
79
 
80
+ return jsonResponse(createEvidence(sheet, request));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  } catch (error) {
82
  return jsonResponse({ ok: false, error: String(error.message || error) });
83
  } finally {
 
87
  }
88
  }
89
 
90
+ function createEvidence(sheet, request) {
91
+ const existingIds = sheet
92
+ .getRange(1, 1, Math.max(sheet.getLastRow(), 1), 1)
93
+ .getDisplayValues()
94
+ .flat();
95
+ if (existingIds.includes(request.documentationId)) {
96
+ return { ok: true, duplicate: true, documentationId: request.documentationId };
97
+ }
98
+
99
+ const now = new Date();
100
+ const folder = getEvidenceFolder(request);
101
+ const photoUrls = request.photos.map((photo, index) => savePhoto(folder, photo, request, index));
102
+ sheet.appendRow([
103
+ request.documentationId,
104
+ now,
105
+ safeText(request.researcher),
106
+ safeText(request.establishmentName),
107
+ safeText(request.commercialRecord),
108
+ safeText(request.contractNumber) || "لا يوجد رقم عقد",
109
+ safeText(request.city),
110
+ safeText(request.fieldStatus),
111
+ safeText(request.statement),
112
+ photoUrls.join("\n"),
113
+ folder.getUrl(),
114
+ photoUrls.length,
115
+ safeText(request.sampleKey),
116
+ now,
117
+ ]);
118
+
119
+ return evidenceResponse(request, folder.getUrl(), photoUrls, now, now);
120
+ }
121
+
122
+ function updateEvidence(sheet, request) {
123
+ const rowNumber = findDocumentationRow(sheet, request.documentationId);
124
+ if (!rowNumber) throw new Error("تعذر العثور على التوثيق المطلوب تعديله.");
125
+
126
+ const row = sheet.getRange(rowNumber, 1, 1, HEADERS.length).getValues()[0];
127
+ if (safeText(row[12]) && safeText(row[12]) !== safeText(request.sampleKey)) {
128
+ throw new Error("لا يمكن تعديل توثيق عينة أخرى.");
129
+ }
130
+ if (safeText(row[2]) && safeText(row[2]) !== safeText(request.researcher)) {
131
+ throw new Error("لا يمكن تعديل توثيق باحث آخر.");
132
+ }
133
+
134
+ const oldPhotoUrls = splitUrls(row[9]);
135
+ const keptPhotoUrls = Array.isArray(request.keptPhotoUrls)
136
+ ? request.keptPhotoUrls.map(safeText).filter((url) => oldPhotoUrls.includes(url))
137
+ : oldPhotoUrls;
138
+ if (keptPhotoUrls.length + request.photos.length > CONFIG.maxPhotos) {
139
+ throw new Error("الحد الأعلى 3 صور.");
140
+ }
141
+
142
+ const folder = row[10] ? getFolderByUrl(row[10], request) : getEvidenceFolder(request);
143
+ const newPhotoUrls = request.photos.map((photo, index) =>
144
+ savePhoto(folder, photo, request, keptPhotoUrls.length + index),
145
+ );
146
+ const updatedAt = new Date();
147
+ const photoUrls = [...keptPhotoUrls, ...newPhotoUrls];
148
+ sheet.getRange(rowNumber, 8, 1, 7).setValues([[
149
+ safeText(request.fieldStatus),
150
+ safeText(request.statement),
151
+ photoUrls.join("\n"),
152
+ folder.getUrl(),
153
+ photoUrls.length,
154
+ safeText(request.sampleKey),
155
+ updatedAt,
156
+ ]]);
157
+ oldPhotoUrls
158
+ .filter((url) => !keptPhotoUrls.includes(url))
159
+ .forEach(trashDriveFileByUrl);
160
+
161
+ const documentedAt = row[1] instanceof Date ? row[1] : updatedAt;
162
+ return evidenceResponse(request, folder.getUrl(), photoUrls, documentedAt, updatedAt, true);
163
+ }
164
+
165
+ function evidenceResponse(request, folderUrl, photoUrls, documentedAt, updatedAt, updated) {
166
+ return {
167
+ ok: true,
168
+ updated: Boolean(updated),
169
+ documentationId: request.documentationId,
170
+ sampleKey: safeText(request.sampleKey),
171
+ researcher: safeText(request.researcher),
172
+ establishmentName: safeText(request.establishmentName),
173
+ commercialRecord: safeText(request.commercialRecord),
174
+ contractNumber: safeText(request.contractNumber),
175
+ city: safeText(request.city),
176
+ fieldStatus: safeText(request.fieldStatus),
177
+ statement: safeText(request.statement),
178
+ documentedAt: documentedAt.toISOString(),
179
+ documentedDate: Utilities.formatDate(documentedAt, CONFIG.timezone, "yyyy-MM-dd"),
180
+ updatedAt: updatedAt.toISOString(),
181
+ folderUrl,
182
+ photoUrls,
183
+ photoCount: photoUrls.length,
184
+ };
185
+ }
186
+
187
+ function findDocumentationRow(sheet, documentationId) {
188
+ if (sheet.getLastRow() < 2) return 0;
189
+ const ids = sheet.getRange(2, 1, sheet.getLastRow() - 1, 1).getDisplayValues().flat();
190
+ const index = ids.indexOf(String(documentationId));
191
+ return index < 0 ? 0 : index + 2;
192
+ }
193
+
194
+ function getFolderByUrl(url, request) {
195
+ const id = driveIdFromUrl(url);
196
+ return id ? DriveApp.getFolderById(id) : getEvidenceFolder(request);
197
+ }
198
+
199
+ function trashDriveFileByUrl(url) {
200
+ const id = driveIdFromUrl(url);
201
+ if (!id) return;
202
+ try {
203
+ DriveApp.getFileById(id).setTrashed(true);
204
+ } catch (_) {}
205
+ }
206
+
207
+ function driveIdFromUrl(url) {
208
+ const value = String(url || "");
209
+ const match =
210
+ value.match(/\/folders\/([a-zA-Z0-9_-]+)/) ||
211
+ value.match(/\/d\/([a-zA-Z0-9_-]+)/) ||
212
+ value.match(/[?&]id=([a-zA-Z0-9_-]+)/);
213
+ return match ? match[1] : "";
214
+ }
215
+
216
  function validateResearcherAccess(accessCode) {
217
  const expected = PropertiesService.getScriptProperties().getProperty("ACCESS_CODE") || "20302030";
218
  if (String(accessCode || "") !== expected) throw new Error("غير مصرح بالوصول.");
 
258
  fieldStatus: safeText(row[7]),
259
  photoCount: Number(row[11]) || splitUrls(row[9]).length,
260
  sampleKey: safeText(row[12]),
261
+ updatedAt: row[13] instanceof Date ? row[13].toISOString() : "",
262
  };
263
  if (!includeDetails) return base;
264
  return Object.assign(base, {
 
267
  folderUrl: safeText(row[10]),
268
  });
269
  })
270
+ .sort((a, b) =>
271
+ String(b.updatedAt || b.documentedAt).localeCompare(String(a.updatedAt || a.documentedAt)),
272
+ );
273
  }
274
 
275
  function splitUrls(value) {
 
294
  return;
295
  }
296
  if (!current[12]) sheet.getRange(1, 13).setValue(HEADERS[12]);
297
+ if (!current[13]) sheet.getRange(1, 14).setValue(HEADERS[13]);
298
  }
299
 
300
  function getEvidenceFolder(request) {