scout-browser 4.93.1__py3-none-any.whl → 4.94__py3-none-any.whl

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.
Files changed (35) hide show
  1. scout/adapter/mongo/base.py +0 -0
  2. scout/adapter/mongo/query.py +88 -53
  3. scout/adapter/mongo/variant.py +2 -2
  4. scout/commands/export/export_command.py +0 -0
  5. scout/commands/load/base.py +0 -0
  6. scout/commands/load/user.py +0 -0
  7. scout/commands/update/disease.py +0 -0
  8. scout/commands/update/genes.py +0 -0
  9. scout/commands/wipe_database.py +0 -0
  10. scout/parse/variant/compound.py +0 -0
  11. scout/parse/variant/gene.py +0 -0
  12. scout/parse/variant/genotype.py +0 -0
  13. scout/resources/custom_igv_tracks/mane.bb +0 -0
  14. scout/server/blueprints/cases/controllers.py +21 -0
  15. scout/server/blueprints/cases/templates/cases/case_report.html +8 -0
  16. scout/server/blueprints/clinvar/controllers.py +4 -5
  17. scout/server/blueprints/institutes/controllers.py +129 -67
  18. scout/server/blueprints/institutes/forms.py +5 -2
  19. scout/server/blueprints/institutes/templates/overview/cases.html +6 -0
  20. scout/server/blueprints/institutes/templates/overview/utils.html +6 -5
  21. scout/server/blueprints/variant/templates/variant/tx_overview.html +3 -3
  22. scout/server/blueprints/variants/templates/variants/cancer-sv-variants.html +4 -18
  23. scout/server/blueprints/variants/templates/variants/cancer-variants.html +2 -12
  24. scout/server/blueprints/variants/templates/variants/components.html +15 -1
  25. scout/server/blueprints/variants/templates/variants/sv-variants.html +2 -2
  26. scout/server/links.py +1 -1
  27. scout/utils/acmg.py +0 -1
  28. scout/utils/md5.py +0 -0
  29. {scout_browser-4.93.1.dist-info → scout_browser-4.94.dist-info}/METADATA +66 -45
  30. {scout_browser-4.93.1.dist-info → scout_browser-4.94.dist-info}/RECORD +22 -23
  31. {scout_browser-4.93.1.dist-info → scout_browser-4.94.dist-info}/WHEEL +1 -2
  32. scout/__version__.py +0 -1
  33. scout_browser-4.93.1.dist-info/top_level.txt +0 -1
  34. {scout_browser-4.93.1.dist-info → scout_browser-4.94.dist-info}/entry_points.txt +0 -0
  35. {scout_browser-4.93.1.dist-info → scout_browser-4.94.dist-info/licenses}/LICENSE +0 -0
File without changes
@@ -474,63 +474,98 @@ class QueryHandler(object):
474
474
 
475
475
  return mongo_query
476
476
 
477
- def sv_coordinate_query(self, query):
478
- """Adds genomic coordinated-related filters to the query object
479
- This method is called to buid coordinate query for sv variants
480
-
481
- Args:
482
- query(dict): a dictionary of query filters specified by the users
483
- mongo_query(dict): the query that is going to be submitted to the database
477
+ def get_position_query(self, chrom: str, start: int, end: int) -> dict:
478
+ """Helper function that returns a dictionary containing start and stop coordinates.
479
+
480
+ The position query consists of 3 parts, each of them elements of the $or
481
+ First part applies to searches when chromosome and end_chrom are the same.
482
+ Here are the possible overlapping search scenarios:
483
+ # Case 1
484
+ # filter xxxxxxxxx
485
+ # Variant xxxxxxxx
486
+
487
+ # Case 2
488
+ # filter xxxxxxxxx
489
+ # Variant xxxxxxxx
490
+
491
+ # Case 3
492
+ # filter xxxxxxxxx
493
+ # Variant xx
494
+
495
+ # Case 4
496
+ # filter xxxxxxxxx
497
+ # Variant xxxxxxxxxxxxxx
498
+
499
+ Second and third elements of the $or cover queries for variants where chromosome != end_chrom.
500
+ In this situation there are the following scenarios:
501
+ - Case chromosome != end_chrom, position matching 'chromosome'
502
+ - Case chromosome != end_chrom, position matching 'end_chrom'
503
+ """
484
504
 
485
- Returns:
486
- coordinate_query(dict): returned object contains coordinate filters for sv variant
505
+ return {
506
+ "$or": [
507
+ # Case chromosome == end_chrom
508
+ {
509
+ "$and": [
510
+ {"chromosome": chrom},
511
+ {"end_chrom": chrom},
512
+ {
513
+ "$or": [
514
+ # Overlapping cases 1-4 (chromosome == end_chrom)
515
+ {"end": {"$gte": start, "$lte": end}}, # Case 1
516
+ {"position": {"$gte": start, "$lte": end}}, # Case 2
517
+ {
518
+ "$and": [
519
+ {"position": {"$lte": start}},
520
+ {"end": {"$gte": end}},
521
+ ]
522
+ }, # Case 3
523
+ {
524
+ "$and": [
525
+ {"position": {"$gte": start}},
526
+ {"end": {"$lte": end}},
527
+ ]
528
+ }, # Case 4
529
+ ]
530
+ },
531
+ ]
532
+ },
533
+ # Case chromosome != end_chrom, position matching 'chromosome'
534
+ {
535
+ "$and": [
536
+ {"chromosome": chrom},
537
+ {"end_chrom": {"$ne": chrom}},
538
+ {"position": {"$gte": start}},
539
+ {"position": {"$lte": end}},
540
+ ]
541
+ },
542
+ # Case chromosome != end_chrom, position matching 'end_chrom'
543
+ {
544
+ "$and": [
545
+ {"chromosome": {"$ne": chrom}},
546
+ {"end_chrom": chrom},
547
+ {"end": {"$gte": start}},
548
+ {"end": {"$lte": end}},
549
+ ]
550
+ },
551
+ ]
552
+ }
487
553
 
554
+ def sv_coordinate_query(self, query: dict) -> dict:
555
+ """Adds genomic coordinated-related filters to the query object
556
+ This method is called to build coordinate query for sv variants
488
557
  """
489
- coordinate_query = None
490
- chromosome_query = {"$or": [{"chromosome": query["chrom"]}, {"end_chrom": query["chrom"]}]}
491
- if query.get("start") and query.get("end"):
492
- # Query for overlapping intervals. Taking into account these cases:
493
- # 1
494
- # filter xxxxxxxxx
495
- # Variant xxxxxxxx
496
-
497
- # 2
498
- # filter xxxxxxxxx
499
- # Variant xxxxxxxx
500
-
501
- # 3
502
- # filter xxxxxxxxx
503
- # Variant xx
504
-
505
- # 4
506
- # filter xxxxxxxxx
507
- # Variant xxxxxxxxxxxxxx
508
- position_query = {
509
- "$or": [
510
- {"end": {"$gte": int(query["start"]), "$lte": int(query["end"])}}, # 1
511
- {
512
- "position": {
513
- "$lte": int(query["end"]),
514
- "$gte": int(query["start"]),
515
- }
516
- }, # 2
517
- {
518
- "$and": [
519
- {"position": {"$gte": int(query["start"])}},
520
- {"end": {"$lte": int(query["end"])}},
521
- ]
522
- }, # 3
523
- {
524
- "$and": [
525
- {"position": {"$lte": int(query["start"])}},
526
- {"end": {"$gte": int(query["end"])}},
527
- ]
528
- }, # 4
529
- ]
558
+ if (
559
+ query.get("start") is not None and query.get("end") is not None
560
+ ): # query contains full coordinates
561
+ chrom = query["chrom"]
562
+ start = int(query["start"])
563
+ end = int(query["end"])
564
+ coordinate_query = self.get_position_query(chrom=chrom, start=start, end=end)
565
+ else: # query contains only chromosome info
566
+ coordinate_query = {
567
+ "$or": [{"chromosome": query["chrom"]}, {"end_chrom": query["chrom"]}]
530
568
  }
531
- coordinate_query = {"$and": [chromosome_query, position_query]}
532
- else:
533
- coordinate_query = chromosome_query
534
569
  return coordinate_query
535
570
 
536
571
  def gene_filter(self, query, build="37"):
@@ -311,14 +311,14 @@ class VariantHandler(VariantLoader):
311
311
  "category": variant_obj["category"], # sv
312
312
  "variant_type": variant_obj["variant_type"], # clinical or research
313
313
  "sub_category": variant_obj["sub_category"], # example -> "del"
314
- "$and": coordinate_query["$and"], # query for overlapping SV variants
314
+ "$or": coordinate_query["$or"], # query for overlapping SV variants
315
315
  }
316
-
317
316
  overlapping_svs = list(
318
317
  self.variant_collection.find(
319
318
  query,
320
319
  )
321
320
  )
321
+
322
322
  if not overlapping_svs:
323
323
  return None
324
324
  if len(overlapping_svs) == 1:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -62,6 +62,7 @@ from scout.server.utils import (
62
62
  case_has_rna_tracks,
63
63
  institute_and_case,
64
64
  )
65
+ from scout.utils.acmg import get_acmg_temperature
65
66
 
66
67
  LOG = logging.getLogger(__name__)
67
68
 
@@ -611,6 +612,22 @@ def check_outdated_gene_panel(panel_obj, latest_panel):
611
612
  return extra_genes, missing_genes
612
613
 
613
614
 
615
+ def add_bayesian_acmg_classification(variant_obj: dict):
616
+ """Append info to display the ACMG VUS Bayesian score / temperature."""
617
+ variant_acmg_classifications = list(
618
+ store.get_evaluations_case_specific(document_id=variant_obj["_id"])
619
+ )
620
+ if variant_acmg_classifications:
621
+ variant_obj["bayesian_acmg"] = get_acmg_temperature(
622
+ set(
623
+ [
624
+ criterium.get("term")
625
+ for criterium in variant_acmg_classifications[0].get("criteria", [])
626
+ ]
627
+ )
628
+ )
629
+
630
+
614
631
  def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dict, data: dict):
615
632
  """Gather evaluated variants info to include in case report."""
616
633
 
@@ -624,6 +641,7 @@ def case_report_variants(store: MongoAdapter, case_obj: dict, institute_obj: dic
624
641
  continue
625
642
  if case_key == "partial_causatives":
626
643
  var_obj["phenotypes"] = case_obj["partial_causatives"][var_id]
644
+ add_bayesian_acmg_classification(var_obj)
627
645
  evaluated_variants_by_type[eval_category].append(
628
646
  _get_decorated_var(var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj)
629
647
  )
@@ -663,6 +681,9 @@ def _append_evaluated_variant_by_type(
663
681
  """
664
682
  for eval_category, variant_key in CASE_REPORT_VARIANT_TYPES.items():
665
683
  if variant_key in var_obj and var_obj[variant_key] is not None:
684
+
685
+ add_bayesian_acmg_classification(var_obj)
686
+
666
687
  evaluated_variants_by_type[eval_category].append(
667
688
  _get_decorated_var(var_obj=var_obj, institute_obj=institute_obj, case_obj=case_obj)
668
689
  )
@@ -668,6 +668,7 @@
668
668
  <th>Inheritance models</th>
669
669
  {% endif %}
670
670
  <th>ACMG classification</th>
671
+ <th>Bayesian classification</th>
671
672
  {% if cancer %}
672
673
  <th>ClinGen-CGC-VICC classification</th>
673
674
  {% endif %}
@@ -718,6 +719,13 @@
718
719
  {% else %}
719
720
  -
720
721
  {% endif %}
722
+ <td>
723
+ {% if variant.bayesian_acmg %}
724
+ <span class="badge rounded-pill bg-{{variant.bayesian_acmg.temperature_class}}">Score {{variant.bayesian_acmg.points|string}} <span class='fa {{variant.bayesian_acmg.temperature_icon}}'></span> {{variant.bayesian_acmg.temperature}} ({{variant.bayesian_acmg.point_classification}})</span>
725
+ {% else %}
726
+ -
727
+ {% endif %}
728
+ </td>
721
729
  </td>
722
730
  {% if cancer %}
723
731
  <td>
@@ -399,11 +399,10 @@ def json_api_submission(submission_id):
399
399
  afile.flush()
400
400
  afile.seek(0)
401
401
 
402
- with NamedTemporaryFile(
403
- mode="a+", prefix="Variant", suffix=".csv"
404
- ) as variant_file, NamedTemporaryFile(
405
- mode="a+", prefix="CaseData", suffix=".csv"
406
- ) as casedata_file:
402
+ with (
403
+ NamedTemporaryFile(mode="a+", prefix="Variant", suffix=".csv") as variant_file,
404
+ NamedTemporaryFile(mode="a+", prefix="CaseData", suffix=".csv") as casedata_file,
405
+ ):
407
406
  # Write temp Variant CSV file
408
407
  _, variants_header, variants_lines = clinvar_submission_file(
409
408
  submission_id, "variant_data", "SUB000"
@@ -3,7 +3,7 @@ import datetime
3
3
  import logging
4
4
  from typing import Dict, List, Optional, Tuple
5
5
 
6
- from flask import Response, current_app, flash, url_for
6
+ from flask import Response, current_app, flash, request, url_for
7
7
  from flask_login import current_user
8
8
  from pymongo import ASCENDING, DESCENDING
9
9
  from pymongo.cursor import Cursor
@@ -11,10 +11,13 @@ from werkzeug.datastructures import Headers, MultiDict
11
11
 
12
12
  from scout.adapter.mongo.base import MongoAdapter
13
13
  from scout.constants import (
14
+ CANCER_PHENOTYPE_MAP,
14
15
  CASE_STATUSES,
15
16
  DATE_DAY_FORMATTER,
16
17
  ID_PROJECTION,
17
18
  PHENOTYPE_GROUPS,
19
+ PHENOTYPE_MAP,
20
+ SEX_MAP,
18
21
  VARIANTS_TARGET_FROM_CATEGORY,
19
22
  )
20
23
  from scout.server.blueprints.variant.utils import predictions, update_representative_gene
@@ -422,97 +425,129 @@ def _sort_cases(data, request, all_cases):
422
425
  return all_cases
423
426
 
424
427
 
425
- def cases(store, request, institute_id):
426
- """Preprocess case objects.
428
+ def export_case_samples(institute_id, filtered_cases) -> Response:
429
+ """Export to CSV file a list of samples from selected cases."""
430
+ EXPORT_HEADER = [
431
+ "Sample ID",
432
+ "Sample Name",
433
+ "Analysis",
434
+ "Affected status",
435
+ "Sex",
436
+ "Sex confirmed",
437
+ "Parenthood confirmed",
438
+ "Predicted ancestry",
439
+ "Tissue",
440
+ "Case Name",
441
+ "Case ID",
442
+ "Analysis date",
443
+ "Case Status",
444
+ "Case phenotypes",
445
+ "Research",
446
+ "Track",
447
+ "Default panels",
448
+ "Genome build",
449
+ "SNV/SV rank models",
450
+ ]
451
+ export_lines = []
452
+ export_lines.append("\t".join(EXPORT_HEADER)) # Use tab-separated values
453
+ for case in filtered_cases:
454
+ for individual in case.get("individuals", []):
455
+ export_line = [
456
+ individual["individual_id"],
457
+ individual["display_name"],
458
+ individual.get("analysis_type").upper(),
459
+ (
460
+ CANCER_PHENOTYPE_MAP[individual.get("phenotype", 0)]
461
+ if case.get("track") == "cancer"
462
+ else PHENOTYPE_MAP[individual.get("phenotype", 0)]
463
+ ),
464
+ SEX_MAP[individual.get("sex", 0)],
465
+ individual.get("confirmed_sex") or "-",
466
+ individual.get("confirmed_parent") or "-",
467
+ individual.get("predicted_ancestry") or "-",
468
+ individual.get("tissue_type") or "-",
469
+ case["display_name"],
470
+ case["_id"],
471
+ case["analysis_date"].strftime("%Y-%m-%d %H:%M:%S"),
472
+ case["status"],
473
+ ", ".join(hpo["phenotype_id"] for hpo in case.get("phenotype_terms", [])),
474
+ case.get("is_research"),
475
+ case.get("track"),
476
+ ", ".join(
477
+ panel["panel_name"] for panel in case.get("panels") if panel.get("is_default")
478
+ ),
479
+ case.get("genome_build"),
480
+ f"{case.get('rank_model_version', '-')}/{case.get('sv_rank_model_version', '-')}",
481
+ ]
482
+ export_lines.append("\t".join(str(item) for item in export_line))
427
483
 
428
- Add all the necessary information to display the 'cases' view
484
+ file_content = "\n".join(export_lines)
429
485
 
430
- Args:
431
- store(adapter.MongoAdapter)
432
- request(flask.request) request sent by browser to the api_institutes endpoint
433
- institute_id(str): An _id of an institute
486
+ return Response(
487
+ file_content,
488
+ mimetype="text/plain",
489
+ headers={
490
+ "Content-Disposition": f"attachment;filename={institute_id}_cases_{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}.txt"
491
+ },
492
+ )
434
493
 
435
- Returns:
436
- data(dict): includes the cases, how many there are and the limit.
437
- """
494
+
495
+ def cases(store: MongoAdapter, request: request, institute_id: str) -> dict:
496
+ """Preprocess case objects for the 'cases' view."""
438
497
  data = {}
498
+
499
+ # Initialize data (institute info, filters, and case counts)
439
500
  institute_obj = institute_and_case(store, institute_id)
440
501
  data["institute"] = institute_obj
441
-
442
- name_query = request.form
443
- limit = int(request.form.get("search_limit")) if request.form.get("search_limit") else 100
444
-
445
502
  data["form"] = CaseFilterForm(request.form)
503
+ data["status_ncases"] = store.nr_cases_by_status(institute_id=institute_id)
504
+ data["nr_cases"] = sum(data["status_ncases"].values())
505
+
506
+ # Fetch Sanger unevaluated and validated cases
507
+ sanger_ordered_not_validated = get_sanger_unevaluated(store, institute_id, current_user.email)
508
+ data["sanger_unevaluated"], data["sanger_validated_by_others"] = sanger_ordered_not_validated
446
509
 
510
+ # Projection for fetching cases
447
511
  ALL_CASES_PROJECTION = {
448
512
  "analysis_date": 1,
449
513
  "assignees": 1,
450
514
  "beacon": 1,
451
515
  "case_id": 1,
452
516
  "display_name": 1,
517
+ "genome_build": 1,
453
518
  "individuals": 1,
454
519
  "is_rerun": 1,
455
520
  "is_research": 1,
456
521
  "mme_submission": 1,
457
522
  "owner": 1,
458
523
  "panels": 1,
524
+ "phenotype_terms": 1,
525
+ "rank_model_version": 1,
459
526
  "status": 1,
527
+ "sv_rank_model_version": 1,
460
528
  "track": 1,
461
529
  "vcf_files": 1,
462
530
  }
463
531
 
464
- data["status_ncases"] = store.nr_cases_by_status(institute_id=institute_id)
465
- data["nr_cases"] = sum(data["status_ncases"].values())
466
-
467
- sanger_ordered_not_validated: Tuple[Dict[str, list]] = get_sanger_unevaluated(
468
- store, institute_id, current_user.email
469
- )
470
- data["sanger_unevaluated"]: Dict[str, list] = sanger_ordered_not_validated[0]
471
- data["sanger_validated_by_others"]: Dict[str, list] = sanger_ordered_not_validated[1]
472
-
473
- # local function to add info to case obj
474
- def populate_case_obj(case_obj):
475
- analysis_types = set(ind["analysis_type"] for ind in case_obj["individuals"])
476
- if len(analysis_types) > 1:
477
- LOG.debug("Set analysis types to {'mixed'}")
478
- analysis_types = set(["mixed"])
479
-
480
- case_obj["analysis_types"] = list(analysis_types)
481
- case_obj["assignees"] = [
482
- store.user(user_email) for user_email in case_obj.get("assignees", [])
483
- ]
484
- # Check if case was re-runned
485
- last_analysis_date = case_obj.get("analysis_date", datetime.datetime.now())
486
- all_analyses_dates = set()
487
- for analysis in case_obj.get("analyses", [{"date": last_analysis_date}]):
488
- all_analyses_dates.add(analysis.get("date", last_analysis_date))
489
-
490
- case_obj["is_rerun"] = len(all_analyses_dates) > 1 or last_analysis_date > max(
491
- all_analyses_dates
492
- )
493
-
494
- case_obj["clinvar_variants"] = store.case_to_clinVars(case_obj["_id"])
495
- case_obj["display_track"] = TRACKS[case_obj.get("track", "rare")]
496
- return case_obj
497
-
532
+ # Group cases by status
498
533
  case_groups = {status: [] for status in CASE_STATUSES}
534
+ nr_cases_showall_statuses = 0
535
+ status_show_all_cases = institute_obj.get("show_all_cases_status") or ["prioritized"]
499
536
 
500
- nr_cases_showall_statuses = (
501
- 0 # Nr of cases for the case statuses where all cases should be shown
502
- )
503
- # In institute settings, retrieve all case status categories for which all cases should be displayed
504
- status_show_all_cases: List[str] = institute_obj.get("show_all_cases_status") or ["prioritized"]
537
+ # Process cases for statuses that require all cases to be shown
505
538
  for status in status_show_all_cases:
506
539
  cases_in_status = store.cases_by_status(
507
540
  institute_id=institute_id, status=status, projection=ALL_CASES_PROJECTION
508
541
  )
509
542
  cases_in_status = _sort_cases(data, request, cases_in_status)
510
543
  for case_obj in cases_in_status:
511
- populate_case_obj(case_obj)
512
- nr_cases_showall_statuses += 1
544
+ populate_case_obj(case_obj, store)
513
545
  case_groups[status].append(case_obj)
546
+ nr_cases_showall_statuses += 1
514
547
 
515
- # Retrieve cases for the remaining status categories
548
+ # Fetch additional cases based on filters
549
+ name_query = request.form
550
+ limit = int(request.form.get("search_limit")) if request.form.get("search_limit") else 100
516
551
  all_cases = store.cases(
517
552
  collaborator=institute_id,
518
553
  name_query=name_query,
@@ -525,17 +560,20 @@ def cases(store, request, institute_id):
525
560
  )
526
561
  all_cases = _sort_cases(data, request, all_cases)
527
562
 
563
+ if request.form.get("export"):
564
+ return export_case_samples(institute_id, all_cases)
565
+
566
+ # Process additional cases for the remaining statuses
528
567
  nr_cases = 0
529
568
  for case_obj in all_cases:
530
- case_status = case_obj["status"]
531
- if case_status in status_show_all_cases:
532
- continue
533
- if nr_cases == limit:
534
- break
535
- populate_case_obj(case_obj)
536
- case_groups[case_status].append(case_obj)
537
- nr_cases += 1
538
-
569
+ if case_obj["status"] not in status_show_all_cases:
570
+ if nr_cases == limit:
571
+ break
572
+ populate_case_obj(case_obj, store)
573
+ case_groups[case_obj["status"]].append(case_obj)
574
+ nr_cases += 1
575
+
576
+ # Compile the final data
539
577
  data["cases"] = [(status, case_groups[status]) for status in CASE_STATUSES]
540
578
  data["found_cases"] = nr_cases + nr_cases_showall_statuses
541
579
  data["limit"] = limit
@@ -543,8 +581,32 @@ def cases(store, request, institute_id):
543
581
  return data
544
582
 
545
583
 
584
+ def populate_case_obj(case_obj: dict, store: MongoAdapter):
585
+ """Helper function to populate additional case information."""
586
+ analysis_types = set(ind["analysis_type"] for ind in case_obj["individuals"])
587
+ if len(analysis_types) > 1:
588
+ analysis_types = set(["mixed"])
589
+ case_obj["analysis_types"] = list(analysis_types)
590
+
591
+ case_obj["assignees"] = [store.user(user_email) for user_email in case_obj.get("assignees", [])]
592
+
593
+ last_analysis_date = case_obj.get("analysis_date", datetime.datetime.now())
594
+ all_analyses_dates = {
595
+ analysis.get("date", last_analysis_date)
596
+ for analysis in case_obj.get("analyses", [{"date": last_analysis_date}])
597
+ }
598
+ case_obj["is_rerun"] = len(all_analyses_dates) > 1 or last_analysis_date > max(
599
+ all_analyses_dates
600
+ )
601
+
602
+ case_obj["clinvar_variants"] = store.case_to_clinVars(case_obj["_id"])
603
+ case_obj["display_track"] = TRACKS.get(case_obj.get("track", "rare"))
604
+
605
+
546
606
  def _get_unevaluated_variants_for_case(
547
- case_obj: dict, var_ids_list: List[str], sanger_validated_by_user_by_case: Dict[str, List[str]]
607
+ case_obj: dict,
608
+ var_ids_list: List[str],
609
+ sanger_validated_by_user_by_case: Dict[str, List[str]],
548
610
  ) -> Tuple[Dict[str, list]]:
549
611
  """Returns the variants with Sanger ordered by a user that need validation or are validated by another user."""
550
612
 
@@ -73,7 +73,8 @@ class InstituteForm(FlaskForm):
73
73
  pheno_abbrev = StringField("Abbreviation", validators=[validators.Optional()])
74
74
 
75
75
  gene_panels = NonValidatingSelectMultipleField(
76
- "Gene panels available for variants filtering", validators=[validators.Optional()]
76
+ "Gene panels available for variants filtering",
77
+ validators=[validators.Optional()],
77
78
  )
78
79
 
79
80
  gene_panels_matching = NonValidatingSelectMultipleField(
@@ -144,7 +145,8 @@ class GeneVariantFiltersForm(FlaskForm):
144
145
  variant_type = SelectMultipleField(choices=[("clinical", "clinical"), ("research", "research")])
145
146
  category = SelectMultipleField(choices=CATEGORY_CHOICES)
146
147
  hgnc_symbols = TagListField(
147
- "HGNC Symbols (comma-separated, case sensitive)", validators=[validators.InputRequired()]
148
+ "HGNC Symbols (comma-separated, case sensitive)",
149
+ validators=[validators.InputRequired()],
148
150
  )
149
151
  rank_score = IntegerField(default=15)
150
152
  phenotype_terms = TagListField("HPO terms (comma-separated)")
@@ -181,3 +183,4 @@ class CaseFilterForm(FlaskForm):
181
183
  has_rna = BooleanField("Has RNA-seq data")
182
184
  validation_ordered = BooleanField("Validation pending")
183
185
  search = SubmitField(label="Search")
186
+ export = SubmitField(label="Filter and export")
@@ -249,6 +249,12 @@
249
249
  advSearchBlock.hide();
250
250
  }
251
251
 
252
+ function StopSpinner() {
253
+ // Avoid page spinner being stuck on file download
254
+ $(window).unbind('beforeunload');
255
+ return true;
256
+ }
257
+
252
258
  advBlockCheckbox.on('click', function() {
253
259
  if($(this).is(':checked')) {
254
260
  advSearchBlock.show();
@@ -9,7 +9,7 @@
9
9
  {% endif %}
10
10
  {% endfor %}
11
11
 
12
- <form action="{{ form_action }}" method="POST" accept-charset="utf-8">
12
+ <form action="{{ form_action }}" method="POST" accept-charset="utf-8" onsubmit="return StopSpinner()">
13
13
  {{ form.hidden_tag() }}
14
14
 
15
15
  <div class="row">
@@ -19,7 +19,7 @@
19
19
  {{ form.search_institute(class="form-control") }}
20
20
  </div>
21
21
  {% endif %}
22
- <div class="col-md-3 mb-3">
22
+ <div class="col-md-2 mb-3">
23
23
  {{ form.case.label(class="form-label") }}
24
24
  {{ form.case(class="form-control", placeholder="example:18201", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
25
25
  </div>
@@ -27,9 +27,10 @@
27
27
  {{ form.search_limit.label(class="form-label") }}
28
28
  {{ form.search_limit(class="form-control") }}
29
29
  </div>
30
- <div class="btn-sm mb-2 col-md-2 mx-auto">
31
- {{ form.search(class="btn btn-lg btn-primary mt-4") }}
32
- <a href="{{ reset_action }}" class="btn btn-lg btn-secondary mt-4 text-white">Reset search</a>
30
+ <div class="btn-sm mb-2 col-md-3 mx-auto">
31
+ {{ form.search(class="btn btn-primary mt-4") }}
32
+ {{ form.export(class="btn btn-primary mt-4") }}
33
+ <a href="{{ reset_action }}" class="btn btn-secondary mt-4 text-white">Reset search</a>
33
34
  </div>
34
35
  <div class="col-md-2 mb-3 form-check align-self-center mt-3 ms-3" data-bs-toggle="tooltip" title="Using multiple search criteria will narrow down your results as returned cases will contain all your searched conditions.">
35
36
  {{ form.advanced_search(class="form-check-input") }}
@@ -76,15 +76,15 @@
76
76
  </td> <!-- end of ID col-->
77
77
 
78
78
  <td> <!-- HGVS Description col -->
79
- {% set hgvs_c = (tx.coding_sequence_name or '')|truncate(20, True) %}
79
+ {% set hgvs_c = (tx.coding_sequence_name or '')|truncate(30, True) %}
80
80
  {% if variant.chromosome in ["MT","M"] %}
81
81
  {% set mt_notation = "m." ~ variant.position ~ variant.reference ~ ">" ~ variant.alternative %}
82
- {{ mt_notation|truncate(20,True) }} <span class="text-muted">({{ hgvs_c }})</span>
82
+ {{ mt_notation|truncate(30,True) }} <span class="text-muted">({{ hgvs_c }})</span>
83
83
  {% else %}
84
84
  {{ hgvs_c }}
85
85
  {% endif %}
86
86
  <span class="text-muted float-end">
87
- {{ (tx.protein_sequence_name or '')|url_decode }}
87
+ {{ (tx.protein_sequence_name or '')|url_decode|truncate(30, True) }}
88
88
  </span>
89
89
  </td> <!-- end of HGVS Description col -->
90
90
 
@@ -1,6 +1,6 @@
1
1
  {% extends "layout.html" %}
2
2
  {% from "variants/utils.html" import cancer_sv_filters,cell_rank, pagination_footer, pagination_hidden_div, filter_form_footer, filter_script_main, update_stash_filter_button_status, dismiss_variants_block, callers_cell %}
3
- {% from "variants/components.html" import external_scripts, external_stylesheets, frequency_cell_general, observed_cell_general, variant_gene_symbols_cell, variant_funct_anno_cell %}
3
+ {% from "variants/components.html" import allele_cell, external_scripts, external_stylesheets, frequency_cell_general, observed_cell_general, variant_gene_symbols_cell, variant_funct_anno_cell %}
4
4
 
5
5
  {% block title %}
6
6
  {{ super() }} - {{ institute.display_name }} - {{ case.display_name }} - SV variants
@@ -111,8 +111,8 @@
111
111
  {{ variant.sub_category|upper }}
112
112
  </td>
113
113
  <td>{{ variant.chromosome if variant.chromosome == variant.end_chrom else variant.chromosome+'-'+variant.end_chrom }}</td>
114
- <td class="col-2"><span style="white-space: nowrap;">{{ variant.position|human_longint|safe }}</span></td>
115
- <td class="col-2"><span style="white-space: nowrap;">{{ 'inf' if variant.end == 100000000000 else variant.end|human_longint|safe }}</span></td>
114
+ <td class="col-2">{% if variant.chromosome != variant.end_chrom %}<span class="text-body"></span><b>{{ variant.chromosome }}</b>:</span>{% endif %}<span class="text-body" style="white-space: nowrap;">{{ variant.position|human_longint|safe }}</span></td>
115
+ <td class="col-2">{% if variant.chromosome != variant.end_chrom %}<span class="text-body"><b>{{ variant.end_chrom }}</b>:</span>{% endif %}<span style="white-space: nowrap;">{{ 'inf' if variant.end == 100000000000 else variant.end|human_longint|safe }}</span></td>
116
116
  <td class="col-2"><span style="white-space: nowrap;">{{ '-' if variant.length == 100000000000 else variant.length|human_longint|safe }}</span></td>
117
117
  <td class="text-end">{{ frequency_cell_general(variant) }}</td>
118
118
  <td>{{observed_cell_general(variant)}}</td>
@@ -122,25 +122,11 @@
122
122
  <td>
123
123
  {{ variant_funct_anno_cell(variant) }}
124
124
  </td>
125
- <td>{{ allele_cell(variant.tumor or {}) }}{% if variant.somatic_score %}<small class="text-muted">[{{ variant.somatic_score }}]</small>{% endif %}</td>
125
+ <td>{{ allele_cell(variant.tumor or {}) }}{% if variant.somatic_score %}<small class="text-body" data-bs-toggle="tooltip" data-bs-placement="top" title="SV caller (Manta) somatic score">[{{ variant.somatic_score }}]</small>{% endif %}</td>
126
126
  <td>{{ allele_cell(variant.normal or {}) }}</td>
127
127
  </tr>
128
128
  {% endmacro %}
129
129
 
130
- {% macro allele_cell(allele) %}
131
- {% if 'alt_freq' in allele %}
132
- {% if allele.alt_freq == -1 %}
133
- <span class="text-muted">.</span>
134
- {% else %}
135
- {{ allele.alt_freq|round(4) }}
136
- {% endif %}
137
- <br>
138
- <small class="text-muted">{% if allele.alt_depth >= 0 %}{{ allele.alt_depth }}{% else %}.{% endif %}|{% if allele.ref_depth >= 0 %}{{ allele.ref_depth }}{% else %}.{% endif %}</small>
139
- {% else %}
140
- <span class="text-muted">N/A</span>
141
- {% endif %}
142
- {% endmacro %}
143
-
144
130
 
145
131
  {% block scripts %}
146
132
  {{ super() }}
@@ -1,6 +1,6 @@
1
1
  {% extends "layout.html" %}
2
2
 
3
- {% from "variants/components.html" import external_scripts, external_stylesheets, gene_cell, frequency_cell_general, observed_cell_general, variant_funct_anno_cell %}
3
+ {% from "variants/components.html" import allele_cell, external_scripts, external_stylesheets, gene_cell, frequency_cell_general, observed_cell_general, variant_funct_anno_cell %}
4
4
  {% from "variants/utils.html" import cancer_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status, callers_cell %}
5
5
  {% from "variants/indicators.html" import pin_indicator, causative_badge, clinical_assessments_badge, comments_badge, dismissals_badge, evaluations_badge, ccv_evaluations_badge, group_assessments_badge, matching_manual_rank, other_tiered_variants, research_assessments_badge %}
6
6
 
@@ -175,17 +175,7 @@
175
175
  {% endmacro %}
176
176
 
177
177
  {% macro position_cell(variant) %}
178
- {{ variant.chromosome }}<span class="text-muted">:{{ variant.position }}</span>
179
- {% endmacro %}
180
-
181
- {% macro allele_cell(allele) %}
182
- {% if 'alt_freq' in allele %}
183
- {{ allele.alt_freq|round(4) }}
184
- <br>
185
- <small class="text-muted">{{ allele.alt_depth }} | {{ allele.ref_depth }}</small>
186
- {% else %}
187
- <span class="text-muted">N/A</span>
188
- {% endif %}
178
+ <span class="text-body"><b>{{ variant.chromosome }}</b>:{{ variant.position }}</span>
189
179
  {% endmacro %}
190
180
 
191
181
  {% block scripts %}
@@ -211,7 +211,7 @@
211
211
  title="Overlapping gene panels"
212
212
  data-bs-content="{% for panel in matching_panels %}
213
213
  {{ panel.panel_name|safe }}<br>
214
- {% endfor %}"
214
+ {% else %} No ovelapping gene panels {% endfor %}"
215
215
  >{{ panel_count }}</a>
216
216
  {% endmacro %}
217
217
 
@@ -403,3 +403,17 @@
403
403
  <script src="https://cdn.datatables.net/1.12.0/js/jquery.dataTables.min.js" integrity="sha512-fu0WiDG5xqtX2iWk7cp17Q9so54SC+5lk/z/glzwlKFdEOwGG6piUseP2Sik9hlvlmyOJ0lKXRSuv1ltdVk9Jg==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
404
404
  <script src="https://cdn.datatables.net/1.12.0/js/dataTables.bootstrap5.min.js" integrity="sha512-nfoMMJ2SPcUdaoGdaRVA1XZpBVyDGhKQ/DCedW2k93MTRphPVXgaDoYV1M/AJQLCiw/cl2Nbf9pbISGqIEQRmQ==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
405
405
  {% endmacro %}
406
+
407
+ {% macro allele_cell(allele) %}
408
+ {% if 'alt_freq' in allele %}
409
+ {% if allele.alt_freq == -1 %}
410
+ <span class="text-body">.</span>
411
+ {% else %}
412
+ <span class="text-body"><b>{{ allele.alt_freq|round(4) }}</b></span>
413
+ {% endif %}
414
+ <br>
415
+ <small class="text-body">{% if 'alt_depth' in allele and allele.alt_depth >= 0 %}{{ allele.alt_depth }}{% else %}.{% endif %}|{% if 'ref_depth' in allele and allele.ref_depth >= 0 %}{{ allele.ref_depth }}{% else %}.{% endif %}</small>
416
+ {% else %}
417
+ <span class="text-body">N/A</span>
418
+ {% endif %}
419
+ {% endmacro %}
@@ -108,8 +108,8 @@ onsubmit="return validateForm()">
108
108
  </td>
109
109
  <td>{{ variant.sub_category|upper }}</td>
110
110
  <td>{{ variant.chromosome if variant.chromosome == variant.end_chrom else variant.chromosome+'-'+variant.end_chrom }}</td>
111
- <td><span style="white-space: nowrap;">{{ variant.position|human_longint|safe }}</span></td>
112
- <td><span style="white-space: nowrap;">{{ 'inf' if variant.end == 100000000000 else variant.end|human_longint|safe }}</span></td>
111
+ <td>{% if variant.chromosome != variant.end_chrom %}<span class="text-body"></span><b>{{ variant.chromosome }}</b>:</span>{% endif %}<span style="white-space: nowrap;">{{ variant.position|human_longint|safe }}</span></td>
112
+ <td>{% if variant.chromosome != variant.end_chrom %}<span class="text-body"><b>{{ variant.end_chrom }}</b>:</span>{% endif %}<span style="white-space: nowrap;">{{ 'inf' if variant.end == 100000000000 else variant.end|human_longint|safe }}</span></td>
113
113
  <td class="text-end"><span style="white-space: nowrap;">{{ '-' if variant.length == 100000000000 else variant.length|human_longint|safe}}</span></td>
114
114
  <td>
115
115
  {{ frequency_cell_general(variant) }}
scout/server/links.py CHANGED
@@ -714,7 +714,7 @@ def ucsc_link(variant_obj, build=None):
714
714
  )
715
715
  if build == 38:
716
716
  url_template = (
717
- "http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg20&"
717
+ "http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg38&"
718
718
  "position=chr{this[chromosome]}:{this[position]}"
719
719
  "-{this[position]}&dgv=pack&knownGene=pack&omimGene=pack"
720
720
  )
scout/utils/acmg.py CHANGED
@@ -1,6 +1,5 @@
1
1
  # coding=UTF-8
2
2
 
3
-
4
3
  from typing import Optional
5
4
 
6
5
  from scout.constants import ACMG_COMPLETE_MAP
scout/utils/md5.py CHANGED
File without changes
@@ -1,62 +1,76 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: scout-browser
3
- Version: 4.93.1
4
- Summary: Clinical DNA variant visualizer and browser.
5
- Home-page: https://github.com/Clinical-Genomics/scout
6
- Author: Måns Magnusson
7
- Author-email: mans.magnusson@scilifelab.se
3
+ Version: 4.94
4
+ Summary: Clinical DNA variant visualizer and browser
5
+ Project-URL: Repository, https://github.com/Clinical-Genomics/scout
6
+ Project-URL: Changelog, https://github.com/Clinical-Genomics/scout/blob/main/CHANGELOG.md
7
+ Project-URL: Documentation, https://clinical-genomics.github.io/scout/
8
+ Project-URL: Bug Tracker, https://github.com/Clinical-Genomics/scout/issues
9
+ Project-URL: Issues, https://github.com/Clinical-Genomics/scout/issues
10
+ Author-email: Chiara Rasi <chiara.rasi@scilifelab.se>, Daniel Nilsson <daniel.nilsson@ki.se>, Robin Andeer <robin.andeer@gmail.com>, Mans Magnuson <monsunas@gmail.com>
11
+ License: MIT License
12
+ License-File: LICENSE
13
+ Keywords: acmg,beacon,cancer,clingen-cgc-vicc,clinvar,cnv,coverage,dna,fusion,genome visualisation,lrs,matchmaker exchange,ogm,outliers,panel,panelapp,phenopacket,rna,sma,snv,str,sv,variant ranking,variant triage,variants,vcf,wes,wgs,wts
14
+ Classifier: Development Status :: 5 - Production/Stable
8
15
  Classifier: Environment :: Web Environment
9
16
  Classifier: Intended Audience :: Developers
10
- Classifier: License :: OSI Approved :: BSD License
11
- Classifier: Operating System :: OS Independent
17
+ Classifier: Intended Audience :: Science/Research
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: MacOS :: MacOS X
20
+ Classifier: Operating System :: Unix
12
21
  Classifier: Programming Language :: Python
22
+ Classifier: Programming Language :: Python :: 3.9
23
+ Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
27
+ Classifier: Programming Language :: Python :: Implementation :: CPython
13
28
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
14
- Classifier: Topic :: Software Development :: Libraries
15
- Classifier: Programming Language :: Python :: 3.6
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: werkzeug
19
- Requires-Dist: Flask>=2.0
20
- Requires-Dist: Flask-Bootstrap
21
- Requires-Dist: Flask-CORS
22
- Requires-Dist: path.py
23
- Requires-Dist: markdown
24
- Requires-Dist: WTForms
25
- Requires-Dist: Flask-WTF
26
- Requires-Dist: Flask-Mail
29
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
30
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
31
+ Requires-Python: >=3.9
32
+ Requires-Dist: anytree
33
+ Requires-Dist: authlib
34
+ Requires-Dist: cairosvg
35
+ Requires-Dist: click
27
36
  Requires-Dist: coloredlogs
28
- Requires-Dist: query_phenomizer
29
- Requires-Dist: Flask-Babel>=3
37
+ Requires-Dist: configobj
38
+ Requires-Dist: cryptography
39
+ Requires-Dist: cyvcf2
40
+ Requires-Dist: defusedxml
41
+ Requires-Dist: flask-babel>=3
42
+ Requires-Dist: flask-bootstrap
43
+ Requires-Dist: flask-cors
44
+ Requires-Dist: flask-ldapconn
45
+ Requires-Dist: flask-login
46
+ Requires-Dist: flask-mail
47
+ Requires-Dist: flask-wtf
48
+ Requires-Dist: flask>=2.0
49
+ Requires-Dist: importlib-resources
50
+ Requires-Dist: intervaltree==3.0.2
30
51
  Requires-Dist: livereload>=2.7
31
- Requires-Dist: tornado>=6.4.1
32
- Requires-Dist: python-dateutil
33
- Requires-Dist: pymongo
52
+ Requires-Dist: markdown
53
+ Requires-Dist: path-py
34
54
  Requires-Dist: pathlib
35
55
  Requires-Dist: pdfkit
56
+ Requires-Dist: ped-parser
36
57
  Requires-Dist: phenopackets
37
- Requires-Dist: xlsxwriter
38
- Requires-Dist: click
39
- Requires-Dist: cryptography
40
- Requires-Dist: defusedxml
41
- Requires-Dist: svglib
42
- Requires-Dist: cairosvg
43
- Requires-Dist: importlib_resources
44
- Requires-Dist: authlib
45
- Requires-Dist: flask_login
46
- Requires-Dist: flask-ldapconn
47
- Requires-Dist: cyvcf2
48
- Requires-Dist: configobj
49
- Requires-Dist: ped_parser
50
58
  Requires-Dist: pydantic>=2
51
- Requires-Dist: PyYaml>=5.1
52
- Requires-Dist: intervaltree==3.0.2
53
- Requires-Dist: anytree
59
+ Requires-Dist: pymongo
54
60
  Requires-Dist: python-dateutil
61
+ Requires-Dist: pyyaml>=5.1
62
+ Requires-Dist: query-phenomizer
63
+ Requires-Dist: svglib
55
64
  Requires-Dist: tabulate
65
+ Requires-Dist: tornado>=6.4.1
66
+ Requires-Dist: werkzeug
67
+ Requires-Dist: wtforms
68
+ Requires-Dist: xlsxwriter
56
69
  Provides-Extra: coverage
57
- Requires-Dist: chanjo-report; extra == "coverage"
58
- Requires-Dist: pymysql; extra == "coverage"
59
-
70
+ Requires-Dist: chanjo-report; extra == 'coverage'
71
+ Requires-Dist: pymysql; extra == 'coverage'
72
+ Requires-Dist: python-dateutil; extra == 'coverage'
73
+ Description-Content-Type: text/markdown
60
74
 
61
75
  <p align="center">
62
76
  <a href="https://Clinical-Genomics.github.io/scout/">
@@ -127,6 +141,13 @@ cd scout
127
141
  pip install --editable .
128
142
  ```
129
143
 
144
+ Scout is configured to use `uv` if you like; either run, install, install as a tool or
145
+ ```
146
+ uv sync --frozen
147
+ uv run scout
148
+ ```
149
+
150
+
130
151
  Scout PDF reports are created using [Flask-WeasyPrint](https://pythonhosted.org/Flask-WeasyPrint/). This library requires external dependencies which need be installed separately (namely Cairo and Pango). See platform-specific instructions for Linux, macOS and Windows available on the WeasyPrint installation [pages](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation).
131
152
 
132
153
  <b>NB</b>: in order to convert HTML reports into PDF reports, we have recently switched from the WeasyPrint lib to [python-pdfkit](https://github.com/JazzCore/python-pdfkit). For this reason, when upgrading to a Scout version >4.47, you need to install an additional [wkhtmltopdf system library](https://wkhtmltopdf.org/).
@@ -1,5 +1,4 @@
1
1
  scout/__init__.py,sha256=Z4liXvmEcLkC67ElsWvYHfemPKdWgWI5O6MB6XlDM8M,232
2
- scout/__version__.py,sha256=QvBUfmeaghMXRYxQv7CLutqQ8EWHp4OXnrEJdYtEKgU,23
3
2
  scout/adapter/__init__.py,sha256=-iX_hx2NI1EMAqX0pMd5_90Nnd9uvIMxv9EbefYBzsc,86
4
3
  scout/adapter/client.py,sha256=IuajRsEwTG41ZP14X09Q1Cj94zIgmIvUtlXfcAFn0EA,1513
5
4
  scout/adapter/mongo/__init__.py,sha256=NdHYCUXWUAuX5cUS3-6HCws2hW9uoGep8i0SC-oJd3k,31
@@ -23,11 +22,11 @@ scout/adapter/mongo/matchmaker.py,sha256=amTvFOlUbrt1RmecY_8hPY6bO3j79lc2UbmzlCQ
23
22
  scout/adapter/mongo/omics_variant.py,sha256=VdkNG_l6vcr3UF25EixEfvIdMGkSGeNaMjLRhQHS_lc,7521
24
23
  scout/adapter/mongo/panel.py,sha256=d1oMEYAiDGybLmlbbf3-fQdCdU0-T7x2XdIUwZGthYk,20031
25
24
  scout/adapter/mongo/phenomodel.py,sha256=cypSS8YRzu98Bf1kC0orfrLNn_ZQSYCK8e4mNR5PaPY,2572
26
- scout/adapter/mongo/query.py,sha256=Zk5BV9GZYVlGoSRRjP88TpvB4teVpvHgHnSDV0W-kS0,33157
25
+ scout/adapter/mongo/query.py,sha256=rF4WjVaH0x1vupJkYMj0c_p8TQbApmZ1t8LgH8MsZwA,34777
27
26
  scout/adapter/mongo/rank_model.py,sha256=wvDNI4OLf-f7ZYZ_Q_6qi0myxOJCNNJR_-2-_0u2K2M,5712
28
27
  scout/adapter/mongo/transcript.py,sha256=3U1qtyxZ1DnrzdBJm_emJXISMYFKJuX_zAKynUt8NMY,7417
29
28
  scout/adapter/mongo/user.py,sha256=QoQnRWqi_dV3ZwGzBonEU6Ap9TgMDf2x_C5UIqG0pa0,2569
30
- scout/adapter/mongo/variant.py,sha256=4kGyJn6kitOQ68-zp5bkT6koUP7WbZ3SLl2NXll6dOE,34928
29
+ scout/adapter/mongo/variant.py,sha256=Mwu-2N2ziXSR4lQEFXegbftH2nTskAWL_vpAdeu-i2M,34926
31
30
  scout/adapter/mongo/variant_events.py,sha256=yNnMM58P1LbYFw73rWzti6l2zWa_G93ozUTbVkcz4_I,33475
32
31
  scout/adapter/mongo/variant_loader.py,sha256=C05StRMFVChopcolPYXFIbh_S-FhYHu4NvBCpiBLOeE,28327
33
32
  scout/build/__init__.py,sha256=GUWEaXTpwsJil_KtrXBQJpZIwCQFSgo8NlK49R9IkA8,521
@@ -443,11 +442,12 @@ scout/parse/variant/variant.py,sha256=nf_0q2J7TryWhB-Oz697HYSDJTPNmkXnPHmKtjhHQU
443
442
  scout/resources/__init__.py,sha256=i9UVG651subgmOZ-Gm-JKPsXBqr5-50QUDfSpbwmwIw,443
444
443
  scout/resources/cytoBand_hg19.txt.gz,sha256=pheUD5b9NlVuvwwnbKwDc2FG80Yg70gvPxVXP-Sqd3k,6147
445
444
  scout/resources/cytoBand_hg38.txt.gz,sha256=sqSVmvPlktQ-0hTiTapJM-5UgyV6xDoYZuTF0kPuETs,6105
445
+ scout/resources/custom_igv_tracks/mane.bb,sha256=flEexDxHmbX7IwwrMM0P5DWOxcSCjmHxGNlyg_WqElQ,3519360
446
446
  scout/server/__init__.py,sha256=iwhKnzeBJLKxpRVjvzwiRE63_zNpIBfaKLITauVph-0,24
447
447
  scout/server/app.py,sha256=70YdN9eWL1ySEYkuAHKOk_WA4Fm5dcIwtIZEcEf-xA0,10836
448
448
  scout/server/auto.py,sha256=8B6GwfOaUChkTLuLgYQDFMWlxGuHkEegF1argH2G2Ik,401
449
449
  scout/server/config.py,sha256=BFx6ix8D3KM90FqmaHLxv_feudO2kN2CkX1UWBGTjXE,5923
450
- scout/server/links.py,sha256=UdYqYyRHh7v1gGtP-F3UYBdT3-49ySqiv9VkCnHX5ZU,28115
450
+ scout/server/links.py,sha256=f2h_zUFt4Tm_383ZmdIGsA6mUVQbpQmJ199ZTzUWemE,28115
451
451
  scout/server/utils.py,sha256=d1fGdSwo-XjNHVjHd-uT3mGmUJ8yYB6weT7VgHo9nxM,12592
452
452
  scout/server/blueprints/__init__.py,sha256=iwhKnzeBJLKxpRVjvzwiRE63_zNpIBfaKLITauVph-0,24
453
453
  scout/server/blueprints/alignviewers/__init__.py,sha256=XMVed2ekVUAvroqENDHSr2pwM8otqau8ZA-rbH1T2U8,59
@@ -460,7 +460,7 @@ scout/server/blueprints/alignviewers/templates/alignviewers/utils.html,sha256=x2
460
460
  scout/server/blueprints/api/__init__.py,sha256=HR6HjS7ot1K_8Lt5eQdNT154z_FCdHGSigy8r2LpNCI,26
461
461
  scout/server/blueprints/api/views.py,sha256=pbl78wfhrm1T8JmiJDYF3BbTbfFrlF-hQRbuv2GWI0s,3729
462
462
  scout/server/blueprints/cases/__init__.py,sha256=_c17kPFITFYcIVphF4V9bf0PZBer8bU3rtVWQnljKDU,52
463
- scout/server/blueprints/cases/controllers.py,sha256=gDnjRYzc-z4SZro0Q7q8AQT0muyEUIfOY16hMaT-ZlE,53638
463
+ scout/server/blueprints/cases/controllers.py,sha256=Nn2MrlqAHzDfm8RSkjrWpeCD4W7DoPp_8Gm8Eu4l44Q,54362
464
464
  scout/server/blueprints/cases/views.py,sha256=NCBYZl56jrdmwip4-UXq0-1oH9Y0EddTVDXdCVpmCws,43141
465
465
  scout/server/blueprints/cases/static/case_images.js,sha256=pb_gG7DNQc-1lADqSII8YvjBwmHyeQWYVtuu2jyrTlU,14997
466
466
  scout/server/blueprints/cases/static/case_styles.css,sha256=2Pgc8pFf9DR5HM1sTdAjaRWhjL-bK5bsQnLpH54HZak,541
@@ -468,7 +468,7 @@ scout/server/blueprints/cases/static/edit_pedigree.js,sha256=ntC5fie7SsOYJau8qkk
468
468
  scout/server/blueprints/cases/static/madeline.js,sha256=KHxKMBVlYVJODNu5QkY9hhsGkDJNoaCoCZZ0DRu0YN0,1175
469
469
  scout/server/blueprints/cases/templates/cases/case.html,sha256=9Fcn-OFA1yIHchD0PjDs7-Y8I7Qqr8o6cavN0eOi5rI,37183
470
470
  scout/server/blueprints/cases/templates/cases/case_bionano.html,sha256=PLoRv7hDJcHwxhi-0hC9fQSZc7V_aUYHBhhQqcn_2G8,5946
471
- scout/server/blueprints/cases/templates/cases/case_report.html,sha256=ZhxBrEXq1jmjqeJPGgCRtN-dVo10d5SWK7GAIjbO80A,65341
471
+ scout/server/blueprints/cases/templates/cases/case_report.html,sha256=BFEJdsIgPCBQdZUqi7XyXgh_j0kMaYbxfVvrzh1wX8w,65839
472
472
  scout/server/blueprints/cases/templates/cases/case_sma.html,sha256=99rmRyDXFXjooB52TI5_ebJKv1ZAJbVXEw4deHfSVMA,7285
473
473
  scout/server/blueprints/cases/templates/cases/case_tabular_view.html,sha256=ko-LDUKmIoTazMZ2nFWvPEZsgObU07RwqIkDYFjokoY,4317
474
474
  scout/server/blueprints/cases/templates/cases/chanjo2_form.html,sha256=5Wmk7DM8LI3MynqzxeTzAr_EoEBwVVo31djcI5ZlTdo,2164
@@ -482,7 +482,7 @@ scout/server/blueprints/cases/templates/cases/matchmaker.html,sha256=kPYnmcZJ1gm
482
482
  scout/server/blueprints/cases/templates/cases/phenotype.html,sha256=7vz5XPUExD6vc-wdijLKnPzaOFm9mQxOZ_ITAL3y7U8,16420
483
483
  scout/server/blueprints/cases/templates/cases/utils.html,sha256=gHcxf3DOGcJJZMfjKY4NgvU_tjta5OnyWrKZy9pfGyY,35981
484
484
  scout/server/blueprints/clinvar/__init__.py,sha256=BV3aH2AbiA2WWrUEMbGd0H9MefFd2eTsRE9ShywbzpI,30
485
- scout/server/blueprints/clinvar/controllers.py,sha256=EZv6cwmg2pYwLFq5SPskrARrjjf3Az7GN23lcZn52vg,24254
485
+ scout/server/blueprints/clinvar/controllers.py,sha256=lCrVlokjnSURvsYkm6sTMxrZOL1Xj3VQAWbYr1o_mEY,24251
486
486
  scout/server/blueprints/clinvar/form.py,sha256=2h42YJzaOtsdEglxqt7F1i2ncjSU_IHNt-m4QOJytK4,5148
487
487
  scout/server/blueprints/clinvar/views.py,sha256=hPyTJA7Qy6QOL-ENSG1N0atnQXuYCb-iiO8fuNDjJns,6036
488
488
  scout/server/blueprints/clinvar/static/form_style.css,sha256=Tro2w0Su9st2ZRpt8PjF7qXYet-0n6Eyol4oh94JafU,4073
@@ -508,14 +508,14 @@ scout/server/blueprints/genes/templates/genes/gene.html,sha256=a--c-IjzNKN_JW7z9
508
508
  scout/server/blueprints/genes/templates/genes/genes.html,sha256=x6jXKijvuEX9GvIedHW4O_BerYXiWEmAdYT0HTiGnhI,1130
509
509
  scout/server/blueprints/genes/templates/genes/layout.html,sha256=cZMLe2cInq24z0sZpPxDm_kEiAe5VQbOR-8XWn6wlvI,1601
510
510
  scout/server/blueprints/institutes/__init__.py,sha256=kGSyYrBC1QaEQBDdoMkMLfaowV7roaV5DowICi_0RSQ,65
511
- scout/server/blueprints/institutes/controllers.py,sha256=Fv57oJ96VsOaYGtQb-0m96bb7ospNGRBPiVX9QsMJZw,31720
512
- scout/server/blueprints/institutes/forms.py,sha256=gjVNdKt7hIrNk_JOwCDRuOXiOrE9kS41VZQuTkkmkk8,6615
511
+ scout/server/blueprints/institutes/controllers.py,sha256=qu8bonz74Ff-nTevOy4BaLhYrXbgD4rZEc81NvBkK_w,34055
512
+ scout/server/blueprints/institutes/forms.py,sha256=7ixKPYewR24lnM9vwGNS9EcBHR6072kPRuvE0jk6acE,6685
513
513
  scout/server/blueprints/institutes/views.py,sha256=PSpf82kW0EKZc24CPhBhuE-wXXGyIz-R6IJu7IM0IrM,9052
514
514
  scout/server/blueprints/institutes/static/form_scripts.js,sha256=8Sn7omeXeeUbiXJPfT0Qb4CaBJ_KtZZWw7N2he7EDx0,747
515
515
  scout/server/blueprints/institutes/static/select2_darktheme.css,sha256=Nq_va597W_e5y-52B4ClwSJzACq0WFbPU8SIp3DtKIo,1818
516
516
  scout/server/blueprints/institutes/static/timeline_styles.css,sha256=Vq48ffIidpQmDQhAzfW47O9mGQZEWdlbMtOE8AK-ZEU,2122
517
517
  scout/server/blueprints/institutes/static/variants_list_scripts.js,sha256=gO_ZuJC2jj7Y9x6CEVEVhN1oIE4bIWrTY0_zVcsbRbY,236
518
- scout/server/blueprints/institutes/templates/overview/cases.html,sha256=7-9nUomP6iaqXGtf6GemVECYMG2nD-h64RKTAmLkof0,12758
518
+ scout/server/blueprints/institutes/templates/overview/cases.html,sha256=GOt1Y2PZLVigL3wqi5NAJFqIjQHgaocrbJzFcCUzOfY,12900
519
519
  scout/server/blueprints/institutes/templates/overview/causatives.html,sha256=192pgqUSXQF4qhC933qsYtTwvmlqDctxwHcArERkHc8,1619
520
520
  scout/server/blueprints/institutes/templates/overview/filters.html,sha256=Dzu5YicmdjW_zGqbCUrb8TMbSUuRaG47itTMwwX7Bm8,3102
521
521
  scout/server/blueprints/institutes/templates/overview/gene_variants.html,sha256=EyhBZEdMZQBaJWR_FDeabuWXRSd4Xcqh0teaxVyNvAE,8442
@@ -523,7 +523,7 @@ scout/server/blueprints/institutes/templates/overview/institute_settings.html,sh
523
523
  scout/server/blueprints/institutes/templates/overview/institute_sidebar.html,sha256=vQQunXH2Cr8-z2Kc7uTCzsSqIX14Xld44NWqdhpw-Hs,4304
524
524
  scout/server/blueprints/institutes/templates/overview/timeline.html,sha256=upTv7v4sUwxGfJDSyk8F5zPb61dedG66zgfvh_SMR4M,2289
525
525
  scout/server/blueprints/institutes/templates/overview/users.html,sha256=6dkbDBNLw2b97M2Pd8JImpPoT3FLTcl84oBN3inLJRg,1134
526
- scout/server/blueprints/institutes/templates/overview/utils.html,sha256=Z66aFcrEXzgo0FyPc1tfFA_VYJdfG58QWLgJuXG2l1Y,33123
526
+ scout/server/blueprints/institutes/templates/overview/utils.html,sha256=9W60kUmIRPsu_JX0kDMg0hkz8CUY8vn2MdoegRAMqFc,33199
527
527
  scout/server/blueprints/institutes/templates/overview/verified.html,sha256=qxZ8mIPX00ZXIy_5eLO2QmLrjJZrhw97kdXBZxpRj38,3363
528
528
  scout/server/blueprints/login/__init__.py,sha256=uDzQjNNrMloZ4-i-1cEW7d59FZ--Xy89b-Xn5SvyC1U,52
529
529
  scout/server/blueprints/login/controllers.py,sha256=bg0me00OvwYJiTm4dZnbl8eyRRRDquBMs5U7_YV4xWI,1883
@@ -618,7 +618,7 @@ scout/server/blueprints/variant/templates/variant/rank_score_results.html,sha256
618
618
  scout/server/blueprints/variant/templates/variant/sanger.html,sha256=0kVnscTw3KUwjR4QOEuNJMOK9eADGEn9qGNtGx2ST7Y,4507
619
619
  scout/server/blueprints/variant/templates/variant/str-variant-reviewer.html,sha256=p0nWqo71GJ8ACLEzXuZbJAzDXoCHZLvH6f7yLi3JslI,2208
620
620
  scout/server/blueprints/variant/templates/variant/sv-variant.html,sha256=8199JLlkuNOLI88hSwgtPxKjk9SDyhNS-WJR1S7mnFs,16978
621
- scout/server/blueprints/variant/templates/variant/tx_overview.html,sha256=turyCoOCCd_N80FakxXfIl7q_WViysz1fwx3j312_Lg,6737
621
+ scout/server/blueprints/variant/templates/variant/tx_overview.html,sha256=EXLBDh_SgNVEO7MPMjlmfO6MDhtDcj0JyvZdRgxbwSM,6756
622
622
  scout/server/blueprints/variant/templates/variant/utils.html,sha256=lF-w3400plEn0gVpHG-9k7N9L_p1U-PFGitcT_YlZw8,25414
623
623
  scout/server/blueprints/variant/templates/variant/variant.html,sha256=JA0jI1ITvKamJY88U6-uBXYrHRZa3H2oyBUROtX9TKw,17874
624
624
  scout/server/blueprints/variant/templates/variant/variant_details.html,sha256=KAdrLo8UTxO1Nr5ggfdn663E7xMq1rK0A2PrKXCJGjM,21049
@@ -628,14 +628,14 @@ scout/server/blueprints/variants/forms.py,sha256=w4Woeek6gpZfPM_9e1E_n-gpVvNxXPU
628
628
  scout/server/blueprints/variants/utils.py,sha256=ifFBoyigx0A5KPE4iz9NSpyuUeF1bElrb4ohQLD2GlU,919
629
629
  scout/server/blueprints/variants/views.py,sha256=skUGpZyRDzW4BEDIqWKajHBLF3qBUSP-UYSZYxbv3l8,28895
630
630
  scout/server/blueprints/variants/static/form_scripts.js,sha256=o3GCboaesA9Sm1HgejS_yQwt0I-NTkvcl56jiBdLqZs,8319
631
- scout/server/blueprints/variants/templates/variants/cancer-sv-variants.html,sha256=rLASILYRFwTIA3S0WtfN3QS7JbL_VvXKYJOoa-1yQJQ,7389
632
- scout/server/blueprints/variants/templates/variants/cancer-variants.html,sha256=N2AM-uqgRTwHo5Ep0GNNzm_kqG0hTnYTdWU2Ox2Ocs4,9870
633
- scout/server/blueprints/variants/templates/variants/components.html,sha256=b2LKVE5vkKy8bgHbScCnGyL9xQzMuQYeBpOl5h8IR_E,16820
631
+ scout/server/blueprints/variants/templates/variants/cancer-sv-variants.html,sha256=QlGmUEdUQ7gCq7tMMUFAlGqqC9jFB78Q0h6tSp3QUas,7271
632
+ scout/server/blueprints/variants/templates/variants/cancer-variants.html,sha256=LnybOzNu4RkbighdIcV6okJwQWiO5ixOhTev_-QaQII,9611
633
+ scout/server/blueprints/variants/templates/variants/components.html,sha256=PGtcPGVbd3gcdG_6ozA66LDo3ZJ8VoehaDMUZjwyA5Q,17431
634
634
  scout/server/blueprints/variants/templates/variants/fusion-variants.html,sha256=XGaLgWobzeFHwyQLXr_Yq9THssf8tGU91VbFKdGOFBg,4801
635
635
  scout/server/blueprints/variants/templates/variants/indicators.html,sha256=5aSXytiWvaU87cqcR9GcB7U5jP8t-PVnor8FpKLl_k0,4847
636
636
  scout/server/blueprints/variants/templates/variants/mei-variants.html,sha256=2Tb0vfzM--kJa5mVbT7L32h4E8nKYRSb245g6O5JIUU,5860
637
637
  scout/server/blueprints/variants/templates/variants/str-variants.html,sha256=bjjeyoEO1aScqIDLtCh3xexZfvf1906yusPGJTyeNLU,10644
638
- scout/server/blueprints/variants/templates/variants/sv-variants.html,sha256=V_3hTZXmI_VEcQ6DpxVrTYnM3cBDGWtPri8cDVIsR9A,6153
638
+ scout/server/blueprints/variants/templates/variants/sv-variants.html,sha256=fRDnB9wXago2OB2mthp0rD_Es4kxnj9YKRlOKCGVW3s,6407
639
639
  scout/server/blueprints/variants/templates/variants/utils.html,sha256=MNDc2JwumU9ukISizSAraxIY06rHXR2Ze32RNnDEBI4,50042
640
640
  scout/server/blueprints/variants/templates/variants/variants.html,sha256=KnCcsA1VaRdsbRCCw9Rg0TJTUvq03X9S24QgCqVToUk,9328
641
641
  scout/server/extensions/__init__.py,sha256=s6qkGSFNRi_tP7yoeoXB5UarvLHidaK1Yw2Pae7Py90,1366
@@ -666,7 +666,7 @@ scout/server/translations/sv/LC_MESSAGES/messages.po,sha256=Wp7Mx4LoiFNtzsV1tpAo
666
666
  scout/update/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
667
667
  scout/update/panel.py,sha256=4z-vQ7GVIJXtlIub9pubFcQ-I9mLX5fRXKNPBdpuhJw,1649
668
668
  scout/utils/__init__.py,sha256=DHCMH05dxkyD7m9QSs0WXsQQPgVRFQ9XvyNeNpHjjvg,74
669
- scout/utils/acmg.py,sha256=UCyvfg3Wx4-janh4L7LPRtFI6oigwlPAzns-aydPy8U,11540
669
+ scout/utils/acmg.py,sha256=IkWWmHxc0YvoSurzXimKkH_Vf8C0bncywVUVu8m-YlY,11539
670
670
  scout/utils/algorithms.py,sha256=w--NauXbQohZis7obr39a8bS57C4NRelYXn79V7m1dU,800
671
671
  scout/utils/ccv.py,sha256=85lY7uSjHg3VAfWOfrx9jnaDUQXSa5hDQcRIqZ_Ikas,6025
672
672
  scout/utils/convert.py,sha256=asrsis3zkt9jXzseRmCqSa4_t1UT73HPPQRW-DKDdqE,1794
@@ -683,9 +683,8 @@ scout/utils/md5.py,sha256=KkgdxOf7xbF9AF40ZjQKCgWaxFWJ9tp9RKjd8SU6IoA,649
683
683
  scout/utils/scout_requests.py,sha256=lgPumNI_EikBZR1m9ztZI_mZAfV29y1KGoiBv9kejzQ,12797
684
684
  scout/utils/sort.py,sha256=1AcbeZ6vdt_UXM3BLDBa3aQmN4qxrqtskxwD19oBhvw,756
685
685
  scout/utils/track_resources.py,sha256=eUjSEe-Ff8BIb4BHPC_COkJocQO2PaWueiPz1GAuiwY,2614
686
- scout_browser-4.93.1.dist-info/LICENSE,sha256=TM1Y9Cqbwk55JVfxD-_bpGLtZQAeN9RovQlqHK6eOTY,1485
687
- scout_browser-4.93.1.dist-info/METADATA,sha256=OKsRo0nvHP2tVP3vzUfhPN5T34XvOwXA3u4n_dVPjLM,14293
688
- scout_browser-4.93.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
689
- scout_browser-4.93.1.dist-info/entry_points.txt,sha256=q_mxFwbMFTwXRDDIRVcqKram2ubMVmvs3CSNvZri1nY,45
690
- scout_browser-4.93.1.dist-info/top_level.txt,sha256=qM75h71bztMaLYsxn1up4c_n2rjc_ZnyaW6Q0K5uOXc,6
691
- scout_browser-4.93.1.dist-info/RECORD,,
686
+ scout_browser-4.94.dist-info/METADATA,sha256=ARNNqDP69pZ_TiWq-E-x73TQOruCkufvBkMtXwlTMp0,15640
687
+ scout_browser-4.94.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
688
+ scout_browser-4.94.dist-info/entry_points.txt,sha256=q_mxFwbMFTwXRDDIRVcqKram2ubMVmvs3CSNvZri1nY,45
689
+ scout_browser-4.94.dist-info/licenses/LICENSE,sha256=TM1Y9Cqbwk55JVfxD-_bpGLtZQAeN9RovQlqHK6eOTY,1485
690
+ scout_browser-4.94.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
scout/__version__.py DELETED
@@ -1 +0,0 @@
1
- __version__ = "4.93.1"
@@ -1 +0,0 @@
1
- scout