Urdatorn commited on
Commit
16bc79c
·
1 Parent(s): a594617
Files changed (5) hide show
  1. apitest.py +9 -0
  2. app.py +99 -19
  3. pytest.ini +1 -0
  4. tests/test_markup.py +22 -0
  5. tests/test_online_api.py +14 -0
apitest.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from gradio_client import Client
2
+
3
+ client = Client("al1808th/macronizer")
4
+ result = client.predict(
5
+ text="νεανίας ἀάατός ἐστιν καὶ καλός. τὰ παῖδες τὰ καλά",
6
+ model_label="Macronizer Mega v1 (Pretrained ModernBERT)",
7
+ api_name="/render_results",
8
+ )
9
+ print(result)
app.py CHANGED
@@ -499,6 +499,14 @@ def render_results(text: str, model_label: str):
499
  return html_result, "\n".join(plain_lines)
500
 
501
 
 
 
 
 
 
 
 
 
502
  examples = [
503
  "νεανίας ἀάατός ἐστιν καὶ καλός. τὰ παῖδες τὰ καλά\nκαλὰ μὲν ἠέξευ, καλὰ δ᾽ ἔτραφες, οὐράνιε Ζεῦ,",
504
  "Ἆρες, Ἄρες βροτολοιγὲ μιαιφόνε τειχεσιπλῆτα\nἈτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί",
@@ -542,7 +550,9 @@ CSS = """
542
  }
543
 
544
  html.dark-mode,
545
- body.dark-mode {
 
 
546
  --bg-start: #050506;
547
  --bg-end: #101015;
548
  --ink: #f3f3f8;
@@ -558,7 +568,9 @@ body.dark-mode {
558
  }
559
 
560
  html.light-mode,
561
- body.light-mode {
 
 
562
  --bg-start: #f5f0e8;
563
  --bg-end: #e8edf4;
564
  --ink: #23242a;
@@ -576,14 +588,27 @@ body.light-mode {
576
  .gradio-container {
577
  font-family: 'Space Grotesk', sans-serif;
578
  background: radial-gradient(circle at top left, var(--bg-start), var(--bg-end));
579
- color: var(--ink);
580
  transition: background-color 0.3s, color 0.3s;
 
581
  }
582
 
583
- .dark-mode-toggle {
584
- position: fixed;
585
  top: 20px;
586
  right: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
587
  background: var(--paper);
588
  border: 2px solid var(--ink);
589
  color: var(--ink);
@@ -593,15 +618,28 @@ body.light-mode {
593
  font-weight: 600;
594
  font-family: 'Space Grotesk', sans-serif;
595
  font-size: 0.95rem;
596
- z-index: 1000;
 
 
597
  transition: all 0.3s;
598
  }
599
 
600
- .dark-mode-toggle:hover {
 
 
601
  transform: scale(1.05);
602
  opacity: 0.9;
603
  }
604
 
 
 
 
 
 
 
 
 
 
605
  .title h1 {
606
  font-family: 'Cormorant Garamond', serif;
607
  font-size: 3rem;
@@ -629,7 +667,12 @@ body.light-mode {
629
  .panel .gr-markdown,
630
  .panel .gradio-markdown,
631
  .panel .gr-form label,
632
- .panel .gr-form span {
 
 
 
 
 
633
  color: var(--ink) !important;
634
  }
635
 
@@ -641,7 +684,10 @@ body.light-mode {
641
  .panel .gr-radio,
642
  .panel .gr-radio label,
643
  .panel .gr-box,
644
- .panel .gr-form {
 
 
 
645
  color: var(--ink) !important;
646
  }
647
 
@@ -659,7 +705,7 @@ body.light-mode {
659
 
660
  .dark-mode .panel .gr-button,
661
  .dark-mode .panel button {
662
- color: #f6f2e8 !important;
663
  border-color: rgba(232, 228, 220, 0.28) !important;
664
  }
665
 
@@ -669,6 +715,10 @@ body.light-mode {
669
  color: #f7f9ff !important;
670
  }
671
 
 
 
 
 
672
  .legend {
673
  display: flex;
674
  align-items: center;
@@ -755,11 +805,10 @@ body.light-mode {
755
 
756
  @media (max-width: 820px) {
757
  .title h1 { font-size: 2.2rem; }
758
- .dark-mode-toggle {
759
- position: relative;
760
- top: auto;
761
- right: auto;
762
- margin-bottom: 1rem;
763
  }
764
  }
765
  """
@@ -770,9 +819,11 @@ THEME_INIT_JS = """
770
  const isDark = mode === 'dark';
771
  document.documentElement.classList.toggle('dark-mode', isDark);
772
  document.documentElement.classList.toggle('light-mode', !isDark);
 
773
  if (document.body) {
774
  document.body.classList.toggle('dark-mode', isDark);
775
  document.body.classList.toggle('light-mode', !isDark);
 
776
  }
777
  return isDark ? 'Light Mode' : 'Dark Mode';
778
  }
@@ -791,9 +842,11 @@ THEME_TOGGLE_JS = """
791
  const isDark = mode === 'dark';
792
  document.documentElement.classList.toggle('dark-mode', isDark);
793
  document.documentElement.classList.toggle('light-mode', !isDark);
 
794
  if (document.body) {
795
  document.body.classList.toggle('dark-mode', isDark);
796
  document.body.classList.toggle('light-mode', !isDark);
 
797
  }
798
  localStorage.setItem('themeMode', mode);
799
  return isDark ? 'Light Mode' : 'Dark Mode';
@@ -816,7 +869,10 @@ with gr.Blocks() as demo:
816
  """
817
  <div class="title">
818
  <h1>Ancient Greek Macronizer</h1>
819
- <p>Enter Ancient Greek to have the alphas, iotas and ypsilons marked as long or short. Made by Albin Thörn Cleland (Lund university) and Eric Cullhed (Uppsala university). Training the models was made possible by resources provided by the National Academic Infrastructure for Supercomputing in Sweden (NAISS), partially funded by the Swedish Research Council (grant agreement no. 2022-06725).</p>
 
 
 
820
  </div>
821
  """
822
  )
@@ -836,14 +892,38 @@ with gr.Blocks() as demo:
836
  with gr.Row():
837
  classify_btn = gr.Button("Classify", variant="primary")
838
  clear_btn = gr.Button("Clear")
839
- gr.Examples(examples=examples, inputs=text_input, label="Try examples")
 
 
 
 
 
840
 
841
  with gr.Column(elem_classes=["panel"]):
842
  html_output = gr.HTML(label="Styled Results")
843
  text_output = gr.Textbox(label="Plain Output", lines=12, buttons=["copy"])
844
 
845
- classify_btn.click(render_results, inputs=[text_input, model_choice], outputs=[html_output, text_output])
846
- clear_btn.click(lambda: ("", "", ""), outputs=[text_input, html_output, text_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
847
  theme_toggle.click(fn=None, inputs=None, outputs=theme_toggle, js=THEME_TOGGLE_JS)
848
  demo.load(fn=None, inputs=None, outputs=theme_toggle, js=THEME_INIT_JS)
849
 
 
499
  return html_result, "\n".join(plain_lines)
500
 
501
 
502
+ def render_plain_output(text: str) -> str:
503
+ lines = [line.strip() for line in text.splitlines() if line.strip()]
504
+ if not lines:
505
+ return ""
506
+
507
+ return "\n".join(_render_plain_line_per_word(line, DEFAULT_MODEL_ID) for line in lines)
508
+
509
+
510
  examples = [
511
  "νεανίας ἀάατός ἐστιν καὶ καλός. τὰ παῖδες τὰ καλά\nκαλὰ μὲν ἠέξευ, καλὰ δ᾽ ἔτραφες, οὐράνιε Ζεῦ,",
512
  "Ἆρες, Ἄρες βροτολοιγὲ μιαιφόνε τειχεσιπλῆτα\nἈτρεΐδαι τε καὶ ἄλλοι ἐϋκνήμιδες Ἀχαιοί",
 
550
  }
551
 
552
  html.dark-mode,
553
+ body.dark-mode,
554
+ html[data-theme="dark"],
555
+ body[data-theme="dark"] {
556
  --bg-start: #050506;
557
  --bg-end: #101015;
558
  --ink: #f3f3f8;
 
568
  }
569
 
570
  html.light-mode,
571
+ body.light-mode,
572
+ html[data-theme="light"],
573
+ body[data-theme="light"] {
574
  --bg-start: #f5f0e8;
575
  --bg-end: #e8edf4;
576
  --ink: #23242a;
 
588
  .gradio-container {
589
  font-family: 'Space Grotesk', sans-serif;
590
  background: radial-gradient(circle at top left, var(--bg-start), var(--bg-end));
591
+ color: var(--ink) !important;
592
  transition: background-color 0.3s, color 0.3s;
593
+ min-height: 100vh;
594
  }
595
 
596
+ #dark-mode-toggle {
597
+ position: fixed !important;
598
  top: 20px;
599
  right: 20px;
600
+ width: auto !important;
601
+ min-width: 0 !important;
602
+ max-width: max-content !important;
603
+ flex: 0 0 auto !important;
604
+ display: inline-flex !important;
605
+ z-index: 1000;
606
+ }
607
+
608
+ #dark-mode-toggle button,
609
+ #dark-mode-toggle .gr-button,
610
+ .dark-mode-toggle button,
611
+ button.dark-mode-toggle {
612
  background: var(--paper);
613
  border: 2px solid var(--ink);
614
  color: var(--ink);
 
618
  font-weight: 600;
619
  font-family: 'Space Grotesk', sans-serif;
620
  font-size: 0.95rem;
621
+ width: auto !important;
622
+ min-width: 0 !important;
623
+ white-space: nowrap;
624
  transition: all 0.3s;
625
  }
626
 
627
+ #dark-mode-toggle:hover,
628
+ #dark-mode-toggle button:hover,
629
+ .dark-mode-toggle button:hover {
630
  transform: scale(1.05);
631
  opacity: 0.9;
632
  }
633
 
634
+ .title,
635
+ .title h1,
636
+ .title p,
637
+ .gradio-container .title,
638
+ .gradio-container .title h1,
639
+ .gradio-container .title p {
640
+ color: var(--ink) !important;
641
+ }
642
+
643
  .title h1 {
644
  font-family: 'Cormorant Garamond', serif;
645
  font-size: 3rem;
 
667
  .panel .gr-markdown,
668
  .panel .gradio-markdown,
669
  .panel .gr-form label,
670
+ .panel .gr-form span,
671
+ .panel span,
672
+ .panel p,
673
+ .panel button,
674
+ .panel .prose,
675
+ .panel .prose * {
676
  color: var(--ink) !important;
677
  }
678
 
 
684
  .panel .gr-radio,
685
  .panel .gr-radio label,
686
  .panel .gr-box,
687
+ .panel .gr-form,
688
+ .panel .wrap,
689
+ .panel .block,
690
+ .panel .block * {
691
  color: var(--ink) !important;
692
  }
693
 
 
705
 
706
  .dark-mode .panel .gr-button,
707
  .dark-mode .panel button {
708
+ color: var(--ink) !important;
709
  border-color: rgba(232, 228, 220, 0.28) !important;
710
  }
711
 
 
715
  color: #f7f9ff !important;
716
  }
717
 
718
+ #try-examples .example-text {
719
+ white-space: pre-wrap;
720
+ }
721
+
722
  .legend {
723
  display: flex;
724
  align-items: center;
 
805
 
806
  @media (max-width: 820px) {
807
  .title h1 { font-size: 2.2rem; }
808
+ #dark-mode-toggle {
809
+ position: fixed !important;
810
+ top: 12px;
811
+ right: 12px;
 
812
  }
813
  }
814
  """
 
819
  const isDark = mode === 'dark';
820
  document.documentElement.classList.toggle('dark-mode', isDark);
821
  document.documentElement.classList.toggle('light-mode', !isDark);
822
+ document.documentElement.dataset.theme = mode;
823
  if (document.body) {
824
  document.body.classList.toggle('dark-mode', isDark);
825
  document.body.classList.toggle('light-mode', !isDark);
826
+ document.body.dataset.theme = mode;
827
  }
828
  return isDark ? 'Light Mode' : 'Dark Mode';
829
  }
 
842
  const isDark = mode === 'dark';
843
  document.documentElement.classList.toggle('dark-mode', isDark);
844
  document.documentElement.classList.toggle('light-mode', !isDark);
845
+ document.documentElement.dataset.theme = mode;
846
  if (document.body) {
847
  document.body.classList.toggle('dark-mode', isDark);
848
  document.body.classList.toggle('light-mode', !isDark);
849
+ document.body.dataset.theme = mode;
850
  }
851
  localStorage.setItem('themeMode', mode);
852
  return isDark ? 'Light Mode' : 'Dark Mode';
 
869
  """
870
  <div class="title">
871
  <h1>Ancient Greek Macronizer</h1>
872
+ <p>
873
+ Enter Ancient Greek to have the alphas, iotas and ypsilons marked as long or short. <br><br>
874
+ Made by Albin Thörn Cleland (Lund university) and Eric Cullhed (Uppsala university). Training the models was made possible by resources provided by the National Academic Infrastructure for Supercomputing in Sweden (NAISS), partially funded by the Swedish Research Council (grant agreement no. 2022-06725).
875
+ </p>
876
  </div>
877
  """
878
  )
 
892
  with gr.Row():
893
  classify_btn = gr.Button("Classify", variant="primary")
894
  clear_btn = gr.Button("Clear")
895
+ gr.Examples(
896
+ examples=examples,
897
+ inputs=text_input,
898
+ label="Try examples",
899
+ elem_id="try-examples",
900
+ )
901
 
902
  with gr.Column(elem_classes=["panel"]):
903
  html_output = gr.HTML(label="Styled Results")
904
  text_output = gr.Textbox(label="Plain Output", lines=12, buttons=["copy"])
905
 
906
+ api_text = gr.Textbox(label="text", visible=False)
907
+ api_output = gr.Textbox(label="plain_output", visible=False)
908
+ api_btn = gr.Button("API", visible=False)
909
+
910
+ classify_btn.click(
911
+ render_results,
912
+ inputs=[text_input, model_choice],
913
+ outputs=[html_output, text_output],
914
+ api_name=False,
915
+ )
916
+ clear_btn.click(
917
+ lambda: ("", "", ""),
918
+ outputs=[text_input, html_output, text_output],
919
+ api_name=False,
920
+ )
921
+ api_btn.click(
922
+ render_plain_output,
923
+ inputs=api_text,
924
+ outputs=api_output,
925
+ api_name="render_results",
926
+ )
927
  theme_toggle.click(fn=None, inputs=None, outputs=theme_toggle, js=THEME_TOGGLE_JS)
928
  demo.load(fn=None, inputs=None, outputs=theme_toggle, js=THEME_INIT_JS)
929
 
pytest.ini CHANGED
@@ -10,3 +10,4 @@ addopts = -v --tb=short
10
  # Markers
11
  markers =
12
  markup: Tests for markup output preservation
 
 
10
  # Markers
11
  markers =
12
  markup: Tests for markup output preservation
13
+ online_api: Tests that call the hosted Gradio API
tests/test_markup.py CHANGED
@@ -18,6 +18,7 @@ from app import (
18
  _render_plain_line_per_word,
19
  _render_styled_line_per_word,
20
  preprocess_and_syllabify,
 
21
  render_results,
22
  )
23
 
@@ -158,5 +159,26 @@ def test_plain_output_box_contains_only_output_lines(monkeypatch):
158
  assert '<div class="line-output">alpha</div>' in html_output
159
 
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  if __name__ == "__main__":
162
  pytest.main([__file__, "-v", "-s"])
 
18
  _render_plain_line_per_word,
19
  _render_styled_line_per_word,
20
  preprocess_and_syllabify,
21
+ render_plain_output,
22
  render_results,
23
  )
24
 
 
159
  assert '<div class="line-output">alpha</div>' in html_output
160
 
161
 
162
+ def test_api_returns_only_plain_output(monkeypatch):
163
+ monkeypatch.setattr("app._render_plain_line_per_word", lambda line, model_id: f"marked {line}")
164
+
165
+ assert render_plain_output("alpha\nbeta") == "marked alpha\nmarked beta"
166
+
167
+
168
+ def test_public_api_endpoint_is_plain_output_only():
169
+ import app
170
+
171
+ public_deps = [
172
+ dep
173
+ for dep in app.demo.config.get("dependencies", [])
174
+ if dep.get("api_name") == "render_results"
175
+ and dep.get("api_visibility") == "public"
176
+ ]
177
+
178
+ assert len(public_deps) == 1
179
+ assert len(public_deps[0].get("inputs", [])) == 1
180
+ assert len(public_deps[0].get("outputs", [])) == 1
181
+
182
+
183
  if __name__ == "__main__":
184
  pytest.main([__file__, "-v", "-s"])
tests/test_online_api.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gradio_client import Client
2
+ import pytest
3
+
4
+
5
+ @pytest.mark.online_api
6
+ def test_online_api_returns_plain_output_only():
7
+ client = Client("al1808th/macronizer")
8
+
9
+ result = client.predict(
10
+ text="νεανίας ἀάατός ἐστιν καὶ καλός. τὰ παῖδες τὰ καλά",
11
+ api_name="/render_results",
12
+ )
13
+
14
+ assert result == "νεα_νί^ας ἀ^ά_α^τός ἐστι^ν καὶ κα^λός. τὰ^ παῖδες τὰ^ κα_λά_"