Halper-Stromberg commited on
Commit
f72768b
·
1 Parent(s): 9cb3cbb

Support optional gzip compression for FASTQ output files to reduce size

Browse files
Files changed (4) hide show
  1. pipeline.py +15 -1
  2. src/pipeline.py +15 -1
  3. src/streamlit_app.py +22 -10
  4. streamlit_app.py +22 -10
pipeline.py CHANGED
@@ -306,7 +306,8 @@ def generate_synthetic_bam(
306
  work_dir, snvs_bed, fasta_path, depth, vaf, rg_id, rg_sm,
307
  insert_size, insert_std, indel_interval, read_length=150,
308
  sequencing_mode="hybrid_capture",
309
- log_func=None, progress_func=None
 
310
  ):
311
  import pysam
312
  from collections import defaultdict
@@ -521,6 +522,19 @@ def generate_synthetic_bam(
521
  fastq_r2 = work_dir / "synthetic_R2.fastq"
522
  run_cmd(f"samtools fastq -1 {fastq_r1} -2 {fastq_r2} -0 {dev_null} -s {dev_null} {output_bam}", log_func)
523
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  if progress_func:
525
  progress_func(1.0, "Sorting and indexing BAM...")
526
 
 
306
  work_dir, snvs_bed, fasta_path, depth, vaf, rg_id, rg_sm,
307
  insert_size, insert_std, indel_interval, read_length=150,
308
  sequencing_mode="hybrid_capture",
309
+ log_func=None, progress_func=None,
310
+ compress_fastq=False
311
  ):
312
  import pysam
313
  from collections import defaultdict
 
522
  fastq_r2 = work_dir / "synthetic_R2.fastq"
523
  run_cmd(f"samtools fastq -1 {fastq_r1} -2 {fastq_r2} -0 {dev_null} -s {dev_null} {output_bam}", log_func)
524
 
525
+ if compress_fastq:
526
+ if progress_func:
527
+ progress_func(0.97, "Compressing FASTQ files (gzip)...")
528
+ import gzip
529
+ import shutil
530
+ log("Compressing FASTQ files to gzip format...", log_func)
531
+ for f_path in [fastq_r1, fastq_r2]:
532
+ gz_path = f_path.with_suffix(".fastq.gz")
533
+ with open(f_path, "rb") as f_in:
534
+ with gzip.open(gz_path, "wb", compresslevel=3) as f_out:
535
+ shutil.copyfileobj(f_in, f_out)
536
+ f_path.unlink()
537
+
538
  if progress_func:
539
  progress_func(1.0, "Sorting and indexing BAM...")
540
 
src/pipeline.py CHANGED
@@ -306,7 +306,8 @@ def generate_synthetic_bam(
306
  work_dir, snvs_bed, fasta_path, depth, vaf, rg_id, rg_sm,
307
  insert_size, insert_std, indel_interval, read_length=150,
308
  sequencing_mode="hybrid_capture",
309
- log_func=None, progress_func=None
 
310
  ):
311
  import pysam
312
  from collections import defaultdict
@@ -521,6 +522,19 @@ def generate_synthetic_bam(
521
  fastq_r2 = work_dir / "synthetic_R2.fastq"
522
  run_cmd(f"samtools fastq -1 {fastq_r1} -2 {fastq_r2} -0 {dev_null} -s {dev_null} {output_bam}", log_func)
523
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  if progress_func:
525
  progress_func(1.0, "Sorting and indexing BAM...")
526
 
 
306
  work_dir, snvs_bed, fasta_path, depth, vaf, rg_id, rg_sm,
307
  insert_size, insert_std, indel_interval, read_length=150,
308
  sequencing_mode="hybrid_capture",
309
+ log_func=None, progress_func=None,
310
+ compress_fastq=False
311
  ):
312
  import pysam
313
  from collections import defaultdict
 
522
  fastq_r2 = work_dir / "synthetic_R2.fastq"
523
  run_cmd(f"samtools fastq -1 {fastq_r1} -2 {fastq_r2} -0 {dev_null} -s {dev_null} {output_bam}", log_func)
524
 
525
+ if compress_fastq:
526
+ if progress_func:
527
+ progress_func(0.97, "Compressing FASTQ files (gzip)...")
528
+ import gzip
529
+ import shutil
530
+ log("Compressing FASTQ files to gzip format...", log_func)
531
+ for f_path in [fastq_r1, fastq_r2]:
532
+ gz_path = f_path.with_suffix(".fastq.gz")
533
+ with open(f_path, "rb") as f_in:
534
+ with gzip.open(gz_path, "wb", compresslevel=3) as f_out:
535
+ shutil.copyfileobj(f_in, f_out)
536
+ f_path.unlink()
537
+
538
  if progress_func:
539
  progress_func(1.0, "Sorting and indexing BAM...")
540
 
src/streamlit_app.py CHANGED
@@ -346,6 +346,13 @@ with st.sidebar:
346
  rg_id = st.text_input("Read Group ID", value="CPDV2510843-SEQ-251103")
347
  rg_sm = st.text_input("Sample Name", value="CPDV2510843-SEQ-251103")
348
 
 
 
 
 
 
 
 
349
  st.divider()
350
  st.subheader("🛠️ Debug Info")
351
  st.caption("Helpful diagnostics for troubleshooting deployment status.")
@@ -647,6 +654,7 @@ if run_btn:
647
  sequencing_mode="pcr_amplicon" if seq_mode.startswith("PCR Amplicon") else "hybrid_capture",
648
  log_func=append_log,
649
  progress_func=bam_progress,
 
650
  )
651
 
652
  update_progress(1.0, "Done!")
@@ -662,8 +670,8 @@ if run_btn:
662
  static_dest_cwd = Path("static") / work_dir_name
663
  static_dest_script = Path(__file__).parent / "static" / work_dir_name
664
 
665
- fastq_r1_path = work_dir / "synthetic_R1.fastq"
666
- fastq_r2_path = work_dir / "synthetic_R2.fastq"
667
 
668
  for dest in [static_dest_cwd, static_dest_script]:
669
  dest.mkdir(parents=True, exist_ok=True)
@@ -677,9 +685,9 @@ if run_btn:
677
  if mane_transcripts_bed and mane_transcripts_bed.exists():
678
  shutil.copy(mane_transcripts_bed, dest / "mane_transcripts.bed")
679
  if fastq_r1_path.exists():
680
- shutil.copy(fastq_r1_path, dest / "synthetic_R1.fastq")
681
  if fastq_r2_path.exists():
682
- shutil.copy(fastq_r2_path, dest / "synthetic_R2.fastq")
683
 
684
  # Store URLs and path strings — never load large files into session_state memory
685
  st.session_state["results"] = {
@@ -699,10 +707,11 @@ if run_btn:
699
  "igv_bed_url": f"/app/static/{work_dir_name}/igv_variant_navigator.bed",
700
  "fully_covered_bed_url": f"/app/static/{work_dir_name}/fully_covered_exons.bed" if fully_bed_path and fully_bed_path.exists() else None,
701
  "mane_transcripts_bed_url": f"/app/static/{work_dir_name}/mane_transcripts.bed" if mane_transcripts_bed and mane_transcripts_bed.exists() else None,
702
- "fastq_r1_url": f"/app/static/{work_dir_name}/synthetic_R1.fastq" if fastq_r1_path.exists() else None,
703
- "fastq_r2_url": f"/app/static/{work_dir_name}/synthetic_R2.fastq" if fastq_r2_path.exists() else None,
704
  "work_dir_name": work_dir_name,
705
  "genome_version": genome_version,
 
706
  }
707
  st.session_state["log_lines"] = log_lines[:]
708
 
@@ -861,8 +870,11 @@ if "results" in st.session_state:
861
 
862
  # Row 3 of download buttons (FASTQ R1/R2)
863
  dl7, dl8, dl9 = st.columns(3)
 
 
 
864
  with dl7:
865
- st.markdown("**Synthetic FASTQ R1 (Forward)**")
866
  fastq_r1_path = Path(res["fastq_r1_path"]) if res.get("fastq_r1_path") else None
867
  if fastq_r1_path and fastq_r1_path.exists():
868
  if st.checkbox("Prepare FASTQ R1", key="prep_r1", help="Click to load R1 FASTQ file into memory to prepare it for download."):
@@ -870,12 +882,12 @@ if "results" in st.session_state:
870
  st.download_button(
871
  "⬇ Download FASTQ R1",
872
  data=f,
873
- file_name="synthetic_R1.fastq",
874
  mime="application/octet-stream",
875
  use_container_width=True,
876
  )
877
  with dl8:
878
- st.markdown("**Synthetic FASTQ R2 (Reverse)**")
879
  fastq_r2_path = Path(res["fastq_r2_path"]) if res.get("fastq_r2_path") else None
880
  if fastq_r2_path and fastq_r2_path.exists():
881
  if st.checkbox("Prepare FASTQ R2", key="prep_r2", help="Click to load R2 FASTQ file into memory to prepare it for download."):
@@ -883,7 +895,7 @@ if "results" in st.session_state:
883
  st.download_button(
884
  "⬇ Download FASTQ R2",
885
  data=f,
886
- file_name="synthetic_R2.fastq",
887
  mime="application/octet-stream",
888
  use_container_width=True,
889
  )
 
346
  rg_id = st.text_input("Read Group ID", value="CPDV2510843-SEQ-251103")
347
  rg_sm = st.text_input("Sample Name", value="CPDV2510843-SEQ-251103")
348
 
349
+ st.subheader("FASTQ Output")
350
+ compress_fastq = st.checkbox(
351
+ "Gzip FASTQ output (.fastq.gz)",
352
+ value=True,
353
+ help="Compress the generated FASTQ files using gzip. This makes the files about 4-5x smaller, saving disk space and download time."
354
+ )
355
+
356
  st.divider()
357
  st.subheader("🛠️ Debug Info")
358
  st.caption("Helpful diagnostics for troubleshooting deployment status.")
 
654
  sequencing_mode="pcr_amplicon" if seq_mode.startswith("PCR Amplicon") else "hybrid_capture",
655
  log_func=append_log,
656
  progress_func=bam_progress,
657
+ compress_fastq=compress_fastq,
658
  )
659
 
660
  update_progress(1.0, "Done!")
 
670
  static_dest_cwd = Path("static") / work_dir_name
671
  static_dest_script = Path(__file__).parent / "static" / work_dir_name
672
 
673
+ fastq_r1_path = work_dir / ("synthetic_R1.fastq.gz" if compress_fastq else "synthetic_R1.fastq")
674
+ fastq_r2_path = work_dir / ("synthetic_R2.fastq.gz" if compress_fastq else "synthetic_R2.fastq")
675
 
676
  for dest in [static_dest_cwd, static_dest_script]:
677
  dest.mkdir(parents=True, exist_ok=True)
 
685
  if mane_transcripts_bed and mane_transcripts_bed.exists():
686
  shutil.copy(mane_transcripts_bed, dest / "mane_transcripts.bed")
687
  if fastq_r1_path.exists():
688
+ shutil.copy(fastq_r1_path, dest / fastq_r1_path.name)
689
  if fastq_r2_path.exists():
690
+ shutil.copy(fastq_r2_path, dest / fastq_r2_path.name)
691
 
692
  # Store URLs and path strings — never load large files into session_state memory
693
  st.session_state["results"] = {
 
707
  "igv_bed_url": f"/app/static/{work_dir_name}/igv_variant_navigator.bed",
708
  "fully_covered_bed_url": f"/app/static/{work_dir_name}/fully_covered_exons.bed" if fully_bed_path and fully_bed_path.exists() else None,
709
  "mane_transcripts_bed_url": f"/app/static/{work_dir_name}/mane_transcripts.bed" if mane_transcripts_bed and mane_transcripts_bed.exists() else None,
710
+ "fastq_r1_url": f"/app/static/{work_dir_name}/{fastq_r1_path.name}" if fastq_r1_path.exists() else None,
711
+ "fastq_r2_url": f"/app/static/{work_dir_name}/{fastq_r2_path.name}" if fastq_r2_path.exists() else None,
712
  "work_dir_name": work_dir_name,
713
  "genome_version": genome_version,
714
+ "compress_fastq": compress_fastq,
715
  }
716
  st.session_state["log_lines"] = log_lines[:]
717
 
 
870
 
871
  # Row 3 of download buttons (FASTQ R1/R2)
872
  dl7, dl8, dl9 = st.columns(3)
873
+ is_compressed = res.get("compress_fastq", False)
874
+ ext = ".fastq.gz" if is_compressed else ".fastq"
875
+
876
  with dl7:
877
+ st.markdown(f"**Synthetic FASTQ R1 (Forward){' (gzipped)' if is_compressed else ''}**")
878
  fastq_r1_path = Path(res["fastq_r1_path"]) if res.get("fastq_r1_path") else None
879
  if fastq_r1_path and fastq_r1_path.exists():
880
  if st.checkbox("Prepare FASTQ R1", key="prep_r1", help="Click to load R1 FASTQ file into memory to prepare it for download."):
 
882
  st.download_button(
883
  "⬇ Download FASTQ R1",
884
  data=f,
885
+ file_name=f"synthetic_R1{ext}",
886
  mime="application/octet-stream",
887
  use_container_width=True,
888
  )
889
  with dl8:
890
+ st.markdown(f"**Synthetic FASTQ R2 (Reverse){' (gzipped)' if is_compressed else ''}**")
891
  fastq_r2_path = Path(res["fastq_r2_path"]) if res.get("fastq_r2_path") else None
892
  if fastq_r2_path and fastq_r2_path.exists():
893
  if st.checkbox("Prepare FASTQ R2", key="prep_r2", help="Click to load R2 FASTQ file into memory to prepare it for download."):
 
895
  st.download_button(
896
  "⬇ Download FASTQ R2",
897
  data=f,
898
+ file_name=f"synthetic_R2{ext}",
899
  mime="application/octet-stream",
900
  use_container_width=True,
901
  )
streamlit_app.py CHANGED
@@ -346,6 +346,13 @@ with st.sidebar:
346
  rg_id = st.text_input("Read Group ID", value="CPDV2510843-SEQ-251103")
347
  rg_sm = st.text_input("Sample Name", value="CPDV2510843-SEQ-251103")
348
 
 
 
 
 
 
 
 
349
  st.divider()
350
  st.subheader("🛠️ Debug Info")
351
  st.caption("Helpful diagnostics for troubleshooting deployment status.")
@@ -647,6 +654,7 @@ if run_btn:
647
  sequencing_mode="pcr_amplicon" if seq_mode.startswith("PCR Amplicon") else "hybrid_capture",
648
  log_func=append_log,
649
  progress_func=bam_progress,
 
650
  )
651
 
652
  update_progress(1.0, "Done!")
@@ -662,8 +670,8 @@ if run_btn:
662
  static_dest_cwd = Path("static") / work_dir_name
663
  static_dest_script = Path(__file__).parent / "static" / work_dir_name
664
 
665
- fastq_r1_path = work_dir / "synthetic_R1.fastq"
666
- fastq_r2_path = work_dir / "synthetic_R2.fastq"
667
 
668
  for dest in [static_dest_cwd, static_dest_script]:
669
  dest.mkdir(parents=True, exist_ok=True)
@@ -677,9 +685,9 @@ if run_btn:
677
  if mane_transcripts_bed and mane_transcripts_bed.exists():
678
  shutil.copy(mane_transcripts_bed, dest / "mane_transcripts.bed")
679
  if fastq_r1_path.exists():
680
- shutil.copy(fastq_r1_path, dest / "synthetic_R1.fastq")
681
  if fastq_r2_path.exists():
682
- shutil.copy(fastq_r2_path, dest / "synthetic_R2.fastq")
683
 
684
  # Store URLs and path strings — never load large files into session_state memory
685
  st.session_state["results"] = {
@@ -699,10 +707,11 @@ if run_btn:
699
  "igv_bed_url": f"/app/static/{work_dir_name}/igv_variant_navigator.bed",
700
  "fully_covered_bed_url": f"/app/static/{work_dir_name}/fully_covered_exons.bed" if fully_bed_path and fully_bed_path.exists() else None,
701
  "mane_transcripts_bed_url": f"/app/static/{work_dir_name}/mane_transcripts.bed" if mane_transcripts_bed and mane_transcripts_bed.exists() else None,
702
- "fastq_r1_url": f"/app/static/{work_dir_name}/synthetic_R1.fastq" if fastq_r1_path.exists() else None,
703
- "fastq_r2_url": f"/app/static/{work_dir_name}/synthetic_R2.fastq" if fastq_r2_path.exists() else None,
704
  "work_dir_name": work_dir_name,
705
  "genome_version": genome_version,
 
706
  }
707
  st.session_state["log_lines"] = log_lines[:]
708
 
@@ -861,8 +870,11 @@ if "results" in st.session_state:
861
 
862
  # Row 3 of download buttons (FASTQ R1/R2)
863
  dl7, dl8, dl9 = st.columns(3)
 
 
 
864
  with dl7:
865
- st.markdown("**Synthetic FASTQ R1 (Forward)**")
866
  fastq_r1_path = Path(res["fastq_r1_path"]) if res.get("fastq_r1_path") else None
867
  if fastq_r1_path and fastq_r1_path.exists():
868
  if st.checkbox("Prepare FASTQ R1", key="prep_r1", help="Click to load R1 FASTQ file into memory to prepare it for download."):
@@ -870,12 +882,12 @@ if "results" in st.session_state:
870
  st.download_button(
871
  "⬇ Download FASTQ R1",
872
  data=f,
873
- file_name="synthetic_R1.fastq",
874
  mime="application/octet-stream",
875
  use_container_width=True,
876
  )
877
  with dl8:
878
- st.markdown("**Synthetic FASTQ R2 (Reverse)**")
879
  fastq_r2_path = Path(res["fastq_r2_path"]) if res.get("fastq_r2_path") else None
880
  if fastq_r2_path and fastq_r2_path.exists():
881
  if st.checkbox("Prepare FASTQ R2", key="prep_r2", help="Click to load R2 FASTQ file into memory to prepare it for download."):
@@ -883,7 +895,7 @@ if "results" in st.session_state:
883
  st.download_button(
884
  "⬇ Download FASTQ R2",
885
  data=f,
886
- file_name="synthetic_R2.fastq",
887
  mime="application/octet-stream",
888
  use_container_width=True,
889
  )
 
346
  rg_id = st.text_input("Read Group ID", value="CPDV2510843-SEQ-251103")
347
  rg_sm = st.text_input("Sample Name", value="CPDV2510843-SEQ-251103")
348
 
349
+ st.subheader("FASTQ Output")
350
+ compress_fastq = st.checkbox(
351
+ "Gzip FASTQ output (.fastq.gz)",
352
+ value=True,
353
+ help="Compress the generated FASTQ files using gzip. This makes the files about 4-5x smaller, saving disk space and download time."
354
+ )
355
+
356
  st.divider()
357
  st.subheader("🛠️ Debug Info")
358
  st.caption("Helpful diagnostics for troubleshooting deployment status.")
 
654
  sequencing_mode="pcr_amplicon" if seq_mode.startswith("PCR Amplicon") else "hybrid_capture",
655
  log_func=append_log,
656
  progress_func=bam_progress,
657
+ compress_fastq=compress_fastq,
658
  )
659
 
660
  update_progress(1.0, "Done!")
 
670
  static_dest_cwd = Path("static") / work_dir_name
671
  static_dest_script = Path(__file__).parent / "static" / work_dir_name
672
 
673
+ fastq_r1_path = work_dir / ("synthetic_R1.fastq.gz" if compress_fastq else "synthetic_R1.fastq")
674
+ fastq_r2_path = work_dir / ("synthetic_R2.fastq.gz" if compress_fastq else "synthetic_R2.fastq")
675
 
676
  for dest in [static_dest_cwd, static_dest_script]:
677
  dest.mkdir(parents=True, exist_ok=True)
 
685
  if mane_transcripts_bed and mane_transcripts_bed.exists():
686
  shutil.copy(mane_transcripts_bed, dest / "mane_transcripts.bed")
687
  if fastq_r1_path.exists():
688
+ shutil.copy(fastq_r1_path, dest / fastq_r1_path.name)
689
  if fastq_r2_path.exists():
690
+ shutil.copy(fastq_r2_path, dest / fastq_r2_path.name)
691
 
692
  # Store URLs and path strings — never load large files into session_state memory
693
  st.session_state["results"] = {
 
707
  "igv_bed_url": f"/app/static/{work_dir_name}/igv_variant_navigator.bed",
708
  "fully_covered_bed_url": f"/app/static/{work_dir_name}/fully_covered_exons.bed" if fully_bed_path and fully_bed_path.exists() else None,
709
  "mane_transcripts_bed_url": f"/app/static/{work_dir_name}/mane_transcripts.bed" if mane_transcripts_bed and mane_transcripts_bed.exists() else None,
710
+ "fastq_r1_url": f"/app/static/{work_dir_name}/{fastq_r1_path.name}" if fastq_r1_path.exists() else None,
711
+ "fastq_r2_url": f"/app/static/{work_dir_name}/{fastq_r2_path.name}" if fastq_r2_path.exists() else None,
712
  "work_dir_name": work_dir_name,
713
  "genome_version": genome_version,
714
+ "compress_fastq": compress_fastq,
715
  }
716
  st.session_state["log_lines"] = log_lines[:]
717
 
 
870
 
871
  # Row 3 of download buttons (FASTQ R1/R2)
872
  dl7, dl8, dl9 = st.columns(3)
873
+ is_compressed = res.get("compress_fastq", False)
874
+ ext = ".fastq.gz" if is_compressed else ".fastq"
875
+
876
  with dl7:
877
+ st.markdown(f"**Synthetic FASTQ R1 (Forward){' (gzipped)' if is_compressed else ''}**")
878
  fastq_r1_path = Path(res["fastq_r1_path"]) if res.get("fastq_r1_path") else None
879
  if fastq_r1_path and fastq_r1_path.exists():
880
  if st.checkbox("Prepare FASTQ R1", key="prep_r1", help="Click to load R1 FASTQ file into memory to prepare it for download."):
 
882
  st.download_button(
883
  "⬇ Download FASTQ R1",
884
  data=f,
885
+ file_name=f"synthetic_R1{ext}",
886
  mime="application/octet-stream",
887
  use_container_width=True,
888
  )
889
  with dl8:
890
+ st.markdown(f"**Synthetic FASTQ R2 (Reverse){' (gzipped)' if is_compressed else ''}**")
891
  fastq_r2_path = Path(res["fastq_r2_path"]) if res.get("fastq_r2_path") else None
892
  if fastq_r2_path and fastq_r2_path.exists():
893
  if st.checkbox("Prepare FASTQ R2", key="prep_r2", help="Click to load R2 FASTQ file into memory to prepare it for download."):
 
895
  st.download_button(
896
  "⬇ Download FASTQ R2",
897
  data=f,
898
+ file_name=f"synthetic_R2{ext}",
899
  mime="application/octet-stream",
900
  use_container_width=True,
901
  )