Upload folder using huggingface_hub
Browse files- __pycache__/app.cpython-310.pyc +0 -0
- app.py +76 -64
__pycache__/app.cpython-310.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-310.pyc and b/__pycache__/app.cpython-310.pyc differ
|
|
|
app.py
CHANGED
|
@@ -35,6 +35,8 @@ CSS = """
|
|
| 35 |
--accent-deep: #0c4641;
|
| 36 |
--accent-soft: #dbeee8;
|
| 37 |
--accent-warm: #b8643d;
|
|
|
|
|
|
|
| 38 |
--accent-warm-soft: #f3e1d5;
|
| 39 |
--line: #c6cdbf;
|
| 40 |
--warning: #8a4b0f;
|
|
@@ -122,13 +124,6 @@ CSS = """
|
|
| 122 |
margin-top: 0.45rem;
|
| 123 |
}
|
| 124 |
|
| 125 |
-
.panel-note {
|
| 126 |
-
border-left: 4px solid var(--accent);
|
| 127 |
-
background: rgba(19,90,82,0.06);
|
| 128 |
-
padding: 0.9rem 1rem;
|
| 129 |
-
border-radius: 12px;
|
| 130 |
-
}
|
| 131 |
-
|
| 132 |
.metric-strip {
|
| 133 |
display: grid;
|
| 134 |
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
@@ -140,7 +135,7 @@ CSS = """
|
|
| 140 |
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(248,244,236,0.9));
|
| 141 |
padding: 0.8rem 0.9rem;
|
| 142 |
border-radius: 18px;
|
| 143 |
-
min-height:
|
| 144 |
box-shadow: 0 8px 24px rgba(23,37,45,0.04);
|
| 145 |
}
|
| 146 |
|
|
@@ -311,11 +306,32 @@ CSS = """
|
|
| 311 |
|
| 312 |
.gradio-container input,
|
| 313 |
.gradio-container textarea,
|
| 314 |
-
.gradio-container select
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
background: #fffdf8 !important;
|
| 316 |
border: 1px solid #bfc8bf !important;
|
| 317 |
border-radius: 14px !important;
|
| 318 |
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7) !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
}
|
| 320 |
|
| 321 |
.gradio-container input:focus,
|
|
@@ -336,14 +352,14 @@ CSS = """
|
|
| 336 |
}
|
| 337 |
|
| 338 |
.gradio-container button.primary {
|
| 339 |
-
background: linear-gradient(180deg, var(--accent), var(--accent-deep)) !important;
|
| 340 |
color: #f7fbfa !important;
|
| 341 |
border: none !important;
|
| 342 |
-
box-shadow: 0
|
| 343 |
}
|
| 344 |
|
| 345 |
.gradio-container button.secondary {
|
| 346 |
-
background: linear-gradient(180deg, #
|
| 347 |
color: var(--ink) !important;
|
| 348 |
border: 1px solid #dbc4b4 !important;
|
| 349 |
}
|
|
@@ -458,6 +474,9 @@ EXAMPLES = {
|
|
| 458 |
},
|
| 459 |
}
|
| 460 |
|
|
|
|
|
|
|
|
|
|
| 461 |
|
| 462 |
def _parse_smiles_text(value: str | None) -> list[str]:
|
| 463 |
if not value:
|
|
@@ -715,19 +734,19 @@ with gr.Blocks(title="BioAssayAlign Compatibility Explorer", analytics_enabled=F
|
|
| 715 |
<div id="hero">
|
| 716 |
<div class="hero-grid">
|
| 717 |
<div>
|
| 718 |
-
<div class="eyebrow">BioAssayAlign · assay-conditioned
|
| 719 |
-
<div class="hero-title">
|
| 720 |
<div class="hero-copy">
|
| 721 |
-
|
| 722 |
-
The output is
|
| 723 |
</div>
|
| 724 |
</div>
|
| 725 |
<div class="hero-side">
|
| 726 |
-
<div class="hero-side-title">
|
| 727 |
<ul class="hero-list">
|
| 728 |
-
<li>
|
| 729 |
-
<li>
|
| 730 |
-
<li>
|
| 731 |
</ul>
|
| 732 |
</div>
|
| 733 |
</div>
|
|
@@ -736,21 +755,13 @@ with gr.Blocks(title="BioAssayAlign Compatibility Explorer", analytics_enabled=F
|
|
| 736 |
)
|
| 737 |
|
| 738 |
with gr.Row():
|
| 739 |
-
with gr.Column(scale=
|
| 740 |
-
gr.HTML(
|
| 741 |
-
"""
|
| 742 |
-
<div class="panel-note">
|
| 743 |
-
Use structured assay fields when possible. Missing fields are allowed, but species, readout, format, and target metadata usually improve ranking quality.
|
| 744 |
-
</div>
|
| 745 |
-
"""
|
| 746 |
-
)
|
| 747 |
-
with gr.Column(scale=4):
|
| 748 |
gr.HTML(
|
| 749 |
f"""
|
| 750 |
<div class="metric-strip">
|
| 751 |
-
<div class="metric-card"><span>
|
| 752 |
-
<div class="metric-card"><span>
|
| 753 |
-
<div class="metric-card"><span>
|
| 754 |
</div>
|
| 755 |
"""
|
| 756 |
)
|
|
@@ -758,9 +769,9 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
|
|
| 758 |
gr.HTML(
|
| 759 |
"""
|
| 760 |
<div class="guide-grid">
|
| 761 |
-
<div class="guide-card"><strong>1.
|
| 762 |
-
<div class="guide-card"><strong>2.
|
| 763 |
-
<div class="guide-card"><strong>3.
|
| 764 |
</div>
|
| 765 |
"""
|
| 766 |
)
|
|
@@ -773,29 +784,29 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
|
|
| 773 |
<div class="pane-header">
|
| 774 |
<div>
|
| 775 |
<div class="pane-kicker">Assay definition</div>
|
| 776 |
-
<div class="pane-title">
|
| 777 |
</div>
|
| 778 |
</div>
|
| 779 |
-
<div class="pane-copy">The model performs best when the assay
|
| 780 |
<div class="helper-row">
|
| 781 |
-
<div class="helper-chip"><strong>
|
| 782 |
-
<div class="helper-chip"><strong>
|
| 783 |
-
<div class="helper-chip"><strong>
|
| 784 |
</div>
|
| 785 |
"""
|
| 786 |
)
|
| 787 |
-
example_name = gr.Dropdown(choices=list(EXAMPLES.keys()), value=
|
| 788 |
-
gr.HTML("<div class='examples-note'>
|
| 789 |
-
load_example_btn = gr.Button("
|
| 790 |
-
assay_title = gr.Textbox(label="Assay title")
|
| 791 |
-
description = gr.Textbox(label="Description", lines=6, placeholder="Describe the assay in practical lab language.")
|
| 792 |
with gr.Row():
|
| 793 |
-
organism = gr.Textbox(label="Organism", placeholder="Homo sapiens")
|
| 794 |
-
readout = gr.Textbox(label="Readout", placeholder="binding / fluorescence / luminescence")
|
| 795 |
with gr.Row():
|
| 796 |
-
assay_format = gr.Textbox(label="Assay format", placeholder="biochemical / cell-based")
|
| 797 |
-
assay_type = gr.Textbox(label="Assay type", placeholder="binding / inhibition / activation")
|
| 798 |
-
target_uniprot = gr.Textbox(label="Target UniProt IDs", placeholder="Q06187, P52333")
|
| 799 |
|
| 800 |
with gr.Column(scale=5, elem_classes="pane"):
|
| 801 |
gr.HTML(
|
|
@@ -803,22 +814,23 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
|
|
| 803 |
<div class="pane-header">
|
| 804 |
<div>
|
| 805 |
<div class="pane-kicker">Candidate set</div>
|
| 806 |
-
<div class="pane-title">Submit the
|
| 807 |
</div>
|
| 808 |
</div>
|
| 809 |
-
<div class="pane-copy">The model ranks
|
| 810 |
"""
|
| 811 |
)
|
| 812 |
smiles_text = gr.Textbox(
|
| 813 |
label="Candidate SMILES",
|
|
|
|
| 814 |
lines=14,
|
| 815 |
placeholder="Paste one candidate molecule per line. Example: CCO",
|
| 816 |
)
|
| 817 |
upload_file = gr.File(label="Upload CSV / TXT / SMI", file_count="single", file_types=[".csv", ".txt", ".smi", ".smiles"])
|
| 818 |
top_k = gr.Slider(label="Top-K rows to display", minimum=5, maximum=200, step=5, value=DEFAULT_TOP_K)
|
| 819 |
-
gr.HTML("<div class='section-note'>
|
| 820 |
with gr.Row(elem_classes="action-row"):
|
| 821 |
-
run_btn = gr.Button("Run ranking", variant="primary")
|
| 822 |
clear_btn = gr.ClearButton(value="Clear inputs", components=[assay_title, description, organism, readout, assay_format, assay_type, target_uniprot, smiles_text, upload_file])
|
| 823 |
|
| 824 |
gr.HTML(
|
|
@@ -827,13 +839,13 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
|
|
| 827 |
<div class="pane-header">
|
| 828 |
<div>
|
| 829 |
<div class="pane-kicker">Output</div>
|
| 830 |
-
<div class="pane-title">Interpret the shortlist</div>
|
| 831 |
</div>
|
| 832 |
</div>
|
| 833 |
<div class="explain-grid">
|
| 834 |
-
<div class="explain-card"><strong>Priority</strong>
|
| 835 |
-
<div class="explain-card"><strong>Relative score</strong>0–100 scale within
|
| 836 |
-
<div class="explain-card"><strong>
|
| 837 |
</div>
|
| 838 |
</div>
|
| 839 |
"""
|
|
@@ -863,17 +875,17 @@ Use structured assay fields when possible. Missing fields are allowed, but speci
|
|
| 863 |
<div class="pane-header">
|
| 864 |
<div>
|
| 865 |
<div class="pane-kicker">Practical guidance</div>
|
| 866 |
-
<div class="pane-title">
|
| 867 |
</div>
|
| 868 |
</div>
|
| 869 |
<div class="explain-grid">
|
| 870 |
-
<div class="explain-card"><strong>
|
| 871 |
-
<div class="explain-card"><strong>
|
| 872 |
-
<div class="explain-card"><strong>
|
| 873 |
</div>
|
| 874 |
<div class="footer-note" style="margin-top: 1rem;">
|
| 875 |
-
This Space is
|
| 876 |
-
|
| 877 |
</div>
|
| 878 |
</div>
|
| 879 |
"""
|
|
|
|
| 35 |
--accent-deep: #0c4641;
|
| 36 |
--accent-soft: #dbeee8;
|
| 37 |
--accent-warm: #b8643d;
|
| 38 |
+
--accent-bright: #d06a3b;
|
| 39 |
+
--accent-bright-deep: #b25124;
|
| 40 |
--accent-warm-soft: #f3e1d5;
|
| 41 |
--line: #c6cdbf;
|
| 42 |
--warning: #8a4b0f;
|
|
|
|
| 124 |
margin-top: 0.45rem;
|
| 125 |
}
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
.metric-strip {
|
| 128 |
display: grid;
|
| 129 |
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
| 135 |
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(248,244,236,0.9));
|
| 136 |
padding: 0.8rem 0.9rem;
|
| 137 |
border-radius: 18px;
|
| 138 |
+
min-height: 6.4rem;
|
| 139 |
box-shadow: 0 8px 24px rgba(23,37,45,0.04);
|
| 140 |
}
|
| 141 |
|
|
|
|
| 306 |
|
| 307 |
.gradio-container input,
|
| 308 |
.gradio-container textarea,
|
| 309 |
+
.gradio-container select,
|
| 310 |
+
.gradio-container [role="combobox"],
|
| 311 |
+
.gradio-container [role="listbox"],
|
| 312 |
+
.gradio-container [role="option"],
|
| 313 |
+
.gradio-container .choices,
|
| 314 |
+
.gradio-container .choices__inner,
|
| 315 |
+
.gradio-container .choices__list,
|
| 316 |
+
.gradio-container .choices__item {
|
| 317 |
background: #fffdf8 !important;
|
| 318 |
border: 1px solid #bfc8bf !important;
|
| 319 |
border-radius: 14px !important;
|
| 320 |
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7) !important;
|
| 321 |
+
color: var(--ink) !important;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.gradio-container [role="listbox"],
|
| 325 |
+
.gradio-container .choices__list,
|
| 326 |
+
.gradio-container .choices__item {
|
| 327 |
+
background: #fffdf8 !important;
|
| 328 |
+
color: var(--ink) !important;
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
.gradio-container [role="option"][aria-selected="true"],
|
| 332 |
+
.gradio-container .choices__item--selectable.is-highlighted {
|
| 333 |
+
background: #e7f0ed !important;
|
| 334 |
+
color: #0d3d38 !important;
|
| 335 |
}
|
| 336 |
|
| 337 |
.gradio-container input:focus,
|
|
|
|
| 352 |
}
|
| 353 |
|
| 354 |
.gradio-container button.primary {
|
| 355 |
+
background: linear-gradient(180deg, var(--accent-bright), var(--accent-bright-deep)) !important;
|
| 356 |
color: #f7fbfa !important;
|
| 357 |
border: none !important;
|
| 358 |
+
box-shadow: 0 14px 26px rgba(184,100,61,0.24) !important;
|
| 359 |
}
|
| 360 |
|
| 361 |
.gradio-container button.secondary {
|
| 362 |
+
background: linear-gradient(180deg, #f6e7da, #f0ddcd) !important;
|
| 363 |
color: var(--ink) !important;
|
| 364 |
border: 1px solid #dbc4b4 !important;
|
| 365 |
}
|
|
|
|
| 474 |
},
|
| 475 |
}
|
| 476 |
|
| 477 |
+
DEFAULT_EXAMPLE_NAME = "JAK2 cell assay"
|
| 478 |
+
DEFAULT_EXAMPLE = EXAMPLES[DEFAULT_EXAMPLE_NAME]
|
| 479 |
+
|
| 480 |
|
| 481 |
def _parse_smiles_text(value: str | None) -> list[str]:
|
| 482 |
if not value:
|
|
|
|
| 734 |
<div id="hero">
|
| 735 |
<div class="hero-grid">
|
| 736 |
<div>
|
| 737 |
+
<div class="eyebrow">BioAssayAlign · assay-conditioned compound ranking</div>
|
| 738 |
+
<div class="hero-title">Rank a compound list against one assay definition</div>
|
| 739 |
<div class="hero-copy">
|
| 740 |
+
Define an assay in structured scientific terms, submit a candidate molecule set, and obtain a within-list ranking from the current BioAssayAlign compatibility model.
|
| 741 |
+
The output is intended for shortlist prioritization, retrospective analysis, and assay-conditioned compound triage.
|
| 742 |
</div>
|
| 743 |
</div>
|
| 744 |
<div class="hero-side">
|
| 745 |
+
<div class="hero-side-title">Operational scope</div>
|
| 746 |
<ul class="hero-list">
|
| 747 |
+
<li>Single-assay compound ranking with explicit assay metadata</li>
|
| 748 |
+
<li>Relative prioritization within the submitted molecule set</li>
|
| 749 |
+
<li>Immediate rejection of malformed or non-parseable SMILES</li>
|
| 750 |
</ul>
|
| 751 |
</div>
|
| 752 |
</div>
|
|
|
|
| 755 |
)
|
| 756 |
|
| 757 |
with gr.Row():
|
| 758 |
+
with gr.Column(scale=9):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 759 |
gr.HTML(
|
| 760 |
f"""
|
| 761 |
<div class="metric-strip">
|
| 762 |
+
<div class="metric-card"><span>Model</span><strong>Qwen3-Embedding-0.6B compatibility scorer</strong></div>
|
| 763 |
+
<div class="metric-card"><span>Input contract</span><strong>1 assay definition + up to {MAX_INPUT_SMILES} candidate SMILES</strong></div>
|
| 764 |
+
<div class="metric-card"><span>Score semantics</span><strong>Use rank and relative score within the submitted list</strong></div>
|
| 765 |
</div>
|
| 766 |
"""
|
| 767 |
)
|
|
|
|
| 769 |
gr.HTML(
|
| 770 |
"""
|
| 771 |
<div class="guide-grid">
|
| 772 |
+
<div class="guide-card"><strong>1. Specify the assay</strong>Include target, readout, organism, and format when known. Missing fields are allowed but reduce context.</div>
|
| 773 |
+
<div class="guide-card"><strong>2. Provide a candidate set</strong>Paste one SMILES per line or upload a CSV with a <code>smiles</code> column.</div>
|
| 774 |
+
<div class="guide-card"><strong>3. Read the output scientifically</strong>Use rank and relative score for shortlist decisions. Raw model score is an internal compatibility logit.</div>
|
| 775 |
</div>
|
| 776 |
"""
|
| 777 |
)
|
|
|
|
| 784 |
<div class="pane-header">
|
| 785 |
<div>
|
| 786 |
<div class="pane-kicker">Assay definition</div>
|
| 787 |
+
<div class="pane-title">Define the assay context</div>
|
| 788 |
</div>
|
| 789 |
</div>
|
| 790 |
+
<div class="pane-copy">The model performs best when the assay description is concrete and metadata fields are explicit. This form is preloaded with a live example.</div>
|
| 791 |
<div class="helper-row">
|
| 792 |
+
<div class="helper-chip"><strong>Describe the protocol signal</strong>State the readout, assay system, target biology, and measurement context.</div>
|
| 793 |
+
<div class="helper-chip"><strong>Use target identifiers</strong>UniProt IDs often improve separation between plausible and implausible candidates.</div>
|
| 794 |
+
<div class="helper-chip"><strong>Interpret scores locally</strong>Ranking is meaningful within the submitted library, not across unrelated runs.</div>
|
| 795 |
</div>
|
| 796 |
"""
|
| 797 |
)
|
| 798 |
+
example_name = gr.Dropdown(choices=list(EXAMPLES.keys()), value=DEFAULT_EXAMPLE_NAME, label="Example assay")
|
| 799 |
+
gr.HTML("<div class='examples-note'>The form is initialized with a live JAK2 example. Selecting another example will overwrite the current fields.</div>")
|
| 800 |
+
load_example_btn = gr.Button("Replace with selected example", variant="secondary")
|
| 801 |
+
assay_title = gr.Textbox(label="Assay title", value=DEFAULT_EXAMPLE["title"])
|
| 802 |
+
description = gr.Textbox(label="Description", value=DEFAULT_EXAMPLE["description"], lines=6, placeholder="Describe the assay in practical lab language.")
|
| 803 |
with gr.Row():
|
| 804 |
+
organism = gr.Textbox(label="Organism", value=DEFAULT_EXAMPLE["organism"], placeholder="Homo sapiens")
|
| 805 |
+
readout = gr.Textbox(label="Readout", value=DEFAULT_EXAMPLE["readout"], placeholder="binding / fluorescence / luminescence")
|
| 806 |
with gr.Row():
|
| 807 |
+
assay_format = gr.Textbox(label="Assay format", value=DEFAULT_EXAMPLE["assay_format"], placeholder="biochemical / cell-based")
|
| 808 |
+
assay_type = gr.Textbox(label="Assay type", value=DEFAULT_EXAMPLE["assay_type"], placeholder="binding / inhibition / activation")
|
| 809 |
+
target_uniprot = gr.Textbox(label="Target UniProt IDs", value=DEFAULT_EXAMPLE["target_uniprot"], placeholder="Q06187, P52333")
|
| 810 |
|
| 811 |
with gr.Column(scale=5, elem_classes="pane"):
|
| 812 |
gr.HTML(
|
|
|
|
| 814 |
<div class="pane-header">
|
| 815 |
<div>
|
| 816 |
<div class="pane-kicker">Candidate set</div>
|
| 817 |
+
<div class="pane-title">Submit the candidate molecules</div>
|
| 818 |
</div>
|
| 819 |
</div>
|
| 820 |
+
<div class="pane-copy">The model ranks compounds relative to this exact submitted set. Use parent or cleaned molecules when possible.</div>
|
| 821 |
"""
|
| 822 |
)
|
| 823 |
smiles_text = gr.Textbox(
|
| 824 |
label="Candidate SMILES",
|
| 825 |
+
value=DEFAULT_EXAMPLE["smiles"],
|
| 826 |
lines=14,
|
| 827 |
placeholder="Paste one candidate molecule per line. Example: CCO",
|
| 828 |
)
|
| 829 |
upload_file = gr.File(label="Upload CSV / TXT / SMI", file_count="single", file_types=[".csv", ".txt", ".smi", ".smiles"])
|
| 830 |
top_k = gr.Slider(label="Top-K rows to display", minimum=5, maximum=200, step=5, value=DEFAULT_TOP_K)
|
| 831 |
+
gr.HTML("<div class='section-note'>Use one SMILES per row. Invalid or non-standardizable structures are flagged separately and excluded from ranking.</div>")
|
| 832 |
with gr.Row(elem_classes="action-row"):
|
| 833 |
+
run_btn = gr.Button("Run assay-conditioned ranking", variant="primary")
|
| 834 |
clear_btn = gr.ClearButton(value="Clear inputs", components=[assay_title, description, organism, readout, assay_format, assay_type, target_uniprot, smiles_text, upload_file])
|
| 835 |
|
| 836 |
gr.HTML(
|
|
|
|
| 839 |
<div class="pane-header">
|
| 840 |
<div>
|
| 841 |
<div class="pane-kicker">Output</div>
|
| 842 |
+
<div class="pane-title">Interpret the ranked shortlist</div>
|
| 843 |
</div>
|
| 844 |
</div>
|
| 845 |
<div class="explain-grid">
|
| 846 |
+
<div class="explain-card"><strong>Priority band</strong>Use this as the first-pass triage signal when scanning the ranked list.</div>
|
| 847 |
+
<div class="explain-card"><strong>Relative score</strong>0–100 scale computed within the submitted list only. It is not calibrated across separate runs.</div>
|
| 848 |
+
<div class="explain-card"><strong>Model score</strong>Internal compatibility logit from the scorer head. Useful for export or debugging, not for direct biological interpretation.</div>
|
| 849 |
</div>
|
| 850 |
</div>
|
| 851 |
"""
|
|
|
|
| 875 |
<div class="pane-header">
|
| 876 |
<div>
|
| 877 |
<div class="pane-kicker">Practical guidance</div>
|
| 878 |
+
<div class="pane-title">Operating guidance</div>
|
| 879 |
</div>
|
| 880 |
</div>
|
| 881 |
<div class="explain-grid">
|
| 882 |
+
<div class="explain-card"><strong>Encode the assay explicitly</strong>Use full scientific language rather than keywords alone. Include target and readout when available.</div>
|
| 883 |
+
<div class="explain-card"><strong>Provide chemically reasonable candidates</strong>Prefer parent or neutralized structures. Upload a CSV with a <code>smiles</code> column for larger lists.</div>
|
| 884 |
+
<div class="explain-card"><strong>Use the output as a ranking signal</strong>Priority and relative score are the intended decision aids. Model score is not a calibrated success probability.</div>
|
| 885 |
</div>
|
| 886 |
<div class="footer-note" style="margin-top: 1rem;">
|
| 887 |
+
This Space is intended for assay-conditioned shortlist ranking. It is not a generative chemistry system and it does not replace confirmatory screening.
|
| 888 |
+
Export the ranked CSV if you want to continue downstream analysis in your own workflow.
|
| 889 |
</div>
|
| 890 |
</div>
|
| 891 |
"""
|