farshidk/codon-optimizer
1
1import os2import gradio as gr3import pandas as pd4# from optimizer import optimization5from c_wobble import do_c_wobble, change_codon, aminoacid_percentage, gc_content, c_content6 7BASE_DIR = os.path.dirname(__file__)8SUMMARY_PATH = os.path.join(BASE_DIR, "region_sweep_summary.csv")9 10AA_ORDER = list("ACDEFGHIKLMNPQRSTVWY*")11AA_ALLOWED = set(AA_ORDER)12 13RESTRICTION_SITES = {14 "EcoRI": "GAATTC",15 "BamHI": "GGATCC",16 "HindIII": "AAGCTT",17 "XhoI": "CTCGAG",18 "XbaI": "TCTAGA",19 "SpeI": "ACTAGT",20 "NheI": "GCTAGC",21 "XmaI": "CCCGGG",22 "NotI": "GCGGCCGC",23 "PstI": "CTGCAG",24 "KpnI": "GGTACC",25 "SalI": "GTCGAC",26}27 28def _clean_aa_seq(raw: str) -> str:29 s = (raw or "").upper()30 return "".join(ch for ch in s if ch in AA_ALLOWED)31 32def aa_percent_to_onecol_df(aa_percent: dict, digits: int = 0) -> pd.DataFrame:33 rows = []34 order = AA_ORDER if set(aa_percent).issubset(set(AA_ORDER)) else sorted(aa_percent)35 for aa in order:36 mix = aa_percent.get(aa, {})37 if not mix:38 rows.append([aa, "—"])39 continue40 parts = sorted(mix.items(), key=lambda kv: (-kv[1], kv[0]))41 cell = " - ".join(f"{cod} ({val*100:.{digits}f}%)" for cod, val in parts)42 rows.append([aa, cell])43 return pd.DataFrame(rows, columns=["AA", "Codon percentage"])44 45def run(aa_seq: str, selected_enzymes):46 cleaned = _clean_aa_seq(aa_seq)47 48 if not cleaned:49 raise gr.Error("Input contains no valid amino-acid characters.")50 51 # Step 1: initial sequence52 designed_nt, aa_percent, gc_percent, c_percent, _ = do_c_wobble(53 summary_path=None,54 aa_seq=cleaned,55 )56 57 changed_positions = set()58 bold_intervals = []59 60 # Step 2: modify selected restriction sites61 if selected_enzymes:62 for enzyme in selected_enzymes:63 site = RESTRICTION_SITES[enzyme]64 designed_nt, changed_pos, site_intervals = change_codon(designed_nt, site)65 changed_positions.update(changed_pos)66 bold_intervals.extend(site_intervals)67 68 # Step 3: build HTML69 html = []70 71 for i, nt in enumerate(designed_nt):72 73 char_html = nt74 75 # red for modified nucleotides76 if i in changed_positions:77 char_html = f"<span style='color:red;'>{nt}</span>"78 79 # bold for restriction-site region80 in_bold = any(start <= i < end for start, end in bold_intervals)81 if in_bold:82 char_html = f"<b>{char_html}</b>"83 84 html.append(char_html)85 86 # add space every codon87 if (i + 1) % 3 == 0:88 html.append(" ")89 90 final_html = (91 "<div style='"92 "font-family:monospace;"93 "white-space:pre-wrap;"94 "word-break:break-word;"95 "font-size:14px;"96 "line-height:1.4;"97 "border:1px solid #ccc;"98 "border-radius:6px;"99 "padding:10px;"100 "max-height:250px;"101 "overflow:auto;"102 "background-color:#fafafa;"103 "'>"104 + "".join(html) +105 "</div>"106 )107 108 # Step 4: recalculate tables109 codon_list = [designed_nt[i:i+3] for i in range(0, len(designed_nt), 3)]110 aa_percent_dict, _counts = aminoacid_percentage(codon_list)111 aa_table = aa_percent_to_onecol_df(aa_percent_dict, digits=0)112 gc_percent = gc_content([designed_nt])113 c_percent = c_content([designed_nt])114 115 return final_html, aa_table, gc_percent, c_percent116 117 118# ---- Gradio Interface ----119iface = gr.Interface(120 fn=run,121 inputs=[122 gr.Textbox(123 label="Amino Acid Sequence",124 lines=5,125 placeholder="e.g. MKKLLPTAA..."126 ),127 gr.CheckboxGroup(128 choices=list(RESTRICTION_SITES.keys()),129 label="Remove Restriction Sites",130 info=(131 "Selected restriction enzyme recognition sites will be removed from the optimized nucleotide sequence. "132 "The algorithm starts modifying codons from the most left codon within the detected site, "133 "using wobble-priority substitutions (C > G > A > T) while preserving the amino acid sequence. "134 "In the output sequence, the region corresponding to the detected restriction site is shown in bold black, "135 "and any nucleotide modified to break the site is highlighted in red."136 )137 )138 ],139 140 outputs=[141 gr.HTML(label="Nucleotide Sequence"),142 gr.Dataframe(label="Codon Usage Percent (per AA)", wrap=True),143 gr.Dataframe(label="GC Content (%)", wrap=True),144 gr.Dataframe(label="C Content (%)", wrap=True), # <-- NEW BOX145 ],146 title="Codon Optimizer",147 description=(148 # "**Optimized Codons** → sequence is optimized based on the best motifs in different regions, "149 # "balancing codon usage, wobble, and motif constraints.\n\n"150 "**C wobbling** → amino acids are encoded using codons that prefer C at the wobble position "151 "(C > G > A/T). For serine (S) the priority is TCC, for proline (P) the priority is CCC. "152 "If more than four Cs occur in a row, codons with G at the wobble position are chosen to break the run."153 ),154 flagging_mode="never",155)156 157if __name__ == "__main__":158 iface.launch()159 160 