emvecchi commited on
Commit
9a3e2d8
·
verified ·
1 Parent(s): 42aaed5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -26
app.py CHANGED
@@ -53,26 +53,26 @@ yes_no_labels = ['no','yes']
53
  yes_no_other_labels = ['no','yes','other']
54
  default_labels = agreement_labels
55
 
56
- function_choices = ['Broadening Discussion',
57
- 'Improving Comment Quality',
58
- 'Content Correction',
59
- 'Keeping Discussion on Topic',
60
- 'Organizing Discussion',
61
- 'Policing',
62
- 'Resolving Site Use Issues',
63
- 'Social Functions',
64
- 'None',
65
- 'Other']
66
-
67
- rupture_choices = ["Shutting down",
68
- "Avoiding",
69
- "Masking aspects of one's experience",
70
- "Complaining/critisizing",
71
- "Pushing back",
72
- "Controlling/pressuring",
73
  "Other"]
74
 
75
- default_choices = function_choices
76
 
77
  consent_text = '''
78
  ## Consent Form
@@ -127,12 +127,14 @@ fields: List[Field] = [
127
  title="Level of priority", other_params={'labels': priority_labels}, mandatory=True),
128
  ]),
129
 
130
- Field(type="container", title="**Rupture-Specific Properties**", children=[
131
- Field(name="rupture_function", type="multiselect",
132
- title="What type of rupture markers are found? *(Multiple selection possible)*",
133
- other_params={'choices': rupture_choices}, mandatory=True, following_mandatory_values=['Other (please specify)']),
134
- Field(name="rupture_function_other", type="text", title="*If Other, please specify:*", mandatory=False),
135
- Field(name="rupture_line", type="text", title="What lines demonstrate the rupture markers you notice?", mandatory=False),
 
 
136
  ]),
137
 
138
  Field(type="container", title="**True-To-Patient-Prompt Properties**", children=[
@@ -332,6 +334,69 @@ def render_dialogue(md_path: str,
332
  st_html(html_doc, height=height_px + (16 if show_border else 0), scrolling=False)
333
 
334
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  #################################### Streamlit App ####################################
336
 
337
  # Function to navigate rows
@@ -397,6 +462,8 @@ def show_field(f: Field, index: int, data_collected):
397
  show_field(child, index, data_collected)
398
  case 'skip_checkbox':
399
  st.checkbox(f.title, key=f.name, value=False)
 
 
400
  else:
401
  key = f.name + str(index)
402
  st.session_state.data_inputs_keys.append(f.name)
@@ -516,13 +583,48 @@ def show_fields(fields: List[Field]):
516
  st.session_state.form_displayed = st.session_state.current_index
517
 
518
  def prep_and_save_data(index, skip_sample):
519
- save_data({
520
  'user_id': st.session_state.user_id,
521
  'index': st.session_state.current_index,
522
  **(st.session_state.data.iloc[index][COLS_TO_SAVE].to_dict() if 0 <= index < len(st.session_state.data) else {}),
523
  **{k: st.session_state[k + str(index)] for k in st.session_state.data_inputs_keys},
524
  'skip': skip_sample
525
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
 
527
  st.set_page_config(layout='wide')
528
  # Title of the app
 
53
  yes_no_other_labels = ['no','yes','other']
54
  default_labels = agreement_labels
55
 
56
+ MAX_RUPTURE_MARKERS = 8 # 1 initial + up to 7 extra
57
+ RUPTURE_LINE_INPUT_TYPE = "number" # or "text" if your line numbers aren't pure ints
58
+
59
+ rupture_choices = ["Denial",
60
+ "Minimal response",
61
+ "Abstract communication",
62
+ "Avoidant storytelling and/or topic-shifting",
63
+ "Deferential / appeasing behavior",
64
+ "Content/affect split (i.e. talking content vs emotional tone mismatch)",
65
+ "Self-criticism and/or hopelessness",
66
+ "Complaints or concerns about the therapist",
67
+ "Patient rejects therapist intervention",
68
+ "Complaints/concerns about the activities of therapy",
69
+ "Complaints/concerns about the parameters of therapy (e.g. scheduling, rules)",
70
+ "Complaints/concerns about progress in therapy",
71
+ "Patient defends self against therapist",
72
+ "Efforts to control or pressure the therapist",
73
  "Other"]
74
 
75
+ default_choices = rupture_choices
76
 
77
  consent_text = '''
78
  ## Consent Form
 
127
  title="Level of priority", other_params={'labels': priority_labels}, mandatory=True),
128
  ]),
129
 
130
+ Field(type="container", title="**Rupture Markers**", children=[
131
+ Field(name="rupture_markers", type="rupture_markers",
132
+ title="Select rupture markers noted in the session, include line numbers where rupture is found.", mandatory=False),
133
+ # Field(name="rupture_marker", type="multiselect",
134
+ # title="What type of rupture markers are found? *(Select only one)*",
135
+ # other_params={'choices': rupture_choices}, mandatory=True, following_mandatory_values=['Other (please specify)']),
136
+ # Field(name="rupture_marker", type="text", title="What lines demonstrate the rupture markers you notice?", mandatory=False),
137
+ # Field(name="rupture_marker_other", type="text", title="*If Other, please specify:*", mandatory=False),
138
  ]),
139
 
140
  Field(type="container", title="**True-To-Patient-Prompt Properties**", children=[
 
334
  st_html(html_doc, height=height_px + (16 if show_border else 0), scrolling=False)
335
 
336
 
337
+ def render_rupture_markers_widget(index: int, choices: list[str]):
338
+ """Dynamic list of (rupture_marker_i, line_a_i, line_b_i) with Add/Remove."""
339
+ count_key = f"rupture_count_{index}"
340
+ if count_key not in st.session_state:
341
+ st.session_state[count_key] = 1 # start with one block
342
+
343
+ count = st.session_state[count_key]
344
+
345
+ st.markdown("**What rupture markers are found?** *(one per row; max 8)*")
346
+
347
+ # render the i=1..count rows
348
+ for i in range(1, count + 1):
349
+ col1, col2, col3 = st.columns([3, 1, 1])
350
+
351
+ marker_key = f"rupture_marker_{i}_{index}"
352
+ a_key = f"rupture_marker_{i}_line_a_{index}"
353
+ b_key = f"rupture_marker_{i}_line_b_{index}"
354
+
355
+ with col1:
356
+ st.selectbox(
357
+ f"Rupture marker {i}",
358
+ options=choices,
359
+ key=marker_key,
360
+ index=(choices.index(st.session_state.get(marker_key))
361
+ if st.session_state.get(marker_key) in choices else 0)
362
+ if st.session_state.get(marker_key) is not None else 0
363
+ )
364
+
365
+ # line inputs (number or text)
366
+ with col2:
367
+ if RUPTURE_LINE_INPUT_TYPE == "number":
368
+ st.number_input(f"Lines: {i} – from", min_value=1, step=1, key=a_key)
369
+ else:
370
+ st.text_input(f"Lines: {i} – from", key=a_key, value=st.session_state.get(a_key, ""))
371
+
372
+ with col3:
373
+ if RUPTURE_LINE_INPUT_TYPE == "number":
374
+ st.number_input(f"Lines: {i} – to", min_value=1, step=1, key=b_key)
375
+ else:
376
+ st.text_input(f"Lines: {i} – to", key=b_key, value=st.session_state.get(b_key, ""))
377
+
378
+ st.markdown("---")
379
+
380
+ # add/remove buttons
381
+ a_col, b_col = st.columns([1, 1])
382
+ with a_col:
383
+ st.button(
384
+ "➕ Add another rupture marker",
385
+ key=f"add_rup_{index}",
386
+ disabled=(count >= MAX_RUPTURE_MARKERS),
387
+ on_click=lambda: st.session_state.__setitem__(count_key, min(MAX_RUPTURE_MARKERS, st.session_state[count_key] + 1))
388
+ )
389
+ with b_col:
390
+ st.button(
391
+ "➖ Remove last",
392
+ key=f"remove_rup_{index}",
393
+ disabled=(count <= 1),
394
+ on_click=lambda: st.session_state.__setitem__(count_key, max(1, st.session_state[count_key] - 1))
395
+ )
396
+
397
+ # info text
398
+ st.caption(f"{st.session_state[count_key]}/{MAX_RUPTURE_MARKERS} markers")
399
+
400
  #################################### Streamlit App ####################################
401
 
402
  # Function to navigate rows
 
462
  show_field(child, index, data_collected)
463
  case 'skip_checkbox':
464
  st.checkbox(f.title, key=f.name, value=False)
465
+ case 'rupture_markers':
466
+ render_rupture_markers_widget(index, rupture_choices)
467
  else:
468
  key = f.name + str(index)
469
  st.session_state.data_inputs_keys.append(f.name)
 
583
  st.session_state.form_displayed = st.session_state.current_index
584
 
585
  def prep_and_save_data(index, skip_sample):
586
+ payload = {
587
  'user_id': st.session_state.user_id,
588
  'index': st.session_state.current_index,
589
  **(st.session_state.data.iloc[index][COLS_TO_SAVE].to_dict() if 0 <= index < len(st.session_state.data) else {}),
590
  **{k: st.session_state[k + str(index)] for k in st.session_state.data_inputs_keys},
591
  'skip': skip_sample
592
+ }
593
+ # normalize rupture markers -> always write 8 slots
594
+ count_key = f"rupture_count_{index}"
595
+ count = st.session_state.get(count_key, 1)
596
+ for i in range(1, MAX_RUPTURE_MARKERS + 1):
597
+ m_key = f"rupture_marker_{i}_{index}"
598
+ a_key = f"rupture_marker_{i}_line_a_{index}"
599
+ b_key = f"rupture_marker_{i}_line_b_{index}"
600
+
601
+ # export names WITHOUT the trailing _{index}, so your JSON uses a stable schema:
602
+ out_m = f"rupture_marker_{i}"
603
+ out_a = f"rupture_marker_{i}_line_a"
604
+ out_b = f"rupture_marker_{i}_line_b"
605
+
606
+ if i <= count:
607
+ marker_val = st.session_state.get(m_key)
608
+ line_a_val = st.session_state.get(a_key)
609
+ line_b_val = st.session_state.get(b_key)
610
+
611
+ # Basic sanity: if numbers, coerce order (optional)
612
+ if RUPTURE_LINE_INPUT_TYPE == "number":
613
+ try:
614
+ if line_a_val is not None and line_b_val is not None and line_a_val > line_b_val:
615
+ line_a_val, line_b_val = line_b_val, line_a_val
616
+ except Exception:
617
+ pass
618
+
619
+ payload[out_m] = marker_val if marker_val not in ("", None) else None
620
+ payload[out_a] = line_a_val if line_a_val not in ("", None) else None
621
+ payload[out_b] = line_b_val if line_b_val not in ("", None) else None
622
+ else:
623
+ payload[out_m] = None
624
+ payload[out_a] = None
625
+ payload[out_b] = None
626
+
627
+ save_data(payload)
628
 
629
  st.set_page_config(layout='wide')
630
  # Title of the app