File size: 9,492 Bytes
6e9b5dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
"""
File: CodonJupyter.py
---------------------
Includes Jupyter-specific functions for displaying interactive widgets.
"""

from typing import Dict, List, Tuple

import ipywidgets as widgets
from IPython.display import HTML, display

from CodonTransformer.CodonUtils import (
    COMMON_ORGANISMS,
    ID2ORGANISM,
    ORGANISM2ID,
    DNASequencePrediction,
)


class UserContainer:
    """
    A container class to store user inputs for organism and protein sequence.
    Attributes:
        organism (int): The selected organism id.
        protein (str): The input protein sequence.
    """

    def __init__(self) -> None:
        self.organism: int = -1
        self.protein: str = ""


def create_styled_options(
    organisms: list, organism2id: Dict[str, int], is_fine_tuned: bool = False
) -> list:
    """
    Create styled options for the dropdown widget.

    Args:
        organisms (list): List of organism names.
        organism2id (Dict[str, int]): Dictionary mapping organism names to their IDs.
        is_fine_tuned (bool): Whether these are fine-tuned organisms.

    Returns:
        list: Styled options for the dropdown widget.
    """
    styled_options = []
    for organism in organisms:
        organism_id = organism2id[organism]
        if is_fine_tuned:
            if organism_id < 10:
                styled_options.append(f"\u200b{organism_id:>6}.  {organism}")
            elif organism_id < 100:
                styled_options.append(f"\u200b{organism_id:>5}.  {organism}")
            else:
                styled_options.append(f"\u200b{organism_id:>4}.  {organism}")
        else:
            if organism_id < 10:
                styled_options.append(f"{organism_id:>6}.  {organism}")
            elif organism_id < 100:
                styled_options.append(f"{organism_id:>5}.  {organism}")
            else:
                styled_options.append(f"{organism_id:>4}.  {organism}")
    return styled_options


def create_dropdown_options(organism2id: Dict[str, int]) -> list:
    """
    Create the full list of dropdown options, including section headers.

    Args:
        organism2id (Dict[str, int]): Dictionary mapping organism names to their IDs.

    Returns:
        list: Full list of dropdown options.
    """
    fine_tuned_organisms = sorted(
        [org for org in organism2id.keys() if org in COMMON_ORGANISMS]
    )
    all_organisms = sorted(organism2id.keys())

    fine_tuned_options = create_styled_options(
        fine_tuned_organisms, organism2id, is_fine_tuned=True
    )
    all_organisms_options = create_styled_options(
        all_organisms, organism2id, is_fine_tuned=False
    )

    return (
        [""]
        + ["Selected Organisms"]
        + fine_tuned_options
        + [""]
        + ["All Organisms"]
        + all_organisms_options
    )


def create_organism_dropdown(container: UserContainer) -> widgets.Dropdown:
    """
    Create and configure the organism dropdown widget.

    Args:
        container (UserContainer): Container to store the selected organism.

    Returns:
        widgets.Dropdown: Configured dropdown widget.
    """
    dropdown = widgets.Dropdown(
        options=create_dropdown_options(ORGANISM2ID),
        description="",
        layout=widgets.Layout(width="40%", margin="0 0 10px 0"),
        style={"description_width": "initial"},
    )

    def show_organism(change: Dict[str, str]) -> None:
        """
        Update the container with the selected organism and print to terminal.

        Args:
            change (Dict[str, str]): Information about the change in dropdown value.
        """
        dropdown_choice = change["new"]
        if dropdown_choice and dropdown_choice not in [
            "Selected Organisms",
            "All Organisms",
        ]:
            organism = "".join(filter(str.isdigit, dropdown_choice))
            organism_id = ID2ORGANISM[int(organism)]
            container.organism = organism_id
        else:
            container.organism = None

    dropdown.observe(show_organism, names="value")
    return dropdown


def get_dropdown_style() -> str:
    """
    Return the custom CSS style for the dropdown widget.

    Returns:
        str: CSS style string.
    """
    return """
    <style>
        .widget-dropdown > select {
            font-size: 16px;
            font-weight: normal;
            background-color: #f0f0f0;
            border-radius: 5px;
            padding: 5px;
        }
        .widget-label {
            font-size: 18px;
            font-weight: bold;
        }
        .custom-container {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
        }
        .widget-dropdown option[value^="\u200b"] {
            font-family: sans-serif;
            font-weight: bold;
            font-size: 18px;
            padding: 510px;
        }
        .widget-dropdown option[value*="Selected Organisms"],
        .widget-dropdown option[value*="All Organisms"] {
            text-align: center;
            font-family: Arial, sans-serif;
            font-weight: bold;
            font-size: 20px;
            color: #6900A1;
            background-color: #00D8A1;
        }
    </style>
    """


def display_organism_dropdown(container: UserContainer) -> None:
    """
    Display the organism dropdown widget and apply custom styles.

    Args:
        container (UserContainer): Container to store the selected organism.
    """
    dropdown = create_organism_dropdown(container)
    header = widgets.HTML(
        '<b style="font-size:20px;">Select Organism:</b>'
        '<div style="height:10px;"></div>'
    )
    container_widget = widgets.VBox(
        [header, dropdown],
        layout=widgets.Layout(padding="12px 0 12px 25px"),
    )
    display(container_widget)
    display(HTML(get_dropdown_style()))


def display_protein_input(container: UserContainer) -> None:
    """
    Display a widget for entering a protein sequence and save it to the container.

    Args:
        container (UserContainer): A container to store the entered protein sequence.
    """
    protein_input = widgets.Textarea(
        value="",
        placeholder="Enter here...",
        description="",
        layout=widgets.Layout(width="100%", height="100px", margin="0 0 10px 0"),
        style={"description_width": "initial"},
    )

    # Custom CSS for the input widget
    input_style = """
        <style>
            .widget-textarea > textarea {
                font-size: 12px;
                font-family: Arial, sans-serif;
                font-weight: normal;
                background-color: #f0f0f0;
                border-radius: 5px;
                padding: 10px;
            }
            .widget-label {
                font-size: 18px;
                font-weight: bold;
            }
            .custom-container {
                display: flex;
                flex-direction: column;
                align-items: flex-start;
            }
        </style>
    """

    # Function to save the input protein sequence to the container
    def save_protein(change: Dict[str, str]) -> None:
        """
        Save the input protein sequence to the container.

        Args:
            change (Dict[str, str]): A dictionary containing information about
            the change in textarea value.
        """
        container.protein = (
            change["new"]
            .upper()
            .strip()
            .replace("\n", "")
            .replace(" ", "")
            .replace("\t", "")
        )

    # Attach the function to the input widget
    protein_input.observe(save_protein, names="value")

    # Display the input widget
    header = widgets.HTML(
        '<b style="font-size:20px;">Enter Protein Sequence:</b>'
        '<div style="height:18px;"></div>'
    )
    container_widget = widgets.VBox(
        [header, protein_input], layout=widgets.Layout(padding="12px 12px 0 25px")
    )

    display(container_widget)
    display(widgets.HTML(input_style))


def format_model_output(output: DNASequencePrediction) -> str:
    """
    Format DNA sequence prediction output in an appealing and easy-to-read manner.

    This function takes the prediction output and formats it into
    a structured string with clear section headers and separators.

    Args:
        output (DNASequencePrediction): Object containing the prediction output.
            Expected attributes:
            - organism (str): The organism name.
            - protein (str): The input protein sequence.
            - processed_input (str): The processed input sequence.
            - predicted_dna (str): The predicted DNA sequence.

    Returns:
        str: A formatted string containing the organized output.
    """

    def format_section(title: str, content: str) -> str:
        """Helper function to format individual sections."""
        separator = "-" * 29
        title_line = f"| {title.center(25)} |"
        return f"{separator}\n{title_line}\n{separator}\n{content}\n\n"

    sections: List[Tuple[str, str]] = [
        ("Organism", output.organism),
        ("Input Protein", output.protein),
        ("Processed Input", output.processed_input),
        ("Predicted DNA", output.predicted_dna),
    ]

    formatted_output = ""
    for title, content in sections:
        formatted_output += format_section(title, content)

    # Remove the last newline to avoid extra space at the end
    return formatted_output.rstrip()