apscale-gui 3.2.4__tar.gz → 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apscale_gui
3
- Version: 3.2.4
3
+ Version: 3.3.0
4
4
  Summary: Advanced Pipeline for Simple yet Comprehensive AnaLysEs of DNA metabarcoding data - Graphical User Interface
5
5
  Home-page: https://github.com/TillMacher/apscale_gui
6
6
  Author: Till-Hendrik Macher
@@ -25,6 +25,9 @@ import importlib.metadata
25
25
  import subprocess
26
26
  import asyncio
27
27
  import sys
28
+ import datetime, sys, getpass, requests_html, duckdb
29
+ from tqdm import tqdm
30
+ from stqdm import stqdm
28
31
 
29
32
  # demultiplexer2
30
33
  from demultiplexer2.create_primerset import create_primerset
@@ -51,9 +54,10 @@ from apscale_blast.__main__ import organism_filter as organism_filter
51
54
 
52
55
  # BOLDigger3
53
56
  from boldigger3 import id_engine
54
- from boldigger3 import metadata_download
55
57
  from boldigger3 import add_metadata
56
58
  from boldigger3 import select_top_hit
59
+ from boldigger3 import download_database
60
+ import requests
57
61
 
58
62
  # help texts
59
63
  n_cores_help = """
@@ -555,7 +559,117 @@ def download_seafile_zip(public_url, output_dir="downloads"):
555
559
 
556
560
  st.success(f"Finished the extraction of {len(list(output_dir.glob('*')))} databases!")
557
561
 
558
- def run_boldigger3(available_fasta_files, bold_modes, bold_databases):
562
+ class DownloadProgressBar(tqdm):
563
+ def update_to(self, b=1, bsize=1, tsize=None):
564
+ if tsize is not None:
565
+ self.total = tsize
566
+ self.update(b * bsize - self.n)
567
+
568
+ def run_boldigger3_db_download(output_dir, user_name, password):
569
+
570
+ """Download the latest BOLD public database and save it as a DuckDB file.
571
+
572
+ Logs into BOLD Systems, checks whether the local database is already up to
573
+ date, and if not, downloads the latest Parquet release and converts it to
574
+ DuckDB format.
575
+
576
+ Args:
577
+ output_file: Full path (including filename) for the output .ddb file.
578
+ user_name: BOLD Systems username.
579
+ password: BOLD Systems password.
580
+ """
581
+
582
+ # --- Login ---
583
+ data = {
584
+ "name": user_name,
585
+ "password": password,
586
+ "destination": "MAS_Management_UserConsole",
587
+ "loginType": "",
588
+ }
589
+
590
+ session = requests_html.HTMLSession()
591
+ session.post("https://bench.boldsystems.org/index.php/Login", data=data)
592
+
593
+ r = session.get("https://bench.boldsystems.org/index.php/datapackages/Latest")
594
+ log_out_text = r.html.find(".site-navigation > li:nth-child(4) > a:nth-child(1)")[0].text
595
+
596
+ if log_out_text != "Log out":
597
+ print(f"{datetime.datetime.now():%H:%M:%S}: Login failed, please check credentials.")
598
+ return
599
+
600
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Login successful, checking database status.")
601
+
602
+ # --- Check DB status ---
603
+ r = session.get("https://bench.boldsystems.org/index.php/datapackages/Latest")
604
+ button = r.html.find(
605
+ "div.row:nth-child(5) > div:nth-child(1) > table:nth-child(3) > tbody:nth-child(1) > tr:nth-child(4) > td:nth-child(2) > button:nth-child(1)"
606
+ )
607
+ package_id = button[0].attrs["data-package-id"]
608
+ data_url = button[0].attrs["data-url"]
609
+
610
+ parquet_path = output_dir / f"{package_id}.parquet"
611
+ database_path = output_dir / f"{package_id}.duckdb"
612
+
613
+ if database_path.is_file():
614
+ st.info(f"{datetime.datetime.now():%H:%M:%S}: Database is up to date.")
615
+ return
616
+
617
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Database does not exist or is outdated.")
618
+
619
+ # --- Download and convert ---
620
+ uid = session.get(f"https://bench.boldsystems.org{data_url}")
621
+ uid = uid.text.replace('"', "")
622
+ download_url = f"https://bench.boldsystems.org{data_url}&uid={uid}"
623
+
624
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Downloading latest database from {download_url}.")
625
+ print(f"{datetime.datetime.now():%H:%M:%S}: Downloading latest database from {download_url}.")
626
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Storing latest database as '{parquet_path}'.")
627
+ print(f"{datetime.datetime.now():%H:%M:%S}: Storing latest database as '{parquet_path}'.")
628
+
629
+ progress_bar = st.progress(0.0, text="Downloading public database...")
630
+ status = st.empty()
631
+ with requests.get(download_url, stream=True) as response:
632
+ response.raise_for_status()
633
+ total_size = int(response.headers.get("content-length", 0))
634
+ downloaded = 0
635
+ with open(parquet_path, "wb") as f:
636
+ for chunk in response.iter_content(chunk_size=8192):
637
+ if chunk:
638
+ f.write(chunk)
639
+ downloaded += len(chunk)
640
+ if total_size > 0:
641
+ progress = min(downloaded / total_size, 1.0)
642
+ progress_bar.progress(progress)
643
+ status.text(f"{downloaded / 1024 ** 2:.1f} MB / {total_size / 1024 ** 2:.1f} MB")
644
+ progress_bar.progress(1.0)
645
+ status.text("Download complete.")
646
+
647
+ if database_path.is_file():
648
+ database_path.unlink()
649
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Outdated database successfully removed.")
650
+
651
+ if not parquet_path.is_file():
652
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Could not find '{parquet_path}'.")
653
+ return
654
+
655
+ st.write(f"{datetime.datetime.now():%H:%M:%S}: Building new database.")
656
+
657
+ with duckdb.connect(database_path) as con:
658
+ con.execute(f"""
659
+ CREATE TABLE bold_public
660
+ AS SELECT
661
+ processid, sex, life_stage, inst,
662
+ "country/ocean", identified_by,
663
+ identification_method, coord, nuc, marker_code
664
+ FROM read_parquet('{parquet_path}')
665
+ """)
666
+
667
+ st.success(f"{datetime.datetime.now():%H:%M:%S}: New database saved at {database_path}.")
668
+
669
+ if parquet_path.is_file():
670
+ parquet_path.unlink()
671
+
672
+ def run_boldigger3_identification(available_fasta_files, bold_modes, bold_databases, db_path):
559
673
 
560
674
  st.info('Starting BOLDigger3! Please refer to the terminal for live outputs!')
561
675
  print('')
@@ -575,22 +689,15 @@ def run_boldigger3(available_fasta_files, bold_modes, bold_databases):
575
689
  thresholds.append(50)
576
690
 
577
691
  # Collect input values
578
- query_fasta = str(available_fasta_files[st.session_state['bold_query_fasta']])
692
+ query_fasta = Path(str(available_fasta_files[st.session_state['bold_query_fasta']]))
579
693
  mode = bold_modes[st.session_state['bold_mode']]
580
694
  db = bold_databases[st.session_state['bold_database']]
581
695
 
582
- # download the current metadata from BOLD
583
- metadata_download.main()
584
-
585
696
  # run the id engine
586
- id_engine.main(
587
- query_fasta,
588
- db,
589
- mode,
590
- )
697
+ id_engine.main(query_fasta, db, mode)
591
698
 
592
699
  # add additional data via the metadata
593
- add_metadata.main(query_fasta)
700
+ add_metadata.main(query_fasta, db_path)
594
701
 
595
702
  # select the top hit
596
703
  select_top_hit.main(query_fasta, thresholds)
@@ -680,7 +787,7 @@ def main():
680
787
  if f.is_dir() and "_apscale" in f.name
681
788
  ]
682
789
 
683
- if st.button(label='Remember project folder', key='remember_project_folder',use_container_width=True):
790
+ if st.button(label='Remember project folder', key='remember_project_folder', use_container_width=True):
684
791
  script_path = Path(__file__).resolve()
685
792
  user_data_txt = script_path.parent / '_user_data' / 'user_data.txt'
686
793
  user_data_txt.parent.mkdir(parents=True, exist_ok=True)
@@ -991,7 +1098,6 @@ def main():
991
1098
  update_settings_file(settings_xlsx, settings_dfs)
992
1099
  run_apscale(st.session_state['run_apscale_mode'], project_folder, modules_to_run)
993
1100
 
994
-
995
1101
  ############################################################################################################
996
1102
  st.markdown("---")
997
1103
  st.header("APSCALE BLAST")
@@ -1037,25 +1143,33 @@ def main():
1037
1143
  st.header("BOLDigger3")
1038
1144
  st.subheader('Settings')
1039
1145
  # General settings
1146
+ available_databases = {Path(i).name:Path(i) for i in glob.glob(str(path_to_projects / 'APSCALE_databases' / '*.duckdb'))}
1040
1147
  with st.expander("🗄️ Database & Operating Mode", expanded=False):
1041
- bold_databases = {
1042
- 'ANIMAL LIBRARY (PUBLIC)': 1,
1043
- 'ANIMAL SPECIES-LEVEL LIBRARY (PUBLIC + PRIVATE)': 2,
1044
- 'ANIMAL LIBRARY (PUBLIC+PRIVATE)': 3,
1045
- 'VALIDATED CANADIAN ARTHROPOD LIBRARY': 4,
1046
- 'PLANT LIBRARY (PUBLIC)': 5,
1047
- 'FUNGI LIBRARY (PUBLIC)': 6,
1048
- 'ANIMAL SECONDARY MARKERS (PUBLIC)': 7,
1049
- 'VALIDATED ANIMAL RED LIST LIBRARY': 8
1050
- }
1051
- st.selectbox(label='BOLD database', key='bold_database', options=list(bold_databases.keys()))
1052
- bold_modes = {
1053
- 'Rapid Species Search': 1,
1054
- 'Genus and Species Search': 2,
1055
- 'Exhaustive Search': 3
1056
- }
1057
- st.selectbox(label='Operating mode', key='bold_mode', options=list(bold_modes.keys()))
1058
- st.selectbox(label='Query FASTA', key='bold_query_fasta', options=list(available_fasta_files.keys()))
1148
+ col1, col2, col3 = st.columns(3)
1149
+ with col1:
1150
+ boldigger_db_file = st.selectbox(label='Local BOLD database file', key='boldigger_db_file', options=list(available_databases.keys()))
1151
+ st.selectbox(label='Query FASTA', key='bold_query_fasta', options=list(available_fasta_files.keys()))
1152
+ with col2:
1153
+ bold_modes = {
1154
+ 'Rapid Species Search': 1,
1155
+ 'Genus and Species Search': 2,
1156
+ 'Exhaustive Search': 3
1157
+ }
1158
+ st.selectbox(label='Operating mode', key='bold_mode', options=list(bold_modes.keys()))
1159
+ bold_databases = {
1160
+ 'ANIMAL LIBRARY (PUBLIC)': 1,
1161
+ 'ANIMAL SPECIES-LEVEL LIBRARY (PUBLIC + PRIVATE)': 2,
1162
+ 'ANIMAL LIBRARY (PUBLIC+PRIVATE)': 3,
1163
+ 'VALIDATED CANADIAN ARTHROPOD LIBRARY': 4,
1164
+ 'PLANT LIBRARY (PUBLIC)': 5,
1165
+ 'FUNGI LIBRARY (PUBLIC)': 6,
1166
+ 'ANIMAL SECONDARY MARKERS (PUBLIC)': 7,
1167
+ 'VALIDATED ANIMAL RED LIST LIBRARY': 8
1168
+ }
1169
+ st.selectbox(label='BOLD database', key='bold_database', options=list(bold_databases.keys()))
1170
+ with col3:
1171
+ st.text_input("Enter BOLD user name", key="bold_user")
1172
+ st.text_input("Enter BOLD password", type="password", key="bold_password")
1059
1173
  # Thresholds
1060
1174
  with st.expander("⚖️ Thresholds", expanded=False):
1061
1175
  col1, col2 = st.columns(2)
@@ -1068,8 +1182,21 @@ def main():
1068
1182
  st.text_input(label='Order (%)', key='bold_order', value=85)
1069
1183
 
1070
1184
  st.subheader('Run BOLDigger3')
1071
- if st.button(f'Start taxonomic assignment against BOLD'):
1072
- run_boldigger3(available_fasta_files, bold_modes, bold_databases)
1185
+ if not st.session_state['boldigger_db_file']:
1186
+ st.info('No local BOLD database file found: Latest Database file will be downloaded.')
1187
+
1188
+ if st.button(f'Download latest BOLD database', use_container_width=True):
1189
+ if st.session_state['bold_user'] and st.session_state['bold_password']:
1190
+ bold_download_folder = path_to_projects / 'APSCALE_databases'
1191
+ run_boldigger3_db_download(bold_download_folder, st.session_state['bold_user'], st.session_state['bold_password'])
1192
+ else:
1193
+ st.error('Please provide a BOLD user name and password.')
1194
+ if st.session_state['boldigger_db_file']:
1195
+ if st.button(f'Start taxonomic assignment against BOLD', use_container_width=True):
1196
+ db_path = available_databases[st.session_state['boldigger_db_file']]
1197
+ run_boldigger3_identification(available_fasta_files, bold_modes, bold_databases, db_path)
1198
+ else:
1199
+ st.info('Please download the latest BOLD database first.')
1073
1200
 
1074
1201
  ############################################################################################################
1075
1202
  st.markdown("---")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apscale_gui
3
- Version: 3.2.4
3
+ Version: 3.3.0
4
4
  Summary: Advanced Pipeline for Simple yet Comprehensive AnaLysEs of DNA metabarcoding data - Graphical User Interface
5
5
  Home-page: https://github.com/TillMacher/apscale_gui
6
6
  Author: Till-Hendrik Macher
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="apscale_gui",
8
- version="3.2.4",
8
+ version="3.3.0",
9
9
  author="Till-Hendrik Macher",
10
10
  author_email="macher@uni-trier.de",
11
11
  description="Advanced Pipeline for Simple yet Comprehensive AnaLysEs of DNA metabarcoding data - Graphical User Interface",
File without changes
File without changes
File without changes
File without changes