Skip to content

Commit c171e36

Browse files
authored
Merge pull request #198 from griffithlab/endorsement-rename
Rename "Endorsements" to "Approvals"
2 parents d55d0a5 + 5811e52 commit c171e36

File tree

12 files changed

+1696
-1252
lines changed

12 files changed

+1696
-1252
lines changed

civicpy/civic.py

Lines changed: 1125 additions & 836 deletions
Large diffs are not rendered by default.

civicpy/cli.py

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,23 @@
22
import click
33
import logging
44
from civicpy import LOCAL_CACHE_PATH, civic
5-
from civicpy.exports.civic_gks_record import CivicGksRecordError, CivicGksPredictiveAssertion, CivicGksDiagnosticAssertion, CivicGksPrognosticAssertion, create_gks_record_from_assertion
5+
from civicpy.exports.civic_gks_record import (
6+
CivicGksRecordError,
7+
CivicGksPredictiveAssertion,
8+
CivicGksDiagnosticAssertion,
9+
CivicGksPrognosticAssertion,
10+
create_gks_record_from_assertion,
11+
)
612
from civicpy.exports.civic_gks_writer import CivicGksWriter, GksAssertionError
713
from civicpy.exports.civic_vcf_writer import CivicVcfWriter
814
from civicpy.exports.civic_vcf_record import CivicVcfRecord
915
from civicpy.civic import CoordinateQuery
1016
import vcfpy
11-
import binascii
1217
from collections import OrderedDict
1318
from civicpy.__version__ import __version__
1419

1520

16-
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
21+
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
1722

1823

1924
@click.group(context_settings=CONTEXT_SETTINGS)
@@ -23,22 +28,35 @@ def cli():
2328

2429

2530
@cli.command(context_settings=CONTEXT_SETTINGS)
26-
@click.option('--soft/--hard', default=True,
27-
help='Hard-update from live API (slow) or \
28-
soft-update from daily precache (fast; default)')
29-
@click.option('--cache-save-path',
30-
help='Filepath to save cache to. Default: {}'.format(LOCAL_CACHE_PATH),
31-
default=LOCAL_CACHE_PATH)
31+
@click.option(
32+
"--soft/--hard",
33+
default=True,
34+
help="Hard-update from live API (slow) or \
35+
soft-update from daily precache (fast; default)",
36+
)
37+
@click.option(
38+
"--cache-save-path",
39+
help="Filepath to save cache to. Default: {}".format(LOCAL_CACHE_PATH),
40+
default=LOCAL_CACHE_PATH,
41+
)
3242
def update(soft, cache_save_path):
3343
"""Updates CIViC content from server and stores to local cache file"""
3444
civic.update_cache(from_remote_cache=soft, local_cache_path=cache_save_path)
3545

46+
3647
@cli.command(context_settings=CONTEXT_SETTINGS)
37-
@click.option('-v', '--vcf-file-path', required=True,
38-
help="The file path to write the VCF to.")
39-
@click.option('-i', '--include-status', required=True, multiple=True, type=click.Choice(['accepted', 'submitted', 'rejected']),
40-
help="Limits the variants and annotations in the VCF to only the ones that match the given statuses. \
41-
May be specified more than once.")
48+
@click.option(
49+
"-v", "--vcf-file-path", required=True, help="The file path to write the VCF to."
50+
)
51+
@click.option(
52+
"-i",
53+
"--include-status",
54+
required=True,
55+
multiple=True,
56+
type=click.Choice(["accepted", "submitted", "rejected"]),
57+
help="Limits the variants and annotations in the VCF to only the ones that match the given statuses. \
58+
May be specified more than once.",
59+
)
4260
def create_vcf(vcf_file_path, include_status):
4361
"""Create a VCF file of CIViC variants"""
4462
records = []
@@ -47,12 +65,13 @@ def create_vcf(vcf_file_path, include_status):
4765
records.append(CivicVcfRecord(variant, include_status))
4866
CivicVcfWriter(vcf_file_path, records)
4967

68+
5069
@cli.command(context_settings=CONTEXT_SETTINGS)
5170
@click.option(
5271
"--organization-id",
5372
required=True,
54-
help="The CIViC organization ID that endorsed the assertion(s) for submission to ClinVar.",
55-
type=int
73+
help="The CIViC organization ID that approved the assertion(s) for submission to ClinVar.",
74+
type=int,
5675
)
5776
@click.option(
5877
"-o",
@@ -64,15 +83,15 @@ def create_vcf(vcf_file_path, include_status):
6483
readable=True,
6584
dir_okay=False,
6685
path_type=Path,
67-
)
86+
),
6887
)
6988
def create_gks_json(organization_id: int, output_json: Path) -> None:
70-
"""Create a JSON file for CIViC assertion records endorsed by a specific organization that are ready for ClinVar submission, represented as GKS objects.
89+
"""Create a JSON file for CIViC assertion records approved by a specific organization that are ready for ClinVar submission, represented as GKS objects.
7190
7291
For now, we will only support simple molecular profiles and diagnostic, prognostic,
7392
or predictive assertions.
7493
75-
:param organization_id: The CIViC organization ID that endorsed the assertion(s) for submission to ClinVar
94+
:param organization_id: The CIViC organization ID that approved the assertion(s) for submission to ClinVar
7695
:param output_json: The output file path to write the JSON file to
7796
"""
7897
try:
@@ -81,41 +100,81 @@ def create_gks_json(organization_id: int, output_json: Path) -> None:
81100
logging.exception("Error getting organization %i", organization_id)
82101
return
83102

84-
records: list[CivicGksDiagnosticAssertion | CivicGksPredictiveAssertion | CivicGksPrognosticAssertion] = []
103+
records: list[
104+
CivicGksDiagnosticAssertion
105+
| CivicGksPredictiveAssertion
106+
| CivicGksPrognosticAssertion
107+
] = []
85108
errors: list[GksAssertionError] = []
86109

87-
for endorsement in civic.get_all_endorsements_ready_for_clinvar_submission_for_org(organization_id):
88-
assertion = endorsement.assertion
110+
for approval in civic.get_all_approvals_ready_for_clinvar_submission_for_org(
111+
organization_id
112+
):
113+
assertion = approval.assertion
89114
if assertion.is_valid_for_gks_json(emit_warnings=True):
90115
try:
91-
gks_record = create_gks_record_from_assertion(assertion, endorsement=endorsement)
116+
gks_record = create_gks_record_from_assertion(
117+
assertion, approval=approval
118+
)
92119
except (CivicGksRecordError, NotImplementedError) as e:
93-
errors.append(GksAssertionError(assertion_id=assertion.id, message=str(e)))
120+
errors.append(
121+
GksAssertionError(assertion_id=assertion.id, message=str(e))
122+
)
94123
continue
95124
records.append(gks_record)
96125
else:
97-
errors.append(GksAssertionError(assertion_id=assertion.id, message="Assertion is not valid for GKS JSON. See logs for more details."))
126+
errors.append(
127+
GksAssertionError(
128+
assertion_id=assertion.id,
129+
message="Assertion is not valid for GKS JSON. See logs for more details.",
130+
)
131+
)
98132
if not records:
99-
logging.warning('No assertions ready for submission to ClinVar found for organization {}'.format(organization_id))
133+
logging.warning(
134+
"No assertions ready for submission to ClinVar found for organization {}".format(
135+
organization_id
136+
)
137+
)
100138
else:
101139
CivicGksWriter(output_json, records, errors=errors)
102140

103141

104142
@cli.command(context_settings=CONTEXT_SETTINGS)
105-
@click.option('--input-vcf', required=True,
106-
help="A VCF to annotate with information from CIViC.")
107-
@click.option('--output-vcf', required=True,
108-
help="The file path to write the annotated VCF to.")
109-
@click.option('--reference', required=True, type=click.Choice(['NCBI36', 'GRCh37', 'GRCh38']),
110-
help="The reference sequence build used to create the input VCF")
111-
@click.option('-i', '--include-status', required=True, multiple=True, type=click.Choice(['accepted', 'submitted', 'rejected']),
112-
help="Limits the variants and annotations in the VCF to only the ones that match the given statuses. \
113-
May be specified more than once.")
143+
@click.option(
144+
"--input-vcf", required=True, help="A VCF to annotate with information from CIViC."
145+
)
146+
@click.option(
147+
"--output-vcf", required=True, help="The file path to write the annotated VCF to."
148+
)
149+
@click.option(
150+
"--reference",
151+
required=True,
152+
type=click.Choice(["NCBI36", "GRCh37", "GRCh38"]),
153+
help="The reference sequence build used to create the input VCF",
154+
)
155+
@click.option(
156+
"-i",
157+
"--include-status",
158+
required=True,
159+
multiple=True,
160+
type=click.Choice(["accepted", "submitted", "rejected"]),
161+
help="Limits the variants and annotations in the VCF to only the ones that match the given statuses. \
162+
May be specified more than once.",
163+
)
114164
def annotate_vcf(input_vcf, output_vcf, reference, include_status):
115165
"""Annotate a VCF with information from CIViC"""
116166
reader = vcfpy.Reader.from_path(input_vcf)
117167
new_header = reader.header.copy()
118-
new_header.add_info_line(OrderedDict([('ID', 'CIVIC'), ('Number', '.'), ('Type', 'String'), ('Description', CivicVcfWriter.CSQ_DESCRIPTION)]))
168+
new_header.add_info_line(
169+
OrderedDict(
170+
[
171+
("ID", "CIVIC"),
172+
("Number", "."),
173+
("Type", "String"),
174+
("Description", CivicVcfWriter.CSQ_DESCRIPTION),
175+
]
176+
)
177+
)
119178
writer = vcfpy.Writer.from_path(output_vcf, new_header)
120179
for entry in reader:
121180
for alt in entry.ALT:
@@ -135,29 +194,37 @@ def annotate_vcf(input_vcf, output_vcf, reference, include_status):
135194
if len(ref) > len(alt):
136195
start = position + 1
137196
end = start + len(ref) - 1
138-
if alt == '':
197+
if alt == "":
139198
alt = None
140199
else:
141200
start = position
142-
if ref == '':
201+
if ref == "":
143202
ref = None
144203
end = start + 1
145204
else:
146205
end = start + len(ref) - 1
147206
query = CoordinateQuery(entry.CHROM, start, end, alt, ref, reference)
148-
variants = civic.search_variants_by_coordinates(query, search_mode='exact')
207+
variants = civic.search_variants_by_coordinates(query, search_mode="exact")
149208
if variants is not None:
150209
if len(variants) == 1:
151210
record = CivicVcfRecord(variants[0], include_status)
152-
csq = record.INFO['CSQ']
211+
csq = record.INFO["CSQ"]
153212
if len(csq) > 0:
154-
entry.INFO['CIVIC'] = csq
213+
entry.INFO["CIVIC"] = csq
155214
elif len(variants) > 1:
156-
print("More than one variant found for start {} stop {} ref {} alt {}. CIViC Variants IDs: {}".format(start, end, ref, alt, ",".join(list(map(lambda v: str(v.id), variants)))))
215+
print(
216+
"More than one variant found for start {} stop {} ref {} alt {}. CIViC Variants IDs: {}".format(
217+
start,
218+
end,
219+
ref,
220+
alt,
221+
",".join(list(map(lambda v: str(v.id), variants))),
222+
)
223+
)
157224
writer.write_record(entry)
158225
writer.close()
159226
reader.close()
160227

161228

162-
if __name__ == '__main__':
229+
if __name__ == "__main__":
163230
cli()

civicpy/data/test_cache.pkl

-19 Bytes
Binary file not shown.

civicpy/exports/civic_gks_record.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
LINKS_URL,
4747
Assertion,
4848
Coordinate,
49-
Endorsement,
49+
Approval,
5050
Evidence,
5151
Disease,
5252
Gene,
@@ -287,7 +287,8 @@ def _get_variant_concept_mapping(variant: GeneVariant) -> ConceptMapping:
287287
),
288288
relation=Relation.EXACT_MATCH,
289289
)
290-
for vt in variant.variant_types if vt.url is not None
290+
for vt in variant.variant_types
291+
if vt.url is not None
291292
]
292293

293294
if variant_types:
@@ -826,12 +827,12 @@ class _CivicGksAssertionRecord(_CivicGksEvidenceAssertionMixin, ABC):
826827
def __init__(
827828
self,
828829
assertion: Assertion,
829-
endorsement: Endorsement | None = None,
830+
approval: Approval | None = None,
830831
) -> None:
831832
"""Initialize _CivicGksAssertionRecord class
832833
833834
:param assertion: CIViC assertion record
834-
:param endorsement: CIViC endorsement for the assertion, defaults to None
835+
:param approval: CIViC approval for the assertion, defaults to None
835836
:raises CivicGksRecordError: If CIViC assertion is not able to be represented as
836837
GKS object
837838
"""
@@ -842,7 +843,7 @@ def __init__(
842843
classification, strength = self.get_classification_and_strength(
843844
assertion.amp_level
844845
)
845-
contributions = self.get_contributions(endorsement) if endorsement else None
846+
contributions = self.get_contributions(approval) if approval else None
846847

847848
super().__init__(
848849
id=f"civic.aid:{assertion.id}",
@@ -858,17 +859,17 @@ def __init__(
858859
)
859860

860861
@staticmethod
861-
def get_contributions(endorsement: Endorsement) -> list[Contribution]:
862-
"""Get contributions for an endorsement
862+
def get_contributions(approval: Approval) -> list[Contribution]:
863+
"""Get contributions for an approval
863864
864-
:param endorsement: Endorsement for assertion
865-
:return: List of contributions containing when the endorsement was last reviewed
865+
:param approval: Approval for assertion
866+
:return: List of contributions containing when the approval was last reviewed
866867
"""
867-
organization: Organization = endorsement.organization
868+
organization: Organization = approval.organization
868869
return [
869870
Contribution(
870-
activityType=f"{endorsement.type}.last_reviewed",
871-
date=endorsement.last_reviewed.split("T", 1)[0],
871+
activityType=f"{approval.type}.last_reviewed",
872+
date=approval.last_reviewed.split("T", 1)[0],
872873
contributor=Agent(
873874
id=f"civic.{organization.type}:{organization.id}",
874875
name=organization.name,
@@ -955,7 +956,7 @@ class CivicGksPrognosticAssertion(
955956

956957

957958
def create_gks_record_from_assertion(
958-
assertion: Assertion, endorsement: Endorsement | None = None
959+
assertion: Assertion, approval: Approval | None = None
959960
) -> (
960961
CivicGksDiagnosticAssertion
961962
| CivicGksPredictiveAssertion
@@ -964,20 +965,20 @@ def create_gks_record_from_assertion(
964965
"""Create GKS Record from CIViC Assertion
965966
966967
:param assertion: CIViC assertion record
967-
:param endorsement: CIViC endorsement for the assertion, defaults to None
968+
:param approval: CIViC approval for the assertion, defaults to None
968969
:raises NotImplementedError: If GKS Record translation is not yet supported.
969970
Currently, only the following assertion types are supported: DIAGNOSTIC,
970971
PREDICTIVE, and PROGNOSTIC.
971972
:return: GKS Assertion Record object
972973
"""
973974
if assertion.assertion_type == "DIAGNOSTIC":
974-
return CivicGksDiagnosticAssertion(assertion, endorsement=endorsement)
975+
return CivicGksDiagnosticAssertion(assertion, approval=approval)
975976

976977
if assertion.assertion_type == "PREDICTIVE":
977-
return CivicGksPredictiveAssertion(assertion, endorsement=endorsement)
978+
return CivicGksPredictiveAssertion(assertion, approval=approval)
978979

979980
if assertion.assertion_type == "PROGNOSTIC":
980-
return CivicGksPrognosticAssertion(assertion, endorsement=endorsement)
981+
return CivicGksPrognosticAssertion(assertion, approval=approval)
981982

982983
err_msg = f"Assertion type {assertion.assertion_type} is not currently supported"
983984
raise NotImplementedError(err_msg)

0 commit comments

Comments
 (0)