Skip to content

Commit f140de7

Browse files
committed
Fix: unit test err due to code_text; Update: CHANGELOG
1 parent 0ab5b9b commit f140de7

3 files changed

Lines changed: 108 additions & 95 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@ and this project adheres to [Semantic Versioning][].
88
[keep a changelog]: https://keepachangelog.com/en/1.0.0/
99
[semantic versioning]: https://semver.org/spec/v2.0.0.html
1010

11-
## [Unreleased]
11+
## [0.2.5] 2025-05-19
1212

1313
### Added
1414

15-
- Basic tool, preprocessing and plotting functions
15+
- Adopted Cookiecutter-style structure based on the Scanpy project template.
16+
- Added a new flag to export summary metrics as a CSV file.
17+
- The HTML report now also includes a warning for low mapping rate.
18+
- Added unit tests and scripts to download test data
19+
- Updated the EmptyDrops step by removing the limitation on the number of candidate barcodes and making the FDR threshold dynamically adjustable based on the chemistry version.
20+
- Added source code snippets to the help text section of clustering plots
21+
22+
### Changed
23+
24+
- Switched to more concise progress logging during the cell-calling step.

src/qcatch/convert_to_html.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ def create_plotly_plots(
117117

118118
# ---------------- Tab2 - Barcode Frequency Plots ---------------
119119
fig_bc_freq_all_plots = barcode_frequency_plots(data, valid_bcs)
120-
# fig_bc_freq_UMI, fig_bc_freq_gene, fig_gene_UMI = barcode_frequency_plots(data)
121120

122121
# ---------------- Tab3 - Collapsing ---------------
123122
# NOTE: use the retained data for umi_collapse plot
@@ -144,12 +143,16 @@ def create_plotly_plots(
144143

145144
else:
146145
fig_umap = fig_tsne = None
146+
code_text = ""
147147
logger.info("🦦 Skipping UMAP and t-SNE plots as per user request.")
148148

149149
# Convert plots to HTML div strings
150150
plots = {
151151
# ----tab1----(
152-
"knee_plot1-1": fig_knee_1.to_html(full_html=False, include_plotlyjs="cdn"),
152+
"knee_plot1-1": fig_knee_1.to_html(
153+
full_html=False,
154+
include_plotlyjs="cdn",
155+
),
153156
"knee_plot1-2": fig_knee_2.to_html(full_html=False, include_plotlyjs="cdn"),
154157
# ----tab2----
155158
"bc_freq_all_plots": fig_bc_freq_all_plots.to_html(full_html=False, include_plotlyjs="cdn"),

src/qcatch/plots_tables.py

Lines changed: 92 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def apply_uniform_style(fig: go.Figure) -> go.Figure:
2222
margin={"t": 40, "l": 30, "r": 30, "b": 30},
2323
plot_bgcolor="rgba(0,0,0,0)", # Fully transparent background
2424
paper_bgcolor="rgba(0,0,0,0)",
25+
# plot_bgcolor="#fff", # Solid white background for plot area
26+
# paper_bgcolor="#fff", # Solid white background for entire figure
2527
font={"family": "Arial, sans-serif", "size": 14, "color": "#2c3e50"},
2628
title_font={"size": 16, "color": "#34495e"},
2729
xaxis={
@@ -110,6 +112,94 @@ def generate_knee_plots(data: pd.DataFrame, valid_bcs: list[str]) -> tuple[go.Fi
110112
return apply_uniform_style(fig_knee_1), apply_uniform_style(fig_knee_2)
111113

112114

115+
def barcode_frequency_plots(data: pd.DataFrame, valid_bcs: list[str]) -> go.Figure:
116+
"""
117+
Generate subplots showing barcode frequency vs UMI counts, barcode frequency vs detected genes, and UMI counts vs detected genes.
118+
119+
Args:
120+
data: DataFrame with barcode and cell metrics.
121+
valid_bcs: List of valid barcode strings.
122+
123+
Returns
124+
-------
125+
A Plotly Figure with three subplots.
126+
"""
127+
width = 1200
128+
height = 400
129+
opacity = 0.5
130+
# Map is_retained to human-readable cell_type
131+
data["cell_type"] = np.where(data["barcodes"].isin(valid_bcs), "Retained Cell", "Background")
132+
133+
# Create subplots: 1 row, 3 columns
134+
fig = make_subplots(
135+
rows=1,
136+
cols=3,
137+
subplot_titles=[
138+
"Barcode Frequency vs UMI Counts (All Cells)",
139+
"Barcode Frequency vs Detected Genes (All Cells)",
140+
"UMI Counts vs Detected Genes (All Cells)",
141+
],
142+
shared_yaxes=False,
143+
)
144+
145+
# Plot 1 (legend enabled)
146+
for trace in px.scatter(
147+
data,
148+
x="corrected_reads",
149+
y="deduplicated_reads",
150+
color="cell_type",
151+
color_discrete_map={"Retained Cell": "#636EFA", "Background": "lightgrey"},
152+
opacity=opacity,
153+
).data:
154+
fig.add_trace(trace, row=1, col=1)
155+
156+
# Plot 2 (legend disabled)
157+
for trace in px.scatter(
158+
data,
159+
x="corrected_reads",
160+
y="num_genes_expressed",
161+
color="cell_type",
162+
color_discrete_map={"Retained Cell": "#636EFA", "Background": "lightgrey"},
163+
opacity=opacity,
164+
).data:
165+
trace.showlegend = False # Disable legend for this trace
166+
fig.add_trace(trace, row=1, col=2)
167+
168+
# Plot 3 (legend disabled)
169+
for trace in px.scatter(
170+
data,
171+
x="deduplicated_reads",
172+
y="num_genes_expressed",
173+
color="cell_type",
174+
color_discrete_map={"Retained Cell": "#636EFA", "Background": "lightgrey"},
175+
opacity=opacity,
176+
).data:
177+
trace.showlegend = False # Disable legend for this trace
178+
fig.add_trace(trace, row=1, col=3)
179+
180+
fig.update_layout(
181+
width=width,
182+
height=height,
183+
legend={"orientation": "h", "yanchor": "bottom", "y": -0.3, "xanchor": "center", "x": 0.5, "title_text": ""},
184+
margin={"t": 60, "b": 80},
185+
title_x=0.5,
186+
)
187+
fig = apply_uniform_style(fig)
188+
# Explicitly reinforce grid lines for all subplots without overriding other style
189+
fig.update_xaxes(showgrid=True, gridcolor="lightgrey", linecolor="#34495e")
190+
fig.update_yaxes(showgrid=True, gridcolor="lightgrey", linecolor="#34495e")
191+
# Add axis labels for all three subplots
192+
fig.update_xaxes(title_text="Barcode frequency", row=1, col=1)
193+
fig.update_yaxes(title_text="UMI Counts", row=1, col=1)
194+
195+
fig.update_xaxes(title_text="Barcode frequency", row=1, col=2)
196+
fig.update_yaxes(title_text="Detected Genes", row=1, col=2)
197+
198+
fig.update_xaxes(title_text="UMI Counts", row=1, col=3)
199+
fig.update_yaxes(title_text="Detected Genes", row=1, col=3)
200+
return fig
201+
202+
113203
def generate_gene_histogram(data: pd.DataFrame, is_all_cells: bool) -> go.Figure:
114204
"""
115205
Generate a histogram plot of the number of detected genes per cell.
@@ -262,7 +352,7 @@ def generate_SUA_plots(adata: sc.AnnData, is_all_cells: bool) -> tuple[str, str]
262352

263353
# Customize the layout
264354
fig_S_ratio.update_layout(
265-
title="Histogram of Ratio" + title_suffix,
355+
title="Histogram of Splicing Ratio" + title_suffix,
266356
xaxis={"title": "(S+A)/(S+U+A) Ratio"},
267357
yaxis={"title": "Counts"},
268358
bargap=0.1, # Space between bars
@@ -351,94 +441,6 @@ def umi_dedup(data: pd.DataFrame) -> tuple[go.Figure, float]:
351441
return apply_uniform_style(fig_dedup), mean_dedup_rate
352442

353443

354-
def barcode_frequency_plots(data: pd.DataFrame, valid_bcs: list[str]) -> go.Figure:
355-
"""
356-
Generate subplots showing barcode frequency vs UMI counts, barcode frequency vs detected genes, and UMI counts vs detected genes.
357-
358-
Args:
359-
data: DataFrame with barcode and cell metrics.
360-
valid_bcs: List of valid barcode strings.
361-
362-
Returns
363-
-------
364-
A Plotly Figure with three subplots.
365-
"""
366-
width = 1200
367-
height = 400
368-
opacity = 0.5
369-
# Map is_retained to human-readable cell_type
370-
data["cell_type"] = np.where(data["barcodes"].isin(valid_bcs), "Retained Cell", "Background")
371-
372-
# Create subplots: 1 row, 3 columns
373-
fig = make_subplots(
374-
rows=1,
375-
cols=3,
376-
subplot_titles=[
377-
"Barcode Frequency vs UMI Counts (All Cells)",
378-
"Barcode Frequency vs Detected Genes (All Cells)",
379-
"UMI Counts vs Detected Genes (All Cells)",
380-
],
381-
shared_yaxes=False,
382-
)
383-
384-
# Plot 1 (legend enabled)
385-
for trace in px.scatter(
386-
data,
387-
x="corrected_reads",
388-
y="deduplicated_reads",
389-
color="cell_type",
390-
color_discrete_map={"Retained Cell": "#636EFA", "Background": "lightgrey"},
391-
opacity=opacity,
392-
).data:
393-
fig.add_trace(trace, row=1, col=1)
394-
395-
# Plot 2 (legend disabled)
396-
for trace in px.scatter(
397-
data,
398-
x="corrected_reads",
399-
y="num_genes_expressed",
400-
color="cell_type",
401-
color_discrete_map={"Retained Cell": "#636EFA", "Background": "lightgrey"},
402-
opacity=opacity,
403-
).data:
404-
trace.showlegend = False # Disable legend for this trace
405-
fig.add_trace(trace, row=1, col=2)
406-
407-
# Plot 3 (legend disabled)
408-
for trace in px.scatter(
409-
data,
410-
x="deduplicated_reads",
411-
y="num_genes_expressed",
412-
color="cell_type",
413-
color_discrete_map={"Retained Cell": "#636EFA", "Background": "lightgrey"},
414-
opacity=opacity,
415-
).data:
416-
trace.showlegend = False # Disable legend for this trace
417-
fig.add_trace(trace, row=1, col=3)
418-
419-
fig.update_layout(
420-
width=width,
421-
height=height,
422-
legend={"orientation": "h", "yanchor": "bottom", "y": -0.3, "xanchor": "center", "x": 0.5, "title_text": ""},
423-
margin={"t": 60, "b": 80},
424-
title_x=0.5,
425-
)
426-
fig = apply_uniform_style(fig)
427-
# Explicitly reinforce grid lines for all subplots without overriding other style
428-
fig.update_xaxes(showgrid=True, gridcolor="lightgrey", linecolor="#34495e")
429-
fig.update_yaxes(showgrid=True, gridcolor="lightgrey", linecolor="#34495e")
430-
# Add axis labels for all three subplots
431-
fig.update_xaxes(title_text="Barcode frequency", row=1, col=1)
432-
fig.update_yaxes(title_text="UMI Counts", row=1, col=1)
433-
434-
fig.update_xaxes(title_text="Barcode frequency", row=1, col=2)
435-
fig.update_yaxes(title_text="Detected Genes", row=1, col=2)
436-
437-
fig.update_xaxes(title_text="UMI Counts", row=1, col=3)
438-
fig.update_yaxes(title_text="Detected Genes", row=1, col=3)
439-
return fig
440-
441-
442444
def mitochondria_plot(adata: sc.AnnData, is_all_cells: bool) -> go.Figure:
443445
"""
444446
Generate a violin plot for mitochondrial content in cells.
@@ -712,7 +714,7 @@ def show_quant_log_table(quant_json_data: dict, permit_list_json_data: dict | No
712714
quant_table_content += f"{table_row}<td>{accordion_content}</td></tr>"
713715
else:
714716
# Directly display the content for other cases
715-
quant_table_content += f"{table_row}<td>{quant_json_data[key_lv1]}</td></tr>"
717+
quant_table_content += f"{table_row}<td>{str(quant_json_data[key_lv1])}</td></tr>"
716718

717719
# Create Permit List Table
718720
if permit_list_json_data:
@@ -743,7 +745,6 @@ def generate_summary_table(
743745
median_genes_per_cell: Median number of genes per retained cell.
744746
mapping_rate: Fraction of reads mapped to reference, or None.
745747
seq_saturation_value: Sequencing saturation percentage.
746-
export_summary_table: Boolean flag indicating whether to save the summary table as a CSV file.
747748
748749
Returns
749750
-------

0 commit comments

Comments
 (0)