akaburia commited on
Commit
ac4279d
·
verified ·
1 Parent(s): 7f7b615

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -57
app.py CHANGED
@@ -596,37 +596,19 @@ custom_css = """
596
  margin-bottom: 20px !important;
597
  border: 1px solid #e5e7eb !important;
598
  }
 
 
 
 
 
 
 
 
 
 
599
  """
600
 
601
- with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
602
-
603
- with gr.Row():
604
- with gr.Column(scale=4):
605
- main_title = gr.Markdown("# Policy Coherence Annotation Tool")
606
- lang_selector = gr.Dropdown(choices=list(LANG_CODES.keys()), value="English", label="Language / Langue", scale=1)
607
-
608
- main_desc = gr.Markdown(
609
- "Evaluate how specific elements of different policies interact with one another.\n\n"
610
- "**Definitions:**\n"
611
- "- **Nexus Domain:** The broad sector being analyzed (e.g., Land, Water, Energy).\n"
612
- "- **Policy:** The specific document under review.\n"
613
- "- **Target:** The exact objective or statement you are currently evaluating.\n"
614
- "- **Context:** The broader set of measures belonging to the policy, provided as background reference."
615
- )
616
-
617
- # NEW: Accordion containing the 7 classes table
618
- with gr.Accordion("Interaction Class Definitions (Click to Expand)", open=False):
619
- gr.Markdown(
620
- "| Interaction Label | Meaning | Implication |\n"
621
- "| :--- | :--- | :--- |\n"
622
- "| **+3 (Indivisible)** | Progress on one target automatically delivers progress on another | There is high level of compatibility between the two targets. |\n"
623
- "| **+2 (Reinforcing)** | Progress on one target makes it easier to make progress on another | There is relatively higher level of compatibility between the targets being compared. |\n"
624
- "| **+1 (Enabling)** | Progress on one target creates conditions that enable progress on another | There is a small level of compatibility between the two targets compared. |\n"
625
- "| **0 (Consistent)** | There is no significant link between two targets' progress | There is no significant compatibility between the two targets being evaluated. |\n"
626
- "| **-1 (Constraining)** | Progress on one target constrains the options for how to deliver on another | The targets are relatively competitive resulting in counterproductive effects. |\n"
627
- "| **-2 (Counteracting)** | Progress on one target makes it more difficult to make progress on another | The targets are counterproductive and do not support each other. |\n"
628
- "| **-3 (Cancelling)** | Progress on one target automatically leads to a negative impact on another | The targets are highly opposite and are highly counterproductive. Cannot deliver synergistic effects. |"
629
- )
630
 
631
  hf_df_state = gr.State()
632
  user_tag_state = gr.State()
@@ -642,8 +624,33 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
642
  ctx_a_eng_state = gr.State("")
643
  ctx_b_eng_state = gr.State("")
644
 
645
- # --- LOGIN PANEL ---
646
- with gr.Group() as login_box:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
647
  login_title = gr.Markdown("### User Authentication & Informed Consent")
648
 
649
  gr.Markdown(
@@ -666,7 +673,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
666
  login_btn = gr.Button("Login & Accept", variant="primary")
667
  login_status = gr.Markdown(value="Waiting for authentication...")
668
 
669
- # Logic to disable the second question if the first is 'No'
670
  def toggle_followup(link_choice):
671
  if link_choice == "Yes":
672
  return gr.update(interactive=True)
@@ -675,8 +681,8 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
675
 
676
  consent_link_radio.change(fn=toggle_followup, inputs=consent_link_radio, outputs=consent_follow_radio)
677
 
678
- # --- EXPERTISE / SECTOR SELECTION ---
679
- with gr.Group(visible=False) as sector_box:
680
  gr.Markdown("### What is your expertise?")
681
  sector_cb = gr.CheckboxGroup(
682
  choices=SECTOR_CHOICES,
@@ -684,15 +690,34 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
684
  )
685
  proceed_btn = gr.Button("Proceed to Workspace", variant="primary", size="lg")
686
 
687
- # --- MAIN APPLICATION ---
688
- with gr.Group(visible=False) as app_box:
 
 
 
 
 
 
 
 
689
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
  with gr.Accordion("Data Selection", open=True) as data_acc:
691
-
692
- # --- NEW LOCATION FOR BACK BUTTON ---
693
  with gr.Row():
694
  back_to_sectors_btn = gr.Button("⬅️ Back to Sector Selection", variant="secondary", size="sm")
695
- gr.Markdown("") # Empty markdown to push button to the left
696
  gr.Markdown("")
697
 
698
  with gr.Row():
@@ -710,34 +735,28 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
710
  target_col_dd = gr.Dropdown(choices=AVAILABLE_COLUMNS, value='Strategic objectives / directions', label="Target Column")
711
  context_col_dd = gr.Dropdown(choices=AVAILABLE_COLUMNS, value='Policy Actions and Measures (PAMs)', label="Context Column")
712
 
713
- # Just the Load Button at the bottom now
714
  load_btn = gr.Button("Fetch Data", variant="primary")
715
 
716
  gr.Markdown("---")
717
  progress_text = gr.Markdown("**Progress:** Waiting for data selection...")
718
 
719
- with gr.Group(visible=False) as workspace_box:
720
 
721
- # --- THE FIXED HEADER ---
722
- # This stays naturally at the top of the workspace
723
  with gr.Row():
724
  with gr.Column(scale=1, variant="panel"):
725
  meta_a = gr.Markdown("### Source A Information")
726
  display_context_a = gr.Textbox(label="Context A", interactive=False, lines=4)
727
- # display_target_a = gr.Textbox(label="Target A (Active)", interactive=False, lines=4)
728
 
729
  with gr.Column(scale=1, variant="panel"):
730
  meta_b = gr.Markdown("### Source B Information")
731
  display_context_b = gr.Textbox(label="Context B", interactive=False, lines=4)
732
 
733
- # --- AI CHATBOT QUERY OPTION ---
734
  with gr.Accordion("💬 Ask AI about the Context & Policies", open=False):
735
  chatbot = gr.Chatbot(height=300)
736
  with gr.Row():
737
  chat_input = gr.Textbox(placeholder="Ask a question about the policies or targets...", scale=4, show_label=False)
738
  chat_submit = gr.Button("Send", scale=1)
739
 
740
- # --- THE SCROLLABLE ROWS ---
741
  with gr.Group(elem_classes="scrollable-rows-container"):
742
  bulk_title = gr.Markdown("### Bulk Coherence Evaluation")
743
  bulk_desc = gr.Markdown(
@@ -746,18 +765,16 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
746
  "You may leave a row entirely blank to skip it."
747
  )
748
 
749
- # --- DYNAMIC BULK ROWS ---
750
  eval_rows = []
751
  for i in range(MAX_ROWS):
752
- with gr.Group(visible=False, elem_classes="row-card") as row_container:
753
  m_coarse_st = gr.State("")
754
  m_drill_st = gr.State("")
755
  m_conf_st = gr.State("")
756
  m_ai_just_st = gr.State("")
757
  m_ig_json_st = gr.State("")
758
 
759
- # FIX: Show Target A and Target B side-by-side in every row block
760
- with gr.Row(equal_height=True):
761
  with gr.Column(scale=1):
762
  a_text_display = gr.Textbox(label="Target A (Active)", interactive=False, lines=3, elem_classes="scrollable-target")
763
  with gr.Column(scale=1):
@@ -769,7 +786,6 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
769
  conf_md = gr.Markdown("")
770
 
771
  with gr.Column(scale=1, min_width=200):
772
- # ADDED allow_custom_value=True to prevent strict validation crashes on swap/clear
773
  inter_dd = gr.Dropdown(choices=[], label="2. Drill Down Interaction", interactive=True, allow_custom_value=True)
774
  explain_btn = gr.Button("✨ AI Explainability", size="sm", elem_classes="explain-btn")
775
  explain_html = gr.HTML("")
@@ -780,7 +796,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
780
 
781
  explain_btn.click(
782
  fn=generate_row_explanation,
783
- inputs=[target_a_list_state, current_index_state, b_text, lang_selector], # Passed lang_selector here
784
  outputs=[explain_html, just_box, m_ai_just_st, m_ig_json_st]
785
  )
786
 
@@ -790,16 +806,37 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
790
  outputs=[rel_radio, inter_dd, just_box]
791
  )
792
 
793
- # Added a_text_display to the tuple
794
  eval_rows.append((row_container, a_text_display, b_text, rel_radio, conf_md, inter_dd, just_box, m_coarse_st, m_drill_st, m_conf_st, m_ai_just_st, m_ig_json_st))
795
 
796
- # --- NAVIGATION BUTTONS ---
797
  with gr.Row():
798
  skip_btn = gr.Button("Skip Target A", size="lg")
799
  save_btn = gr.Button("Save Filled Annotations", variant="primary", size="lg")
800
 
801
  status_box = gr.Textbox(label="System Log", interactive=False)
802
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
803
  # --- NAVIGATION BUTTONS ---
804
  # Kept outside the scrollable box so they are always visible at the very bottom
805
  # with gr.Row():
@@ -1014,7 +1051,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
1014
  def render_target_a(a_list, tasks_dict, idx, lang, user_tag, pol_a, pol_b, hf_df, progress=gr.Progress()):
1015
  updates = []
1016
 
1017
- empty_row = [gr.update(visible=False), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), "", "", "", "", ""]
1018
 
1019
  if not a_list:
1020
  prog_txt = t_text("**Progress:** No unannotated items found.", lang)
@@ -1150,11 +1187,13 @@ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
1150
  progress(0.1, desc="Validating selections...")
1151
  if not pol_a or not pol_b:
1152
  err = t_text("Error: Select both policies.", lang)
1153
- return [gr.update(value=err)] + [gr.skip()] * (14 + MAX_ROWS*12)
 
1154
 
1155
  if tar_col == ctx_col:
1156
  err = t_text("Error: Target and Context cannot be the same.", lang)
1157
- return [gr.update(value=err)] + [gr.skip()] * (14 + MAX_ROWS*12)
 
1158
 
1159
  progress(0.2, desc="Extracting policy structures...")
1160
  df_a = DOMAIN_MAP[dom_a]
 
596
  margin-bottom: 20px !important;
597
  border: 1px solid #e5e7eb !important;
598
  }
599
+
600
+ /* Persistent Header Styling */
601
+ .persistent-header {
602
+ display: flex;
603
+ align-items: center;
604
+ justify-content: space-between;
605
+ padding-bottom: 15px;
606
+ border-bottom: 2px solid #e5e7eb;
607
+ margin-bottom: 20px;
608
+ }
609
  """
610
 
611
+ with gr.Blocks(css=custom_css) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
 
613
  hf_df_state = gr.State()
614
  user_tag_state = gr.State()
 
624
  ctx_a_eng_state = gr.State("")
625
  ctx_b_eng_state = gr.State("")
626
 
627
+ # --- PERSISTENT HEADER ---
628
+ with gr.Row():
629
+ gr.HTML("""
630
+ <div class="persistent-header">
631
+ <div style="display: flex; align-items: center; gap: 15px;">
632
+ <img src="https://epic-africa.org/wp-content/uploads/2023/10/EPIC-Africa-Logo.png" alt="EPIC Africa Logo" style="height: 50px;">
633
+ <h2 style="margin: 0; color: #374151;">Policy Coherence Annotation Tool</h2>
634
+ </div>
635
+ </div>
636
+ """)
637
+ lang_selector = gr.Dropdown(choices=list(LANG_CODES.keys()), value="English", label="Language / Langue", min_width=120)
638
+
639
+ # --- 1. LANDING PAGE ---
640
+ with gr.Column(visible=True) as landing_page:
641
+ gr.Markdown(
642
+ "### Mapping Policy Synergies Across Sectors\n\n"
643
+ "Welcome to the EPIC Africa Policy Coherence Annotation Tool. This platform is designed to help researchers and policymakers "
644
+ "systematically evaluate how specific objectives within different policy documents interact with one another.\n\n"
645
+ "**How it works:**\n"
646
+ "You will be presented with a target objective from one policy and asked to score its interaction against objectives from a different policy. "
647
+ "By identifying whether these targets reinforce, enable, or constrain one another, you help build a comprehensive understanding of cross-sectoral coherence.\n\n"
648
+ "Click below to authenticate and begin your session."
649
+ )
650
+ start_btn = gr.Button("Get Started", variant="primary", size="lg")
651
+
652
+ # --- 2. LOGIN PANEL ---
653
+ with gr.Column(visible=False) as login_box:
654
  login_title = gr.Markdown("### User Authentication & Informed Consent")
655
 
656
  gr.Markdown(
 
673
  login_btn = gr.Button("Login & Accept", variant="primary")
674
  login_status = gr.Markdown(value="Waiting for authentication...")
675
 
 
676
  def toggle_followup(link_choice):
677
  if link_choice == "Yes":
678
  return gr.update(interactive=True)
 
681
 
682
  consent_link_radio.change(fn=toggle_followup, inputs=consent_link_radio, outputs=consent_follow_radio)
683
 
684
+ # --- 3. EXPERTISE / SECTOR SELECTION ---
685
+ with gr.Column(visible=False) as sector_box:
686
  gr.Markdown("### What is your expertise?")
687
  sector_cb = gr.CheckboxGroup(
688
  choices=SECTOR_CHOICES,
 
690
  )
691
  proceed_btn = gr.Button("Proceed to Workspace", variant="primary", size="lg")
692
 
693
+ # --- 4. MAIN APPLICATION ---
694
+ with gr.Column(visible=False) as app_box:
695
+
696
+ main_desc = gr.Markdown(
697
+ "**Definitions:**\n"
698
+ "- **Nexus Domain:** The broad sector being analyzed (e.g., Land, Water, Energy).\n"
699
+ "- **Policy:** The specific document under review.\n"
700
+ "- **Target:** The exact objective or statement you are currently evaluating.\n"
701
+ "- **Context:** The broader set of measures belonging to the policy, provided as background reference."
702
+ )
703
 
704
+ with gr.Accordion("Interaction Class Definitions (Click to Expand)", open=False):
705
+ gr.Markdown(
706
+ "| Interaction Label | Meaning | Implication |\n"
707
+ "| :--- | :--- | :--- |\n"
708
+ "| **+3 (Indivisible)** | Progress on one target automatically delivers progress on another | There is high level of compatibility between the two targets. |\n"
709
+ "| **+2 (Reinforcing)** | Progress on one target makes it easier to make progress on another | There is relatively higher level of compatibility between the targets being compared. |\n"
710
+ "| **+1 (Enabling)** | Progress on one target creates conditions that enable progress on another | There is a small level of compatibility between the two targets compared. |\n"
711
+ "| **0 (Consistent)** | There is no significant link between two targets' progress | There is no significant compatibility between the two targets being evaluated. |\n"
712
+ "| **-1 (Constraining)** | Progress on one target constrains the options for how to deliver on another | The targets are relatively competitive resulting in counterproductive effects. |\n"
713
+ "| **-2 (Counteracting)** | Progress on one target makes it more difficult to make progress on another | The targets are counterproductive and do not support each other. |\n"
714
+ "| **-3 (Cancelling)** | Progress on one target automatically leads to a negative impact on another | The targets are highly opposite and are highly counterproductive. Cannot deliver synergistic effects. |"
715
+ )
716
+
717
  with gr.Accordion("Data Selection", open=True) as data_acc:
 
 
718
  with gr.Row():
719
  back_to_sectors_btn = gr.Button("⬅️ Back to Sector Selection", variant="secondary", size="sm")
720
+ gr.Markdown("")
721
  gr.Markdown("")
722
 
723
  with gr.Row():
 
735
  target_col_dd = gr.Dropdown(choices=AVAILABLE_COLUMNS, value='Strategic objectives / directions', label="Target Column")
736
  context_col_dd = gr.Dropdown(choices=AVAILABLE_COLUMNS, value='Policy Actions and Measures (PAMs)', label="Context Column")
737
 
 
738
  load_btn = gr.Button("Fetch Data", variant="primary")
739
 
740
  gr.Markdown("---")
741
  progress_text = gr.Markdown("**Progress:** Waiting for data selection...")
742
 
743
+ with gr.Column(visible=False) as workspace_box:
744
 
 
 
745
  with gr.Row():
746
  with gr.Column(scale=1, variant="panel"):
747
  meta_a = gr.Markdown("### Source A Information")
748
  display_context_a = gr.Textbox(label="Context A", interactive=False, lines=4)
 
749
 
750
  with gr.Column(scale=1, variant="panel"):
751
  meta_b = gr.Markdown("### Source B Information")
752
  display_context_b = gr.Textbox(label="Context B", interactive=False, lines=4)
753
 
 
754
  with gr.Accordion("💬 Ask AI about the Context & Policies", open=False):
755
  chatbot = gr.Chatbot(height=300)
756
  with gr.Row():
757
  chat_input = gr.Textbox(placeholder="Ask a question about the policies or targets...", scale=4, show_label=False)
758
  chat_submit = gr.Button("Send", scale=1)
759
 
 
760
  with gr.Group(elem_classes="scrollable-rows-container"):
761
  bulk_title = gr.Markdown("### Bulk Coherence Evaluation")
762
  bulk_desc = gr.Markdown(
 
765
  "You may leave a row entirely blank to skip it."
766
  )
767
 
 
768
  eval_rows = []
769
  for i in range(MAX_ROWS):
770
+ with gr.Column(visible=False, elem_classes="row-card") as row_container:
771
  m_coarse_st = gr.State("")
772
  m_drill_st = gr.State("")
773
  m_conf_st = gr.State("")
774
  m_ai_just_st = gr.State("")
775
  m_ig_json_st = gr.State("")
776
 
777
+ with gr.Row():
 
778
  with gr.Column(scale=1):
779
  a_text_display = gr.Textbox(label="Target A (Active)", interactive=False, lines=3, elem_classes="scrollable-target")
780
  with gr.Column(scale=1):
 
786
  conf_md = gr.Markdown("")
787
 
788
  with gr.Column(scale=1, min_width=200):
 
789
  inter_dd = gr.Dropdown(choices=[], label="2. Drill Down Interaction", interactive=True, allow_custom_value=True)
790
  explain_btn = gr.Button("✨ AI Explainability", size="sm", elem_classes="explain-btn")
791
  explain_html = gr.HTML("")
 
796
 
797
  explain_btn.click(
798
  fn=generate_row_explanation,
799
+ inputs=[target_a_list_state, current_index_state, b_text, lang_selector],
800
  outputs=[explain_html, just_box, m_ai_just_st, m_ig_json_st]
801
  )
802
 
 
806
  outputs=[rel_radio, inter_dd, just_box]
807
  )
808
 
 
809
  eval_rows.append((row_container, a_text_display, b_text, rel_radio, conf_md, inter_dd, just_box, m_coarse_st, m_drill_st, m_conf_st, m_ai_just_st, m_ig_json_st))
810
 
 
811
  with gr.Row():
812
  skip_btn = gr.Button("Skip Target A", size="lg")
813
  save_btn = gr.Button("Save Filled Annotations", variant="primary", size="lg")
814
 
815
  status_box = gr.Textbox(label="System Log", interactive=False)
816
 
817
+ # --- PERSISTENT FOOTER ---
818
+ gr.Markdown(
819
+ "---\n"
820
+ "<div style='text-align: center; color: #6b7280; font-size: 0.9em; padding: 10px;'>"
821
+ "<strong>Disclaimer:</strong> This tool is developed and maintained by EPIC Africa. "
822
+ "The European Union (EU) is not liable for the content, use, or outputs generated by this tool."
823
+ "</div>"
824
+ )
825
+
826
+ # --- Event binding for the Landing Page ---
827
+ start_btn.click(
828
+ fn=lambda: (gr.update(visible=False), gr.update(visible=True)),
829
+ inputs=None,
830
+ outputs=[landing_page, login_box]
831
+ )
832
+
833
+ # --- NAVIGATION BUTTONS ---
834
+ # with gr.Row():
835
+ # skip_btn = gr.Button("Skip Target A", size="lg")
836
+ # save_btn = gr.Button("Save Filled Annotations", variant="primary", size="lg")
837
+
838
+ # status_box = gr.Textbox(label="System Log", interactive=False)
839
+
840
  # --- NAVIGATION BUTTONS ---
841
  # Kept outside the scrollable box so they are always visible at the very bottom
842
  # with gr.Row():
 
1051
  def render_target_a(a_list, tasks_dict, idx, lang, user_tag, pol_a, pol_b, hf_df, progress=gr.Progress()):
1052
  updates = []
1053
 
1054
+ empty_row = [gr.update(visible=False)] + [gr.skip()] * 11
1055
 
1056
  if not a_list:
1057
  prog_txt = t_text("**Progress:** No unannotated items found.", lang)
 
1187
  progress(0.1, desc="Validating selections...")
1188
  if not pol_a or not pol_b:
1189
  err = t_text("Error: Select both policies.", lang)
1190
+ # return [gr.update(value=err)] + [gr.skip()] * (14 + MAX_ROWS*12)
1191
+ return [gr.skip()] * 6 + [gr.update(value=err)] + [gr.skip()] * (6 + MAX_ROWS*12)
1192
 
1193
  if tar_col == ctx_col:
1194
  err = t_text("Error: Target and Context cannot be the same.", lang)
1195
+ # return [gr.update(value=err)] + [gr.skip()] * (14 + MAX_ROWS*12)
1196
+ return [gr.skip()] * 6 + [gr.update(value=err)] + [gr.skip()] * (6 + MAX_ROWS*12)
1197
 
1198
  progress(0.2, desc="Extracting policy structures...")
1199
  df_a = DOMAIN_MAP[dom_a]