yashm commited on
Commit
d9e9ca6
·
verified ·
1 Parent(s): 4e34c92

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -112
app.py CHANGED
@@ -7,36 +7,25 @@ from io import StringIO, BytesIO
7
 
8
  st.set_page_config(page_title="PCR Primer Designer", layout="wide")
9
 
10
- st.sidebar.title("Instructions")
11
- st.sidebar.info("""
12
- 1. Upload a GenBank (.gb/.gbk) file.
13
- 2. Choose a CDS from the dropdown (length shown).
14
- 3. Set desired PCR product size range.
15
- 4. Design primers and visualize binding.
16
- 5. Optionally filter similar-sized products.
17
- """)
18
-
19
- uploaded_file = st.file_uploader("Upload GenBank file", type=["gb", "gbk"])
20
-
21
  plot_export_format = st.selectbox("Export Plot Format", ["PNG", "PDF", "SVG"])
22
 
23
- def extract_features(record):
24
- features = []
25
- for f in record.features:
26
- if f.type == "CDS":
27
- gene = f.qualifiers.get("gene", ["unnamed"])[0]
28
- loc = f.location
29
- length = len(f.location)
30
- features.append((gene, loc, length, f))
31
- return features
 
32
 
33
  def design_primers(seq, size_range, count):
34
  min_size, max_size = size_range
35
  return primer3.bindings.designPrimers(
36
- {
37
- 'SEQUENCE_TEMPLATE': str(seq),
38
- 'PRIMER_PRODUCT_SIZE_RANGE': [[min_size, max_size]]
39
- },
40
  {
41
  'PRIMER_OPT_SIZE': 20,
42
  'PRIMER_MIN_SIZE': 18,
@@ -46,7 +35,7 @@ def design_primers(seq, size_range, count):
46
  'PRIMER_MAX_TM': 63.0,
47
  'PRIMER_MIN_GC': 20.0,
48
  'PRIMER_MAX_GC': 80.0,
49
- 'PRIMER_NUM_RETURN': count * 2
50
  }
51
  )
52
 
@@ -65,100 +54,57 @@ def plot_binding(seq_len, l_start, l_len, r_start, r_len, product_len):
65
  ax.set_title("Primer Binding Sites and PCR Product", fontsize=14)
66
  return fig
67
 
68
- def is_unique(primer_set, seen_seqs):
69
- l_seq = primer_set.get('left')
70
- r_seq = primer_set.get('right')
71
- if (l_seq, r_seq) in seen_seqs:
72
- return False
73
- seen_seqs.add((l_seq, r_seq))
74
- return True
75
-
76
- def filter_similar_by_product(primers, threshold=5):
77
- seen_sizes = set()
78
- filtered = []
79
- for row in primers:
80
- size = row['PCR Product Size']
81
- if any(abs(size - s) <= threshold for s in seen_sizes):
82
- continue
83
- seen_sizes.add(size)
84
- filtered.append(row)
85
- return filtered
86
-
87
  if uploaded_file:
88
  record = SeqIO.read(StringIO(uploaded_file.getvalue().decode("utf-8")), "genbank")
89
- cds_features = extract_features(record)
90
 
91
- if cds_features:
92
  st.subheader("Select CDS Feature")
93
- cds_labels = [f"{g} ({loc.start}:{loc.end}) [length: {length} bp]" for g, loc, length, _ in cds_features]
94
- selected_idx = st.selectbox("Choose CDS:", range(len(cds_labels)), format_func=lambda x: cds_labels[x])
95
- selected_feature = cds_features[selected_idx]
96
- _, _, cds_length, feature = selected_feature
97
- feature_seq = feature.extract(record.seq)
98
 
99
- st.code(str(feature_seq), language="text")
100
 
101
  st.subheader("Primer Design Parameters")
102
- min_range = 100
103
- max_range = cds_length
104
- product_range = st.slider("PCR Product Size Range (bp)", min_value=min_range, max_value=max_range,
105
- value=(min_range, max_range))
106
-
107
- num_primers = st.number_input("Number of primer pairs to return:", min_value=1, value=5, step=1)
108
- filter_checkbox = st.checkbox("Filter similar product sizes (±5 bp)", value=False)
109
 
110
  if st.button("Design Primers"):
111
  with st.spinner("Designing primers..."):
112
- result = design_primers(feature_seq, product_range, num_primers)
113
- seen_seqs = set()
114
- primer_rows = []
115
-
116
- for i in range(result.get('PRIMER_PAIR_NUM_RETURN', 0)):
117
- l_seq = result.get(f'PRIMER_LEFT_{i}_SEQUENCE')
118
- r_seq = result.get(f'PRIMER_RIGHT_{i}_SEQUENCE')
119
- l_pos, l_len = result.get(f'PRIMER_LEFT_{i}', [None, None])
120
- r_pos, r_len = result.get(f'PRIMER_RIGHT_{i}', [None, None])
121
- prod_size = result.get(f'PRIMER_PAIR_{i}_PRODUCT_SIZE')
122
- if None in (l_seq, r_seq, l_pos, l_len, r_pos, r_len, prod_size):
123
- continue
124
- if is_unique({'left': l_seq, 'right': r_seq}, seen_seqs):
125
- primer_rows.append({
126
- "Primer Pair": i + 1,
127
- "Left Primer": l_seq,
128
- "Right Primer": r_seq,
129
- "PCR Product Size": prod_size,
130
- "Left TM (°C)": round(result.get(f'PRIMER_LEFT_{i}_TM', 0), 2),
131
- "Right TM (°C)": round(result.get(f'PRIMER_RIGHT_{i}_TM', 0), 2),
132
- "Left Pos": l_pos,
133
- "Left Len": l_len,
134
- "Right Pos": r_pos,
135
- "Right Len": r_len,
136
- })
137
-
138
- if filter_checkbox:
139
- primer_rows = filter_similar_by_product(primer_rows)
140
-
141
- if primer_rows:
142
- st.subheader("Designed Primers")
143
- df = pd.DataFrame(primer_rows).drop(columns=["Left Pos", "Left Len", "Right Pos", "Right Len"])
144
- st.dataframe(df)
145
-
146
- csv = df.to_csv(index=False).encode('utf-8')
147
- st.download_button("Download Primer Table as CSV", csv, "primers.csv", "text/csv")
148
-
149
- # Plot first pair
150
- first = primer_rows[0]
151
- fig = plot_binding(len(feature_seq), first["Left Pos"], first["Left Len"],
152
- first["Right Pos"], first["Right Len"], first["PCR Product Size"])
153
- st.pyplot(fig)
154
-
155
- # Export image
156
- buf = BytesIO()
157
- fmt = plot_export_format.lower()
158
- fig.savefig(buf, format=fmt, dpi=300)
159
- st.download_button(f"Download Binding Plot ({plot_export_format})",
160
- data=buf.getvalue(),
161
- file_name=f"primer_plot.{fmt}",
162
- mime=f"image/{'svg+xml' if fmt == 'svg' else fmt}")
163
- else:
164
- st.warning("No unique primers found.")
 
7
 
8
  st.set_page_config(page_title="PCR Primer Designer", layout="wide")
9
 
10
+ uploaded_file = st.file_uploader("Upload GenBank File", type=["gb", "gbk"])
 
 
 
 
 
 
 
 
 
 
11
  plot_export_format = st.selectbox("Export Plot Format", ["PNG", "PDF", "SVG"])
12
 
13
+ def extract_cds_features(record):
14
+ return [
15
+ (
16
+ f.qualifiers.get("gene", ["unnamed"])[0],
17
+ f.location,
18
+ len(f.location),
19
+ f
20
+ )
21
+ for f in record.features if f.type == "CDS"
22
+ ]
23
 
24
  def design_primers(seq, size_range, count):
25
  min_size, max_size = size_range
26
  return primer3.bindings.designPrimers(
27
+ {'SEQUENCE_TEMPLATE': str(seq),
28
+ 'PRIMER_PRODUCT_SIZE_RANGE': [[min_size, max_size]]},
 
 
29
  {
30
  'PRIMER_OPT_SIZE': 20,
31
  'PRIMER_MIN_SIZE': 18,
 
35
  'PRIMER_MAX_TM': 63.0,
36
  'PRIMER_MIN_GC': 20.0,
37
  'PRIMER_MAX_GC': 80.0,
38
+ 'PRIMER_NUM_RETURN': count
39
  }
40
  )
41
 
 
54
  ax.set_title("Primer Binding Sites and PCR Product", fontsize=14)
55
  return fig
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  if uploaded_file:
58
  record = SeqIO.read(StringIO(uploaded_file.getvalue().decode("utf-8")), "genbank")
59
+ cds_list = extract_cds_features(record)
60
 
61
+ if cds_list:
62
  st.subheader("Select CDS Feature")
63
+ cds_labels = [f"{g} ({loc.start}:{loc.end}) [length: {length} bp]" for g, loc, length, _ in cds_list]
64
+ selected_idx = st.selectbox("Choose CDS:", range(len(cds_labels)), format_func=lambda i: cds_labels[i])
65
+ _, _, cds_length, selected_feature = cds_list[selected_idx]
66
+ seq = selected_feature.extract(record.seq)
 
67
 
68
+ st.code(str(seq), language="text")
69
 
70
  st.subheader("Primer Design Parameters")
71
+ min_size = 100
72
+ product_range = st.slider("PCR Product Size Range", min_value=min_size, max_value=cds_length,
73
+ value=(min_size, cds_length))
74
+ primer_count = st.number_input("Number of Primer Pairs to Return", min_value=1, value=5, step=1)
 
 
 
75
 
76
  if st.button("Design Primers"):
77
  with st.spinner("Designing primers..."):
78
+ result = design_primers(seq, product_range, primer_count)
79
+
80
+ data = []
81
+ for i in range(result['PRIMER_PAIR_NUM_RETURN']):
82
+ data.append({
83
+ "Primer Pair": i + 1,
84
+ "Left Primer": result.get(f'PRIMER_LEFT_{i}_SEQUENCE'),
85
+ "Right Primer": result.get(f'PRIMER_RIGHT_{i}_SEQUENCE'),
86
+ "PCR Product Size": result.get(f'PRIMER_PAIR_{i}_PRODUCT_SIZE'),
87
+ "Left TM (°C)": round(result.get(f'PRIMER_LEFT_{i}_TM', 0), 2),
88
+ "Right TM (°C)": round(result.get(f'PRIMER_RIGHT_{i}_TM', 0), 2)
89
+ })
90
+
91
+ st.subheader("Designed Primers")
92
+ df = pd.DataFrame(data)
93
+ st.dataframe(df)
94
+
95
+ csv = df.to_csv(index=False).encode('utf-8')
96
+ st.download_button("Download Primer Table", csv, "primers.csv", "text/csv")
97
+
98
+ # Visualize first primer pair
99
+ left = result['PRIMER_LEFT_0']
100
+ right = result['PRIMER_RIGHT_0']
101
+ size = result['PRIMER_PAIR_0_PRODUCT_SIZE']
102
+ fig = plot_binding(len(seq), left[0], left[1], right[0], right[1], size)
103
+ st.pyplot(fig)
104
+
105
+ # Export plot
106
+ buf = BytesIO()
107
+ ext = plot_export_format.lower()
108
+ fig.savefig(buf, format=ext, dpi=300)
109
+ st.download_button(f"Download Plot ({plot_export_format})", buf.getvalue(),
110
+ f"primer_plot.{ext}", mime=f"image/{'svg+xml' if ext == 'svg' else ext}")