diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..a26a5ec --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: CIViCpy tests + +on: + push: + branches: [ master, staging, hotfix ] + pull_request: + types: [opened, synchronize, edited, reopened] + branches: [ master, staging, hotfix] + +jobs: + run_tests: + name: "Test CIViCpy in various Python versions" + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install Python dependencies + run: | + pip install -e .[test] + - name: List installed packages + run: | + pip list + - name: Run tests + run: pytest --cov civicpy --cov-report term-missing + - name: Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: coveralls --service=github diff --git a/.gitignore b/.gitignore index aa7b828..e17bdf5 100644 --- a/.gitignore +++ b/.gitignore @@ -104,7 +104,6 @@ ENV/ # data folders analysis/data/ -civicpy/data/ # vcf files *.vcf diff --git a/civicpy/civic.py b/civicpy/civic.py index 4493d9a..3d6d1db 100644 --- a/civicpy/civic.py +++ b/civicpy/civic.py @@ -1223,6 +1223,8 @@ def _get_elements_by_ids(element, id_list=[], allow_cached=True, get_all=False): def _postprocess_response_element(e, element): + if e is None: + raise Exception("{} not found".format(element.title())) e['type'] = element if element == 'assertion': e['molecular_profile_id'] = e['molecular_profile']['id'] @@ -1235,7 +1237,10 @@ def _postprocess_response_element(e, element): elif element == 'variant': e['gene_id'] = e['gene']['id'] e['entrez_name'] = e['gene']['name'] - e['entrez_id'] = e['gene']['entrezId'] + #TODO: handle other types of Variants + if e['__typename'] != 'GeneVariant': + raise Exception("Variant type {} not supported yet".format(e['__typename'])) + e['entrez_id'] = e['gene']['featureInstance']['entrezId'] build = e['referenceBuild'] if build == 'GRCH37': build = 'GRCh37' @@ -1333,7 +1338,7 @@ def _construct_get_gene_payload(): name description entrez_id: entrezId - aliases: geneAliases + aliases: featureAliases sources { id name @@ -1375,7 +1380,7 @@ def _construct_get_all_genes_payload(): name description entrez_id: entrezId - aliases: geneAliases + aliases: featureAliases sources { id name @@ -1421,7 +1426,7 @@ def _construct_get_molecular_profile_payload(): ... on MolecularProfileTextSegment { text } - ... on Gene { + ... on Feature { id name } @@ -1482,7 +1487,7 @@ def _construct_get_all_molecular_profiles_payload(): ... on MolecularProfileTextSegment { text } - ... on Gene { + ... on Feature { id name } @@ -1523,17 +1528,40 @@ def _construct_get_variant_payload(): return """ query variant($id: Int!) { variant(id: $id) { + __typename id name - allele_registry_id: alleleRegistryId - gene { + ... on GeneVariant { + allele_registry_id: alleleRegistryId + clinvar_entries: clinvarIds + hgvs_expressions: hgvsDescriptions + variantBases + referenceBases + referenceBuild + primaryCoordinates { + chromosome + representativeTranscript + start + stop + } + secondaryCoordinates { + chromosome + representativeTranscript + start + stop + } + ensemblVersion + } + gene: feature { id name - entrezId + featureInstance { + ... on Gene { + entrezId + } + } } single_variant_molecular_profile_id: singleVariantMolecularProfileId - clinvar_entries: clinvarIds - hgvs_expressions: hgvsDescriptions variant_aliases: variantAliases variant_types: variantTypes { id @@ -1542,22 +1570,6 @@ def _construct_get_variant_payload(): description url } - variantBases - referenceBases - referenceBuild - primaryCoordinates { - chromosome - representativeTranscript - start - stop - } - secondaryCoordinates { - chromosome - representativeTranscript - start - stop - } - ensemblVersion } }""" @@ -1565,24 +1577,47 @@ def _construct_get_variant_payload(): def _construct_get_all_variants_payload(): return """ query variants($after: String) { - variants(after: $after) { + variants(after: $after, category: GENE) { totalCount pageInfo { hasNextPage endCursor } nodes { + __typename id name - allele_registry_id: alleleRegistryId - gene { - id - name - entrezId + ... on GeneVariant { + allele_registry_id: alleleRegistryId + clinvar_entries: clinvarIds + hgvs_expressions: hgvsDescriptions + variantBases + referenceBases + referenceBuild + primaryCoordinates { + chromosome + representativeTranscript + start + stop + } + secondaryCoordinates { + chromosome + representativeTranscript + start + stop + } + ensemblVersion + } + gene: feature { + id + name + featureInstance { + ... on Gene { + entrezId + } + } } single_variant_molecular_profile_id: singleVariantMolecularProfileId - clinvar_entries: clinvarIds - hgvs_expressions: hgvsDescriptions variant_aliases: variantAliases variant_types: variantTypes { id @@ -1591,22 +1626,6 @@ def _construct_get_all_variants_payload(): description url } - variantBases - referenceBases - referenceBuild - primaryCoordinates { - chromosome - representativeTranscript - start - stop - } - secondaryCoordinates { - chromosome - representativeTranscript - start - stop - } - ensemblVersion } } }""" @@ -2200,7 +2219,7 @@ def search_variants_by_coordinates(coordinate_query, search_mode='any'): start_ct_idx = start_idx[:right_idx].index left_idx = stop_idx.searchsorted(start) stop_ct_idx = stop_idx[left_idx:].index - match_idx = chr_ct_idx & start_ct_idx & stop_ct_idx + match_idx = list(set(chr_ct_idx) & set(start_ct_idx) & set(stop_ct_idx)) m_df = ct.loc[match_idx, ] if search_mode == 'any': var_digests = m_df.v_hash.to_list() diff --git a/civicpy/data/test_cache.pkl b/civicpy/data/test_cache.pkl new file mode 100644 index 0000000..b109178 Binary files /dev/null and b/civicpy/data/test_cache.pkl differ diff --git a/civicpy/tests/test_civic.py b/civicpy/tests/test_civic.py index 9d4889c..d43d34f 100644 --- a/civicpy/tests/test_civic.py +++ b/civicpy/tests/test_civic.py @@ -173,13 +173,17 @@ def test_get_accepted_only(self): assert len(mps) >= 1333 def test_get_by_id(self): - # Complex MP + mp = civic.get_molecular_profile_by_id(12) + assert mp.type == 'molecular_profile' + assert mp.id == 12 + + def test_get_by_id_complex_mp(self): mp = civic.get_molecular_profile_by_id(4432) assert mp.type == 'molecular_profile' mp_parsed_name = mp.parsed_name assert len(mp_parsed_name) == 5 egfr_gene = mp_parsed_name[0] - assert egfr_gene.type == "gene" + assert egfr_gene.type == "feature" assert egfr_gene.id == 19 assert egfr_gene.name == "EGFR" variant0 = mp_parsed_name[1] @@ -197,7 +201,6 @@ def test_get_by_id(self): assert variant1.name == "Exon 19 Deletion" assert variant1.deprecated is False - class TestVariantGroups(object): def test_get_all(self): @@ -356,7 +359,7 @@ def test_bulk_any_search_variants(self): CoordinateQuery('7', 140453136, 140453137, 'TT') ] search_results = civic.bulk_search_variants_by_coordinates(sorted_queries, search_mode='any') - assert len(search_results[sorted_queries[0]]) == 20 + assert len(search_results[sorted_queries[0]]) == 19 assert len(search_results[sorted_queries[1]]) >= 19 def test_bulk_exact_search_variants(self): @@ -387,8 +390,8 @@ def test_bulk_re_search_variants(self): CoordinateQuery('7', 140453136, 140453137) ] search_results = civic.bulk_search_variants_by_coordinates(sorted_queries, search_mode='record_encompassing') - assert len(search_results[sorted_queries[0]]) == 20 - assert len(search_results[sorted_queries[1]]) == 17 + assert len(search_results[sorted_queries[0]]) == 19 + assert len(search_results[sorted_queries[1]]) == 16 def test_build38_exact_search_variants(self, v600e): query = CoordinateQuery('7', 140753336, 140753336, 'T', 'A', 'GRCh38') diff --git a/civicpy/tests/test_exports.py b/civicpy/tests/test_exports.py index 633d783..95ca7a1 100644 --- a/civicpy/tests/test_exports.py +++ b/civicpy/tests/test_exports.py @@ -94,7 +94,7 @@ def test_addrecord_from_gene(self, vcf_writer): assert len(vcf_writer.variant_records) <= len(gene.variants) def test_addrecord_from_evidence(self, vcf_writer): - evidence = civic._get_element_by_id('evidence', 373) + evidence = civic._get_element_by_id('evidence', 12) vcf_writer.addrecord(evidence) assert len(vcf_writer.variant_records) == 1 assert list(vcf_writer.variant_records)[0].id == evidence.molecular_profile.variants[0].id @@ -106,7 +106,7 @@ def test_addrecord_from_assertion(self, vcf_writer): assert list(vcf_writer.variant_records)[0].id == assertion.molecular_profile.variants[0].id def test_add_record_from_molecular_profile(self, vcf_writer): - mp = civic._get_element_by_id('molecular_profile', 6353) + mp = civic._get_element_by_id('molecular_profile', 12) vcf_writer.addrecord(mp) assert len(vcf_writer.variant_records) == 1 assert list(vcf_writer.variant_records)[0].id == mp.variants[0].id diff --git a/setup.py b/setup.py index f36c5d4..b2b50f4 100644 --- a/setup.py +++ b/setup.py @@ -31,11 +31,11 @@ ], extras_require={ 'test': [ - 'pytest==4.1.0', - 'pytest-cov==2.9.0', - 'attrs==18.2.0', - 'python-coveralls', - 'coverage<5.0', + 'pytest==6.2.5', + 'pytest-cov==5.0.0', + 'attrs==22.1.0', + 'coveralls', + 'coverage<7.4.4', ], 'docs': [ 'sphinx',