solidprivacy-nl commited on
Commit
09184e8
·
1 Parent(s): 74cdf0d

Integrate Scrub Key import UI into Streamlit patch

Browse files
Files changed (1) hide show
  1. fix_streamlit_nested_expanders.py +112 -134
fix_streamlit_nested_expanders.py CHANGED
@@ -14,6 +14,7 @@ Patches currently applied:
14
  - v12.6: show advisory export sanity warnings before download/export.
15
  - v13.1: add local Scrub Key JSON export after review.
16
  - v13.1 hotfix: map Streamlit review-table columns to Scrub Key fields before JSON export.
 
17
  """
18
 
19
  from pathlib import Path
@@ -53,7 +54,7 @@ text = replace_once(
53
  'from pathlib import Path\nfrom datetime import datetime, timezone\n',
54
  )
55
 
56
- # v12.1/v12.2/v12.3/v12.4/v12.5: import review helpers.
57
  text = replace_once(
58
  text,
59
  'from display_labels_nl import entity_label, source_label, confidence_label\n',
@@ -69,93 +70,35 @@ text = replace_once(
69
  ' AI_USAGE_GUIDANCE,\n'
70
  ' EXPORT_GUIDANCE,\n'
71
  ')\n'
72
- 'from review_summary import build_review_summary, review_summary_markdown\n',
73
- )
74
-
75
- # If v12.1 already added review_status but later helpers are not present yet, extend imports only.
76
- text = replace_once(
77
- text,
78
- 'from review_status import review_status_for_source, review_status_label, review_status_order\n',
79
- 'from review_status import review_status_for_source, review_status_label, review_status_order\n'
80
- 'from review_filters import REVIEW_FILTER_OPTIONS, FILTER_SHOW_ALL, filter_review_dataframe\n'
81
- 'from review_table_config import main_review_columns, technical_display_columns\n'
82
- 'from review_guidance import (\n'
83
- ' REVIEW_INTRO_GUIDANCE,\n'
84
- ' CANDIDATE_GUIDANCE,\n'
85
- ' FOCUS_FILTER_GUIDANCE,\n'
86
- ' TECHNICAL_DETAILS_GUIDANCE,\n'
87
- ' AI_USAGE_GUIDANCE,\n'
88
- ' EXPORT_GUIDANCE,\n'
89
- ')\n'
90
- 'from review_summary import build_review_summary, review_summary_markdown\n',
91
- )
92
-
93
- text = replace_once(
94
- text,
95
- 'from review_filters import REVIEW_FILTER_OPTIONS, FILTER_SHOW_ALL, filter_review_dataframe\n',
96
- 'from review_filters import REVIEW_FILTER_OPTIONS, FILTER_SHOW_ALL, filter_review_dataframe\n'
97
- 'from review_table_config import main_review_columns, technical_display_columns\n'
98
- 'from review_guidance import (\n'
99
- ' REVIEW_INTRO_GUIDANCE,\n'
100
- ' CANDIDATE_GUIDANCE,\n'
101
- ' FOCUS_FILTER_GUIDANCE,\n'
102
- ' TECHNICAL_DETAILS_GUIDANCE,\n'
103
- ' AI_USAGE_GUIDANCE,\n'
104
- ' EXPORT_GUIDANCE,\n'
105
- ')\n'
106
- 'from review_summary import build_review_summary, review_summary_markdown\n',
107
- )
108
-
109
- text = replace_once(
110
- text,
111
- 'from review_table_config import main_review_columns, technical_display_columns\n',
112
- 'from review_table_config import main_review_columns, technical_display_columns\n'
113
- 'from review_guidance import (\n'
114
- ' REVIEW_INTRO_GUIDANCE,\n'
115
- ' CANDIDATE_GUIDANCE,\n'
116
- ' FOCUS_FILTER_GUIDANCE,\n'
117
- ' TECHNICAL_DETAILS_GUIDANCE,\n'
118
- ' AI_USAGE_GUIDANCE,\n'
119
- ' EXPORT_GUIDANCE,\n'
120
- ')\n'
121
- 'from review_summary import build_review_summary, review_summary_markdown\n',
122
  )
123
 
 
124
  text = replace_once(
125
  text,
126
- 'from review_guidance import (\n'
127
- ' REVIEW_INTRO_GUIDANCE,\n'
128
- ' CANDIDATE_GUIDANCE,\n'
129
- ' FOCUS_FILTER_GUIDANCE,\n'
130
- ' TECHNICAL_DETAILS_GUIDANCE,\n'
131
- ' AI_USAGE_GUIDANCE,\n'
132
- ' EXPORT_GUIDANCE,\n'
133
- ')\n',
134
- 'from review_guidance import (\n'
135
- ' REVIEW_INTRO_GUIDANCE,\n'
136
- ' CANDIDATE_GUIDANCE,\n'
137
- ' FOCUS_FILTER_GUIDANCE,\n'
138
- ' TECHNICAL_DETAILS_GUIDANCE,\n'
139
- ' AI_USAGE_GUIDANCE,\n'
140
- ' EXPORT_GUIDANCE,\n'
141
- ')\n'
142
  'from review_summary import build_review_summary, review_summary_markdown\n',
 
 
 
 
143
  )
144
 
145
- # v12.6: import advisory export sanity helpers without changing export behavior.
146
  text = replace_once(
147
  text,
148
- 'from review_summary import build_review_summary, review_summary_markdown\n',
149
- 'from review_summary import build_review_summary, review_summary_markdown\n'
150
  'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n',
 
 
 
151
  )
152
 
153
- # v13.1: import Scrub Key JSON export helpers.
154
  text = replace_once(
155
  text,
156
- 'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n',
157
- 'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n'
158
  'from scrub_key import build_scrub_key, scrub_key_to_json, validate_scrub_key\n',
 
 
159
  )
160
 
161
  # v12.1: add review status fields to remembered rows.
@@ -222,6 +165,46 @@ text = replace_once(
222
  ''',
223
  )
224
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  # v12.1/v12.2/v12.3/v12.4: sort review rows, show guidance/status summary, focus filters and technical view.
226
  text = replace_once(
227
  text,
@@ -269,40 +252,12 @@ text = replace_once(
269
  ''',
270
  )
271
 
272
- # v12.4: if v12.1/v12.2/v12.3 block is already present, add guidance to that block.
273
- text = replace_once(
274
- text,
275
- ''' replacement_editor_df = pd.DataFrame(default_editor_rows)
276
- if "review_order" in replacement_editor_df.columns:
277
- ''',
278
- ''' replacement_editor_df = pd.DataFrame(default_editor_rows)
279
- st.info(REVIEW_INTRO_GUIDANCE)
280
- with st.expander("Uitleg bij deze controle", expanded=False):
281
- st.markdown(f"- {CANDIDATE_GUIDANCE}")
282
- st.markdown(f"- {FOCUS_FILTER_GUIDANCE}")
283
- st.markdown(f"- {TECHNICAL_DETAILS_GUIDANCE}")
284
- st.markdown(f"- {AI_USAGE_GUIDANCE}")
285
- if "review_order" in replacement_editor_df.columns:
286
- ''',
287
- )
288
-
289
  text = replace_once(
290
  text,
291
  ' help="Gebruik dit als overzichtsfilter. De volledige vervangtabel hieronder blijft leidend voor de export.",\n',
292
  ' help=FOCUS_FILTER_GUIDANCE,\n',
293
  )
294
 
295
- text = replace_once(
296
- text,
297
- ''' with st.expander("Technische details bij de vervangtabel", expanded=False):
298
- technical_columns = technical_display_columns(replacement_editor_df.columns)
299
- ''',
300
- ''' with st.expander("Technische details bij de vervangtabel", expanded=False):
301
- st.caption(TECHNICAL_DETAILS_GUIDANCE)
302
- technical_columns = technical_display_columns(replacement_editor_df.columns)
303
- ''',
304
- )
305
-
306
  # v12.1: make status visible in the editor.
307
  text = replace_once(
308
  text,
@@ -376,6 +331,48 @@ scrub_key_mapping_block = ''' scrub_key_rows = edited_replacements_df.cop
376
  scrub_key = build_scrub_key(scrub_key_rows)
377
  '''
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  review_summary_block = ''' st.subheader("4. Download opgeschoonde bestanden")
380
  final_review_summary = build_review_summary(edited_replacements_df)
381
  st.markdown("**Eindcontrole vóór download**")
@@ -398,16 +395,17 @@ review_summary_block = ''' st.subheader("4. Download opgeschoonde bestand
398
  if scrub_key.get("item_count", 0) == 0:
399
  st.info("Er zijn geen geselecteerde vervangingen voor de Scrub Key. De JSON bevat dan geen mapping-items.")
400
  st.download_button(
401
- "Download Scrub Key (.json)",
 
402
  data=scrub_key_to_json(scrub_key),
403
  file_name="solidprivacy_scrub_key.json",
404
  mime="application/json",
405
  )
406
- st.warning(EXPORT_GUIDANCE)
407
  '''
408
 
409
- # v12.5/v12.6/v13.1: add final review summary, advisory export sanity warnings and Scrub Key JSON export.
410
- # Handle both unpatched and v12.4-patched app text.
411
  text = replace_once(
412
  text,
413
  ''' st.subheader("4. Download opgeschoonde bestanden")
@@ -423,44 +421,24 @@ text = replace_once(
423
  review_summary_block,
424
  )
425
 
426
- # v12.6: extend an existing v12.5 review-summary block with advisory sanity warnings.
427
- text = replace_once(
428
- text,
429
- ''' st.markdown(review_summary_markdown(final_review_summary))
430
- st.warning(EXPORT_GUIDANCE)
431
- ''',
432
- ''' st.markdown(review_summary_markdown(final_review_summary))
433
- export_sanity_checks = build_export_sanity_checks(edited_replacements_df)
434
- st.markdown("**Extra exportcontrole**")
435
- for export_sanity_warning in export_sanity_warnings(export_sanity_checks):
436
- st.warning(export_sanity_warning)
437
- st.caption("Deze exportcontrole is adviserend: downloads blijven beschikbaar en de exportinstellingen blijven ongewijzigd.")
438
- st.warning(EXPORT_GUIDANCE)
439
- ''',
440
- )
441
-
442
- # v13.1: extend an existing v12.6 block with Scrub Key JSON export.
443
  text = replace_once(
444
  text,
445
- ''' st.caption("Deze exportcontrole is adviserend: downloads blijven beschikbaar en de exportinstellingen blijven ongewijzigd.")
 
 
 
 
 
446
  st.warning(EXPORT_GUIDANCE)
447
  ''',
448
- ''' st.caption("Deze exportcontrole is adviserend: downloads blijven beschikbaar en de exportinstellingen blijven ongewijzigd.")
449
- st.markdown("**Scrub Key (JSON)**")
450
- st.warning("Let op: een Scrub Key maakt de vervangen waarden lokaal herleidbaar. Dit is pseudonimisering, geen volledige anonimisering. Deel deze sleutel niet met AI-diensten of derden tenzij dat bewust en toegestaan is.")
451
- ''' + scrub_key_mapping_block + ''' scrub_key_issues = validate_scrub_key(scrub_key)
452
- if scrub_key_issues:
453
- st.warning("Scrub Key kan nog niet betrouwbaar worden geëxporteerd: " + "; ".join(scrub_key_issues[:3]))
454
- else:
455
- if scrub_key.get("item_count", 0) == 0:
456
- st.info("Er zijn geen geselecteerde vervangingen voor de Scrub Key. De JSON bevat dan geen mapping-items.")
457
- st.download_button(
458
  "Download Scrub Key (.json)",
459
  data=scrub_key_to_json(scrub_key),
460
  file_name="solidprivacy_scrub_key.json",
461
  mime="application/json",
462
  )
463
- st.warning(EXPORT_GUIDANCE)
464
  ''',
465
  )
466
 
 
14
  - v12.6: show advisory export sanity warnings before download/export.
15
  - v13.1: add local Scrub Key JSON export after review.
16
  - v13.1 hotfix: map Streamlit review-table columns to Scrub Key fields before JSON export.
17
+ - v13.2: add local Scrub Key import/reload UI using the pure import helper.
18
  """
19
 
20
  from pathlib import Path
 
54
  'from pathlib import Path\nfrom datetime import datetime, timezone\n',
55
  )
56
 
57
+ # v12/v13: import review, export sanity, Scrub Key export and Scrub Key import helpers.
58
  text = replace_once(
59
  text,
60
  'from display_labels_nl import entity_label, source_label, confidence_label\n',
 
70
  ' AI_USAGE_GUIDANCE,\n'
71
  ' EXPORT_GUIDANCE,\n'
72
  ')\n'
73
+ 'from review_summary import build_review_summary, review_summary_markdown\n'
74
+ 'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n'
75
+ 'from scrub_key import build_scrub_key, scrub_key_to_json, validate_scrub_key\n'
76
+ 'from scrub_key_import import IMPORT_PRIVACY_WARNING, build_scrub_key_import_result\n',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  )
78
 
79
+ # v12.6/v13.1/v13.2: extend imports on already partially patched app files.
80
  text = replace_once(
81
  text,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  'from review_summary import build_review_summary, review_summary_markdown\n',
83
+ 'from review_summary import build_review_summary, review_summary_markdown\n'
84
+ 'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n'
85
+ 'from scrub_key import build_scrub_key, scrub_key_to_json, validate_scrub_key\n'
86
+ 'from scrub_key_import import IMPORT_PRIVACY_WARNING, build_scrub_key_import_result\n',
87
  )
88
 
 
89
  text = replace_once(
90
  text,
 
 
91
  'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n',
92
+ 'from export_sanity import build_export_sanity_checks, export_sanity_warnings\n'
93
+ 'from scrub_key import build_scrub_key, scrub_key_to_json, validate_scrub_key\n'
94
+ 'from scrub_key_import import IMPORT_PRIVACY_WARNING, build_scrub_key_import_result\n',
95
  )
96
 
 
97
  text = replace_once(
98
  text,
 
 
99
  'from scrub_key import build_scrub_key, scrub_key_to_json, validate_scrub_key\n',
100
+ 'from scrub_key import build_scrub_key, scrub_key_to_json, validate_scrub_key\n'
101
+ 'from scrub_key_import import IMPORT_PRIVACY_WARNING, build_scrub_key_import_result\n',
102
  )
103
 
104
  # v12.1: add review status fields to remembered rows.
 
165
  ''',
166
  )
167
 
168
+ scrub_key_import_merge_block = ''' if "scrub_key_import_rows" in st.session_state:
169
+ imported_scrub_key_rows = st.session_state.pop("scrub_key_import_rows")
170
+ added_scrub_key_rows = 0
171
+ existing_find_values = {str(row.get("find", "")).strip() for row in default_editor_rows}
172
+ if len(default_editor_rows) == 1 and not str(default_editor_rows[0].get("find", "")).strip():
173
+ default_editor_rows = []
174
+ existing_find_values = set()
175
+ for imported_scrub_key_row in imported_scrub_key_rows:
176
+ imported_find = str(imported_scrub_key_row.get("find", "")).strip()
177
+ imported_replace = str(imported_scrub_key_row.get("replace_with", "")).strip()
178
+ if not imported_find or not imported_replace or imported_find in existing_find_values:
179
+ continue
180
+ imported_row = dict(imported_scrub_key_row)
181
+ imported_row.setdefault("include", True)
182
+ imported_row.setdefault("remember", False)
183
+ imported_row.setdefault("source", "scrub_key_import")
184
+ imported_row.setdefault("source_label", "Scrub Key")
185
+ imported_row.setdefault("reason", "Geladen uit Scrub Key")
186
+ imported_row.setdefault("context", "")
187
+ imported_row.setdefault("confidence", "")
188
+ imported_row.setdefault("score", None)
189
+ imported_row.setdefault("type_label", entity_label(imported_row.get("entity_type", "MANUAL")))
190
+ imported_row.setdefault("review_status_label", review_status_label(imported_row.get("review_status", "manual")))
191
+ imported_row.setdefault("review_order", review_status_order(imported_row.get("review_status", "manual")))
192
+ default_editor_rows.append(imported_row)
193
+ existing_find_values.add(imported_find)
194
+ added_scrub_key_rows += 1
195
+ if added_scrub_key_rows:
196
+ st.success(f"{added_scrub_key_rows} Scrub Key regel(s) geladen in de vervangtabel.")
197
+ '''
198
+
199
+ # v13.2: merge imported Scrub Key mappings into the review table only after a visible user action.
200
+ text = replace_once(
201
+ text,
202
+ ''' replacement_editor_df = pd.DataFrame(default_editor_rows)
203
+ ''',
204
+ scrub_key_import_merge_block + ''' replacement_editor_df = pd.DataFrame(default_editor_rows)
205
+ ''',
206
+ )
207
+
208
  # v12.1/v12.2/v12.3/v12.4: sort review rows, show guidance/status summary, focus filters and technical view.
209
  text = replace_once(
210
  text,
 
252
  ''',
253
  )
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  text = replace_once(
256
  text,
257
  ' help="Gebruik dit als overzichtsfilter. De volledige vervangtabel hieronder blijft leidend voor de export.",\n',
258
  ' help=FOCUS_FILTER_GUIDANCE,\n',
259
  )
260
 
 
 
 
 
 
 
 
 
 
 
 
261
  # v12.1: make status visible in the editor.
262
  text = replace_once(
263
  text,
 
331
  scrub_key = build_scrub_key(scrub_key_rows)
332
  '''
333
 
334
+ scrub_key_import_ui_block = ''' st.markdown("**Scrub Key laden**")
335
+ st.warning("Let op: een Scrub Key maakt vervangen waarden lokaal herleidbaar. Dit is pseudonimisering, geen volledige anonimisering. Bewaar deze sleutel lokaal en deel deze niet met AI-diensten of derden tenzij dat bewust en toegestaan is.")
336
+ st.caption("Upload of plak een eerder geëxporteerde Scrub Key JSON. De sleutel wordt eerst gevalideerd en daarna pas na jouw klik aan de vervangtabel toegevoegd.")
337
+ scrub_key_import_file = st.file_uploader(
338
+ "Upload Scrub Key JSON (.json)",
339
+ type=["json"],
340
+ key="scrub_key_import_file",
341
+ help="Gebruik alleen een lokale Scrub Key die bij dit dossier of document hoort.",
342
+ )
343
+ scrub_key_import_paste = st.text_area(
344
+ "Of plak Scrub Key JSON",
345
+ value="",
346
+ height=120,
347
+ key="scrub_key_import_paste",
348
+ )
349
+ scrub_key_import_text = ""
350
+ scrub_key_import_decode_error = None
351
+ if scrub_key_import_file is not None:
352
+ try:
353
+ scrub_key_import_text = scrub_key_import_file.getvalue().decode("utf-8")
354
+ except UnicodeDecodeError:
355
+ scrub_key_import_decode_error = "Scrub Key bestand is geen geldige UTF-8 tekst."
356
+ elif scrub_key_import_paste.strip():
357
+ scrub_key_import_text = scrub_key_import_paste
358
+ if st.button("Valideer en laad Scrub Key", key="load_scrub_key_import"):
359
+ if scrub_key_import_decode_error:
360
+ st.error(scrub_key_import_decode_error)
361
+ else:
362
+ scrub_key_import_result = build_scrub_key_import_result(scrub_key_import_text)
363
+ for scrub_key_import_warning in scrub_key_import_result.get("warnings", [IMPORT_PRIVACY_WARNING]):
364
+ st.warning(scrub_key_import_warning)
365
+ if scrub_key_import_result.get("ok"):
366
+ st.success(f"Scrub Key geldig: {scrub_key_import_result.get('item_count', 0)} mappingregel(s) gevonden.")
367
+ st.session_state["scrub_key_import_rows"] = scrub_key_import_result.get("mapping_rows", [])
368
+ if "replacement_editor" in st.session_state:
369
+ del st.session_state["replacement_editor"]
370
+ st.rerun()
371
+ else:
372
+ for scrub_key_import_error in scrub_key_import_result.get("errors", []):
373
+ st.error(scrub_key_import_error)
374
+ '''
375
+
376
  review_summary_block = ''' st.subheader("4. Download opgeschoonde bestanden")
377
  final_review_summary = build_review_summary(edited_replacements_df)
378
  st.markdown("**Eindcontrole vóór download**")
 
395
  if scrub_key.get("item_count", 0) == 0:
396
  st.info("Er zijn geen geselecteerde vervangingen voor de Scrub Key. De JSON bevat dan geen mapping-items.")
397
  st.download_button(
398
+ "Download Scrub Key (.json)
399
+ ",
400
  data=scrub_key_to_json(scrub_key),
401
  file_name="solidprivacy_scrub_key.json",
402
  mime="application/json",
403
  )
404
+ ''' + scrub_key_import_ui_block + ''' st.warning(EXPORT_GUIDANCE)
405
  '''
406
 
407
+ # v12.5/v12.6/v13.1/v13.2: add final review summary, advisory export sanity warnings,
408
+ # Scrub Key JSON export and Scrub Key import/reload.
409
  text = replace_once(
410
  text,
411
  ''' st.subheader("4. Download opgeschoonde bestanden")
 
421
  review_summary_block,
422
  )
423
 
424
+ # v13.2: extend an already patched Scrub Key JSON export block with import/reload UI.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  text = replace_once(
426
  text,
427
+ ''' st.download_button(
428
+ "Download Scrub Key (.json)",
429
+ data=scrub_key_to_json(scrub_key),
430
+ file_name="solidprivacy_scrub_key.json",
431
+ mime="application/json",
432
+ )
433
  st.warning(EXPORT_GUIDANCE)
434
  ''',
435
+ ''' st.download_button(
 
 
 
 
 
 
 
 
 
436
  "Download Scrub Key (.json)",
437
  data=scrub_key_to_json(scrub_key),
438
  file_name="solidprivacy_scrub_key.json",
439
  mime="application/json",
440
  )
441
+ ''' + scrub_key_import_ui_block + ''' st.warning(EXPORT_GUIDANCE)
442
  ''',
443
  )
444