From 6feecc8f3dd21ea6ff1ad19fb5ed1c2527afbe81 Mon Sep 17 00:00:00 2001 From: April Shen Date: Fri, 24 May 2024 15:01:54 +0100 Subject: [PATCH] count multiple clinical classification records --- .../clinvar_xml_io/clinical_classification.py | 16 ++++++++++----- cmat/clinvar_xml_io/clinvar_record.py | 14 ++++++------- .../clinvar_to_evidence_strings.py | 15 ++++++++++++++ cmat/output_generation/report.py | 5 ++++- .../resources/multiple_classifications.xml.gz | Bin 0 -> 27578 bytes .../resources/test_clinvar_dataset.xml.gz | Bin 4620 -> 4438 bytes tests/clinvar_xml_io/test_clinvar_record.py | 19 ++++++++++++++++++ tests/clinvar_xml_io/test_xml_parsing.py | 2 +- 8 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 tests/clinvar_xml_io/resources/multiple_classifications.xml.gz create mode 100644 tests/clinvar_xml_io/test_clinvar_record.py diff --git a/cmat/clinvar_xml_io/clinical_classification.py b/cmat/clinvar_xml_io/clinical_classification.py index dd99e5dc..33feaa18 100644 --- a/cmat/clinvar_xml_io/clinical_classification.py +++ b/cmat/clinvar_xml_io/clinical_classification.py @@ -3,6 +3,12 @@ from cmat.clinvar_xml_io.xml_parsing import find_mandatory_unique_element, find_optional_unique_element +class MultipleClinicalClassificationsError(NotImplementedError): + # Raised when we encounter multiples of clinical classifications or their attributes when not expected. + # This is new as of ClinVar XSD V2 and will be supported at some point in the future. + pass + + class ClinicalClassification: # A score for the review status of the assigned clinical significance ranges from 0 to 4 and corresponds to the @@ -29,7 +35,8 @@ def __init__(self, class_xml, clinvar_record): self.class_xml = class_xml self.clinvar_record = clinvar_record self.xsd_version = clinvar_record.xsd_version - # TODO log the type somewhere.... + # Type of clinical classification: germline, somatic, or oncogenicity + self.type = class_xml.tag @property def last_evaluated_date(self): @@ -45,8 +52,7 @@ def review_status(self): """Return a review status text for the assigned clinical significance. See score_map above for the list of possible values.""" review_status = find_mandatory_unique_element(self.class_xml, './ReviewStatus').text - # TODO replace this assert with something less crash-y - # assert review_status in self.score_map, f'Unknown review status {review_status} in RCV {self.accession}' + assert review_status in self.score_map, f'Unknown review status {review_status} in RCV {self.accession}' return review_status @property @@ -60,8 +66,8 @@ def clinical_significance_raw(self): try: return find_mandatory_unique_element(self.class_xml, './Description').text except AssertionError as e: - # TODO log - return None + raise MultipleClinicalClassificationsError(f'Found multiple descriptions for one ClinicalClassification in ' + f'{self.clinvar_record.accession}') @property def clinical_significance_list(self): diff --git a/cmat/clinvar_xml_io/clinvar_record.py b/cmat/clinvar_xml_io/clinvar_record.py index ffbb906e..72c28cb4 100644 --- a/cmat/clinvar_xml_io/clinvar_record.py +++ b/cmat/clinvar_xml_io/clinvar_record.py @@ -3,7 +3,7 @@ import xml.etree.ElementTree as ElementTree from xml.dom import minidom -from cmat.clinvar_xml_io.clinical_classification import ClinicalClassification +from cmat.clinvar_xml_io.clinical_classification import ClinicalClassification, MultipleClinicalClassificationsError from cmat.clinvar_xml_io.clinvar_measure import ClinVarRecordMeasure from cmat.clinvar_xml_io.clinvar_trait import ClinVarTrait from cmat.clinvar_xml_io.xml_parsing import find_elements, find_optional_unique_element, \ @@ -119,34 +119,34 @@ def valid_allele_origins(self): # The following properties are maintained for backwards compatibility, but are only present for a ClinVarRecord # if there is exactly one ClinicalClassification for the record. - # Otherwise these are best taken from the ClinicalClassification objects directly. + # Otherwise these should be taken from the ClinicalClassification objects directly. @property def last_evaluated_date(self): if len(self.clinical_classifications) > 1: - return None + raise MultipleClinicalClassificationsError(f'Found multiple ClinicalClassifications for {self.accession}') return self.clinical_classifications[0].last_evaluated_date @property def review_status(self): if len(self.clinical_classifications) > 1: - return None + raise MultipleClinicalClassificationsError(f'Found multiple ClinicalClassifications for {self.accession}') return self.clinical_classifications[0].review_status @property def score(self): if len(self.clinical_classifications) > 1: - return None + raise MultipleClinicalClassificationsError(f'Found multiple ClinicalClassifications for {self.accession}') return self.clinical_classifications[0].score @property def clinical_significance_list(self): if len(self.clinical_classifications) > 1: - return None + raise MultipleClinicalClassificationsError(f'Found multiple ClinicalClassifications for {self.accession}') return self.clinical_classifications[0].clinical_significance_list @property def valid_clinical_significances(self): if len(self.clinical_classifications) > 1: - return None + raise MultipleClinicalClassificationsError(f'Found multiple ClinicalClassifications for {self.accession}') return self.clinical_classifications[0].valid_clinical_significances diff --git a/cmat/output_generation/clinvar_to_evidence_strings.py b/cmat/output_generation/clinvar_to_evidence_strings.py index b1cb25c2..a9c4c16c 100644 --- a/cmat/output_generation/clinvar_to_evidence_strings.py +++ b/cmat/output_generation/clinvar_to_evidence_strings.py @@ -9,6 +9,7 @@ import jsonschema from cmat.clinvar_xml_io import ClinVarDataset +from cmat.clinvar_xml_io.clinical_classification import MultipleClinicalClassificationsError from cmat.output_generation import consequence_type as CT from cmat.output_generation.report import Report @@ -77,6 +78,15 @@ def clinvar_to_evidence_strings(string_to_efo_mappings, variant_to_gene_mappings # Catch any exceptions for this record so we can continue processing. try: + # Failure mode 0 (skip). Contains multiple clinical classification annotations. + # This is new as of V2 of the ClinVar XSD and should definitely be supported at some point, + # but as it can cause parsing complications we catch these cases first. + # See GH issue for context: https://github.com/EBIvariation/CMAT/issues/396 + if len(clinvar_record.clinical_classifications) > 1: + logger.warning(f'Found multiple clinical classifications in record {clinvar_record.accession}') + report.clinvar_skip_multiple_clinical_classifications += 1 + continue + # Failure mode 1 (fatal). A ClinVar record contains no valid traits (traits which have at least one valid, # potentially mappable name). if not clinvar_record.traits_with_valid_names: @@ -153,6 +163,11 @@ def clinvar_to_evidence_strings(string_to_efo_mappings, variant_to_gene_mappings report.complete_evidence_string_count += complete_evidence_strings_generated report.evidence_string_count += evidence_strings_generated + except MultipleClinicalClassificationsError as mcce: + # Ensure we catch any of these that fall through (e.g. from multiple description text) + logger.error(str(mcce)) + report.clinvar_skip_multiple_clinical_classifications += 1 + except Exception as e: # We catch exceptions but record when one is thrown, so that the pipeline will crash after processing all # records and printing the report. diff --git a/cmat/output_generation/report.py b/cmat/output_generation/report.py index b2403c7b..714a502e 100644 --- a/cmat/output_generation/report.py +++ b/cmat/output_generation/report.py @@ -31,6 +31,7 @@ def __init__(self, trait_mappings=None, consequence_mappings=None): self.clinvar_skip_no_functional_consequences = 0 self.clinvar_skip_missing_efo_mapping = 0 self.clinvar_skip_invalid_evidence_string = 0 + self.clinvar_skip_multiple_clinical_classifications = 0 self.clinvar_done_one_complete_evidence_string = 0 self.clinvar_done_multiple_complete_evidence_strings = 0 self.clinvar_fatal = 0 @@ -89,7 +90,8 @@ def compute_record_tallies(self): """Compute tallies of records fatal/skipped/done based on the more granular counts.""" self.clinvar_fatal = self.clinvar_fatal_no_valid_traits + self.clinvar_fatal_no_clinical_significance self.clinvar_skipped = (self.clinvar_skip_unsupported_variation + self.clinvar_skip_no_functional_consequences + - self.clinvar_skip_missing_efo_mapping + self.clinvar_skip_invalid_evidence_string) + self.clinvar_skip_missing_efo_mapping + self.clinvar_skip_invalid_evidence_string + + self.clinvar_skip_multiple_clinical_classifications) self.clinvar_done = (self.clinvar_done_one_complete_evidence_string + self.clinvar_done_multiple_complete_evidence_strings) @@ -118,6 +120,7 @@ def print_report(self): No functional consequences\t{self.clinvar_skip_no_functional_consequences} Missing EFO mapping\t{self.clinvar_skip_missing_efo_mapping} Invalid evidence string\t{self.clinvar_skip_invalid_evidence_string} + Multiple clinical classifications\t{self.clinvar_skip_multiple_clinical_classifications} Done: Generated at least one complete evidence string\t{self.clinvar_done} One complete evidence string\t{self.clinvar_done_one_complete_evidence_string} Multiple complete evidence strings\t{self.clinvar_done_multiple_complete_evidence_strings} diff --git a/tests/clinvar_xml_io/resources/multiple_classifications.xml.gz b/tests/clinvar_xml_io/resources/multiple_classifications.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..72bb2c3f53df13393859d91749c82baea42a3f80 GIT binary patch literal 27578 zcmV)UK(N0biwFqZlTc;?|58I%FfcGMHa9jlIWBl@YyjlFX;b4$mN5K%euaWJdIq9x zqs5l(+9tSE6-84}3~+1u`E*0Jg{?-G{AhvljrYgDXUR-uN-_n7fqCb4M0Xdml*`GJ zXJ6j^=Z|G<-G*5nrOEppr)Te2VKPl;Q8ItO^ZV83?r6u#iy)Z=ahinhch+IP^PeC7 z&4T~Dy9nbj$ivI9us#MwID5bIW5rPtMv*7zb@k#M#!T#NqQD6!+aG?Lw@&bIt>b1qZswNSoMacdnu$4nLfo{$bmF&+~fz?r#?dpZDHOd(Ln?zWVoh@%F&lUG=^Pv2WW)ak*z5 zoPD%T!#GINW$Xo>p43g45%DTVfBa|7X0h|j@X?)^!@RU^<5&4JCtV#FYwThn$y}> zIQ0-{)A9mX)^)^aA)dnXqcB^-%0u&a`7_OM8-;gbtR%Gp1-WS!6=4e?@Gvct0)E{2a2^zkbRH(r^xeKT zl-k4o{hN4q3CK`HQ#>K~DL!7pJt;PAv9YU;mBa0hLo3In74YK|_Bq0nbp|_@xfq3x zj1LWqDfgGHB{58hhm7+bQXTQ zK287{ksy3gQiSs`gBxGM3$8!d`5@M6Ez5%Dd$+H~Q6v8o?myl1PiL1`cp+_fH1ciN zASH0!etW+I=N3$0tyX$%9hMoMoOLRO&aK^#>EY>KwU7PgM&5m!03>e1*|CO!!EzOc z8m4^9qIr~j$a(GW_Ql`T3zw^K8il!a75qqnn5~Zif5yJ;?|fL4%K$bhUeG={xV{dj zuv~Rl3+-mseymQ~SlPSN5U$!x8A(?cg<>1dLsT)-6%^ZjpB9acS*-hd(~;sS~%RL7BrbP1aUm(Rp?ctRf4N zTySbY8w>7~h3vxuLK0ecK@PxyTb!18ytZax4)=-9`uoAx!FYW1^>=F*hVo&ky`FUf zFwZPtctPB=c7ZK>dltYKUN05_JWZ@H$YQ)&az?-&HDezB-x7I`l`qme_$I*nFwMdh z;Pe{b^sJ?Fc5DwjP-dc!=Gb`oQ15MB94Ap=ge7?q0UZpt=JY59IJj5ZY z5#gW930$FMZUxtHg!)h8zO{>EA6y;5__H+GLn!{;gDuf;I3%5$th9F-!E?~Ihdhhm+KWd}x%i$_4)6r_1 zxON)EhzCib2@y?d@@&(83~uqxYk21FS#V5A0pym>cN2Cq!VC9lFhG_zv05}&s01Q*V0M?Z;J%XX9Fa@vfHdJr(9iw$1uFldscnL(MrA1dc zW)RKFX`vB-(}Q#CCQ6XJuRy4{3)b7lVA~&g_UQF6_$swRA#u=-i)e{Z1W*~LMR1#y zL=^bErfc{j2~&{2^C;+8=V=_L)0-O6n+92in3$G@h9Lm%bqzn=nI830qg@1cpA+NY{i%QjsMFb$mfKK|YB9J5hK5lHb|5 zA4FK#{gCn1b>&^dzu-Os7e}Pu71sigXs@~f)uv9vby$~mu7Lx<{Y_hDbcSvP89;1A zyc}S%giD5slhyYt5Kj3rx(*BhcN`Yi{Fd&b<=DPB0)`zivAnPx*nO54-B~!#fV*Vy zHL(T6dX{mnvvg^ts6tt!9c7Z%008(zNNmbNxE&-x!PqQbEtjw*A(ERMPV9S@!g^4y zhM^=f0)DO1cunf51^2CqE+A(DK?DgcBKErFo98=Y;5t9Mc~6;PIPMR9`}OYjNwln? zBVw0u)>^kR9JzmvAA}Wu}MT-nJ3Y8IK0u z@Hr9C@An6N_ZesC4;;_)o!3M0&v5`0saR`x?$~>PO^82$qDW3-;1@_>RzX%EeaVml z=2F=tMCt~4o=&5HB`-!RdP@X8{ZL&4fw;pvxj?{ zfq)C54}v(zSP(;V2FhBvwHetDNy;aT(`4lqEe(zZ!5++Jkr1&fa3kh6(0>)IqBur* zr&z4B6tspQ&eO^QBk>~5T99C?6wBpeG)F_%?&HCO&{H5+s^|s02Lcw`>6yEUGbCjwD&P?1ICw** z=`>pdfr{hkPep(5q7|W3`Eg0iEK%MH0C9M+V%!;!v`BG!!Ag79G18u?uv`OugH!HQ z*a8Y3naThg7ttJrMAV{yHdePlt`c=YP<9i%BgAnvlKU1ZkBNfySMrY1^OS;ItXH@y zxO@T;=R|%pCKaHd)@}o+dZgunR!z52zo0~VTaQ)=x=OeOErm4JS-2#?R^k(au8X8x zGu(h$1oR2PL#EMHn4qwP+I{P>Zu_>l-e~ZO;^w(=sv2r_S%kk0H9&qLz1&ZIZ{*eOm zfGC)UNevM+As1+>cQQf=)^WxIxWUqGZ5@8QvUXWhvcBcPO`s^IxX$4oYs!Y{0tC(k z7RN|5%K$35zFycd?pDKQVRVub?=%Gdg}eYZfGZFd6Bvc?oD4KkjMO_z%lQH(zc|?^ zhIIPrWUpIc@>ROUjD4m!Vlbku4MXu>$faHP0m?%{l&B*!%N0NsHPPb(e<2d%vb6=6foCSa4 zHLf3x;0f$fYC$2mREk;zh?`*yj1JJ&Iz|NOf^da)s9TM^bQGOHzfOz7dj9d~^TluI z>+<@zh7xYXyc7~U9Mu_Wr6BP2hhuwBQ%{jv;XRZ0v$8KW?97TYZ`d~Z?!?Bra=Qgu zdBu&&5KrM|#zgjz)`*Lu>(EW=T)&kdu16GOVcmplGNJ)$(JGsh3xfb%QT%5Y)Pd=O zf&oy$=mO7P%IhyAZByh>R~H2m>4iYyg5iB}cFrgmiuPaQEa*IiH0y38a#FhPc87SB)!Kf?YN?Jq|3M*X8W_xlA3R9GL#AIy| zz!?CsQY3wb#&0QKk)8c+0c6S0JQZFm@-4rX8bL)Tauxbe#A}xK61Mm8PCF#JXaZfe zkh3l>rSdS)!Bq&0elome+6?B@YFT(PB`|jdbVZhX)=831Z_pp5owvM6Qs+96eD}Y6=Y*uc>oD7Z5ETjO%9s&UvM_82&9fR)K3<~j zfneOR9zd`NP6m9)?uiyFMQgfp&S5In4+xkH5BTXD-6-grPr}LVMZs^ra{9zFc3-2>xXKXVQ?_et$TEJ4r@&b5s^*voHT}U zUMRY>(29yc=wg#8E=gd$u}oI0jw8xbytaAm-U{WDS0My1Tr!+drK+MzmTj3@1G)Bm z=M`P=SBv1L)E&l-w}*xbLh0=;gArN)*XQg0Khnngj@DHD$q=VUk0;Vm^_#x@rUaktQj?G zYg4=nzYgH~AT|jR4qxpc^g=Rh^9+dYBsRn$tTL{TC%ioyHoKYNl6e;Xt1cM42{NoS z$hE9_i8dB=f{5+$VO5B!A1aBqcrXk?{1n4w5jk)&R!YUrwS8$fj3lXCy19hBjpLZH z6M31V=}Ypp8DQ%qijk8xNdp^G+1L*6q7H{%I%}SyBM0O%4A(|soW&J?fmbwrc~>G5@$Or=jP%s|JV$bgBH04_>$4|O3{ z4~%=d04~VHz@5W}SJmHU;G%Q5I?u^$`o}69;t|v4^#QAe{iq@%1Tl3VQ`xVJi>^j zAPJGMpx~iVG6Vzcs;lIfHAt<0u7R#@fB`F)T?c`2;=Hwd+a0)l4g-mFYmppT3z5Bl zWVRj!81&e6arko=hIa=2@mPf5R>luD`!=*%n6J_X=$>&hgql$bni)vp*(Ck3Mx3I+ zOmq>49pd%u*_~z~MX=-Ljj~EO1JaWIK*9__>{$mBSihQIo@v{;?0!4GB<~|q^llsh z@-c-ES3peef^3#+PDqLXMqy3xtZ51>BJU<$dZcH1us(t>OOc(S{o}J>GeXFu+oCWP zcws1E1>k^Hp+g0x!u2LNW31^8VMHV0S)&GnhoVcDLdF%?gLWGjmD~sn7kEMwP|zBn zB7hqe4p@+m#pHw0kY3n**(9a35TN!zuGLAyf`EpRD_#%Q1$wzkSHcQh*Qv6PfQZ6fQB2K~X{^@!|cX;8|(Nhy7bm_~ifzu%=;S)PM<{b#88l0F@M zzOZ&350+-@QlLocAnsY6InHHjc$$_agW~`h(Kfg14E@p21p>jG0@Fw`3WLNze6sPT zS@$}f(%F(t*jfZpwu0X;&JL{Id;$Nr2Z%YzKq&86O;~7_lp688y!by|AR-tQw0rsO zpN>uB&~?XqrppWwE3c8NtE11|39vN^%yUL)k(5F-UYC+BxNu!Fhaxx@KxBxm5y!2_ks-zLE(xv3?Qjd|hnua; z282>N&UNY*Eq=q$OhMCRA3_}h7#9JOWrX2VVFCv1CJ0n?G9U=Sn$)OqLg6$v^ zA}jJ@e5g_zcdlOdpa2XUH{du6{eJJ+vCrD9@?yA&{d+Ga(HYRW@h zWUewenu}{ySCpqsW+O8HCXtgjaJN`v+;!NF5%;WxH43=bXaK93;h3Z2W^zFy1M5HN zU=}Gl0@wdhw-cEHIa3nV=L-2W`%@qTplBm(*CC**)$zmMLbV5Ll;!jsxj z?;TO#Vt+gUmM{&juhZCkFAy0N_%3BzxITt0A~#!vAYgI)Wiuwkr0*E&(`6?#ddLSu z(n{bN;4jZny|IeRT>Z!hA>%F@&hou_L>9s#PSv_OJV(Ur=uW!}V|f}LCK&gL$C{?Qo826>0OP;lLv273cd6{c(S!apJf7k$<{4F-)R$Z?`}MU6aC7uOGD4 z|Mc6rL+9qY-rifJ8I`sHF9pCbij?v=PCw&&zU%vX*bwHA1hYy+N1PFi$gvxQfSzYFiB|&%PVJ9BP{SXJGg7X` z%T9Dygg7%!!(FBm&hfxV#<)#MjYX0{&`??;9y~37%j9<8^<97LzhZuUIf1hT;o>IJ zPjj$W87dJsbleu4TA(AG8UfiGwvbtw5|1w-KGGL?w2a~)tKw=w_(9PUh%nr;DYH7w zu1NQna_@u-h;r~!(7=;=iZ9MX88>obOq#cDb8GOojw*wae`vN6?s~` zntYpZV$gwQrV#;*5IhwgRfGb+x;q>pDEeb-4H9;)0Af1~5Lm6C;&oW$&7`Qt4W#AQ z*(Kesp0ydt>UO0XXsh~9;VP2Em>`))aA;Gue`4_%y-3F-nBJfvC!uUNAly@7!BNoB zrjet{6R2%LgiD(RLC9f4HbYl_&oS(BHWVI%4q`D+h9ZQb7WqRV<&cJuauUV!h z){l6+R|y1xD#6fX1VCeUM?F#CV&IJREawJU=~>NGuC62w;%rgKGUCbEy+K#6J@c1L zDf}DiYm~VwV}Esh+q?&NQ9L8#JZteZd4~*_*(?roC4>lH>ohD%6x=3o;~J1^Vp*+N z1)C%>>@A#gKm_gr88d)Mcr{q6;|yEQQ`jzsjR!-=9lxSK?zoXwtOrq{14P=A&`5zn zBA8O$U8Zq3En_wuSBbA$BnkyBh~&sh9*pXye9cXvxl~vV7lNNckp=qEGiguBgJpBE z?9m2PVbulCG-V$P9&D7$bd5XaKY>NLyW}Ak{`V>q5v@+JJH#zLP*01~BZ$Fk@_XGX z!h5c#!buZ^w!e;bo(1T3f;*sa5vh)uv!V!tYx^Aot|t%0}b$r6~3=EO-l69Hg~)>txH zs(BG|IF4(y(M9xDn8hjo5y?Fwmw>7tYw0A>iiazO`KSk)vV z{Wvct{~{B!xUenD?^%a%r-fl%M8!q*tQQ5EI7T>$WB-lwZesIg7y=_9O+TDwja6pzDFaNXcgj64Hm*F-TaM6n zM_4f9^;qFi5C=8Obb-k;d470_;c7$29_?-0PdWg3#BpA;u7IpaawQyiuVc+ViBOcORn&Tj zA$!*w^hA|KP!XF2R@BWszdC$tk#T!8LT32Zf;)*CC=xcs&VpnunJWipqI@h0;9Qd| zP_UMax$hCMq9Bt+50{t}%#LB9unDP&e4!*~@7g$0AC>rvPgla$?)Qhjv$qfH)AUg! z0F>lRuZ(TmH4>dk*9`+;4`#_Q$M5xt8o_9}jPM}km^MnvK^&+;j-GDMZ#{xW?1X?S zZOMpaWc5OVUtFUixIs%4R?k=YVfPeHWhss#&Eixh6@9uw*kFzJRPF z=oQt_e@156NX<@+X;|h$N5)YZSFRVFGgPpSEY#aUnF~DxBISk5q9kteJ>WQg z7T$%uOPGSDYiqYZ;OmBS$;!XRr3A4I^IcYpx@BS0?NmWLT6`xgmNZwYi$keS!ko;; zO}b%cAQvsH1I%uk237b3UtD_QBnodk*6%l2Kn0$J2r4idU9z{)tPElj(#55!O7Y-W zhcHxqEpy91O0%>_gu4Bo20>3I022me6dQrmiR{*6z2P~qiImcBcJ zkyXWzDAAC&V|~Fm`+=P!a zu&%HqdabTHTpO)aK3-QIfDDPs=hF^Y4*qHbVO7g4!0;V1+h<|;L+B+frzpUp`vO@7 zC18>^jYQ=RLD8}dpaHc*M`E9FaS}b_V$g7r(8FzIWvt`BHcwZ0&~6JM{RG* zM6U1K?r`*q@i<>(CEGvQVu_JYizuEg(o_#lR6v#H=-PP&6#$ictxBbA=a9<4&LhC3 zE@VwDsu^jvKOgAoltG7g7mI@;D~rMn&|DSMTC zqjHzAGAAz`9T{RY5 z^n32#L`#+Cc{KybH-G?SjWQ9UH~Qi4LBOs1;s+RZ`ih2x@<%wEm8~LhrM%ahB)EOi z9s%`L5kFFk{qPMYt9a@_f2`AFV75*x$?I8XQH5%1P6yQ)!MU)qhhcmSr22`m#aV_u zW;j0eK)M3bt_lT^w8V(xQMFykAt+7AMlmPqXCwUSld!l$&w?oeGs83@n`rT3FH~*~ zd5Pj43z&)XZg$VvQSObf)>(?e&kf92#u&#{$!>C9bNiYFjwlb436P!Hz0*M;%+RMY zqvF1#V`8vZOf?}f4lL2!h_XC_bj0Wu>v+9fEl?r~jRqO4-{zSAPA5y=hHIQbBx7?` z6qT&LQEU)-7l_}a#>Hz*C8`9ADlVa}NlVX96<8pVI~6k^;Y14((G{hxvLS{Q&w>d= z1Uiq<)}y?x3agXTqzm|vP05r_*_KpyKQ_Rv!x6Vlavawmyk>ii7M0t`?r6ktHRyF( zsj*npmL;-Ut?Fwe8c7(=Vw4tC!muc8z5Ds>;I}<=Fr-Qst>1k*!vX92xC|lp^K(o; z5_PCUaf}K2?&m~O2piDMg=`$mE_1=(}JhZ!qW$A`Lec%%5*lhf5^Q59T~md)^#w&EPy~|`f2Av20YE+CCU zJRS{4&MPwz=^Nt$3Tc8dcNk8}t()lLWAQ)^#7^d2YV?i}oTu0t*P4|S3Zny>$P<1# zkJCws{ZqJ+6#AO4jymKa@7r%_%&U)IypH7pE48_>7guIM7K zxnq5a;I<~^vQD5iGAv_mt7Nhq^*-t8Yz73hQ%%yW47VxWkFCoaqv6gi_@_j=(rm97bp-f>DFU(zJ^D+j}->1K6W+5%) zxgK~QW1e&-?NAi7%V81vOQL6wX*;_Oa;AV-E4aY=4UI6}-S3}{KEJh`vFlRph)sd| zsVvk|N$RLJQphH?S%YEfLOa~fJR~buCS%~VhCsNJ4t6L5G-sNqY<3b)$>@eXJ8Eap zVhor4E@gq5DwedqGNC!j6$!UhsO3``N@}HzR{eqLD0lz9_Z%AIaOisdR}^zUmOyRb zCzFcTsN06HNK%Mei-T}6B@;Wb*#xH#>M6AmHs1+VQO0~J=&ROMp72ma+jH$sS?5co z9Y~w6Fi#ci^4w>pts?eCL_bnU7YW)$g=~qo!Tt%%195f|jG#8=e zz_z8Ms_)5YM@0WB6|g{9d~7>p6l4=6yl+UT&>gi z#>xXxQ~_ut-1jK0$7FFACh}9#!%JWeVP^JEA2h=2wQ{}=aL9UCuc2DMZ5Diy!C$x& z@w(W88G$FmytWPQkOwpVvO~DYeki= zGs-8&3Z9r~7*xBGwSm0h$QIqkkq#R1;4q4o!;GiB0_m|_PGEst4sjJEiJ=|GSi#lk zz1!LugKN3TI&Pb~^HtSewv1jQZ#){_|uCb+bN1<8s`Jy zRke`+v+8&t5w5Mjm82zMw#JN*dexN?$7=W#@B)X^eV1-+wbOSSV> znaZZu72^vcy5HoPI2JB0f)TY5>G0ww;Uc(=QnVAI=>y~KkTAf+90xQD*{>j?rj}~~ zR@C2f&Isocm-#vgRwC(ri=vv6`&Cx5!>bD_8|v93 zY=TU2Zdw+_>8BG%l|z>;o${<)q1wN~t`?#YX-BKu*;0kSN}Mx{*qAY`&<200N*c&k zw>f2XQ-x?ue-CI$STc4%)lLVPoH$)zo2fuVHE9)8u}b{KiL+npnnccgUOv{$N|=|2 z_^w-ZGE<&(&0M}LGqe`5aa{HvJ1jB>S4jg-7a^60&)EagRtDq0BcP9oj2*;UOT)2E z9q1(l>(eq==y0_8X}g>`#wEOz!=TX|go+d-KnTJxQz2hQ8-oV%YD1|;eNS!6W?H!j z@&w(q%&M$5gn~*S6L}xHL{?|m+fI2NI>Y{JrbAtTx}E4&ZD+Jsl~d$n*0SQbwTXBD z5-O!kA#+sGOmv6u(g|`PKn*lyc0G`+fA`?D3iP{SW(ZnbseePhC`Ivpg zk&Ek1^8gwOmdFTYeIm^(hL}zE{(7Ad68=r{_37(5sN25d4qcaS*5TFf4m|)3VNV*I z;URj2oS{8HzF$>dk(GSSRrjoeMhDdMWACJybKKEdt{E0stsL%#ets}D?MF9;1Lr0O z7RAra$W_T?h<%c#M5{rvr8bEUXHl}vn#b#D%JHDhN*XdRn?tEFv=l1>&C;YQOx>}} zE)@zDt4CjsKRZ;j-1AUDTN2ufn37tN@0yPYzw8DvNKo=Z7@>!3nrH=MvjX)7lcqMC z4jxL`Q?qZt{2IA!121>;66LnoZ)IFJvAAZB2)daJSS-jRjduh*4Zd5Zz~iwy7!Q6) zm#v;1i}&8uAHbta>cyaq{f*?`4??PiLHO_Lg-hHzK=dm3kpg_y$Fui4W8e07J`g#6 zw=Z7MX*{``!r3LPy39YULp1l9&(ucD1-(0^lIhx5`~boD0qfjlqK$bHuix!!zp3%e z0qz}-+rObC`hbDjx!c(Dya#hP40ez$EuJdcNM=HIL$Q@7=yWmVOJ( zN#7j;xgf{*<@v|s4`+vekc(^BbNX+bzC9ekbb~hsZ?53_8gI578o<=pP5m@KtCo9* zRWnqyYfFLndb3i6D{r6Z$@8Px@Q?KR4>|iE;?P=G0mJ_DBD|*ay!`gY#=0p3Z^!!h z8~iPXw7w;j>apIj_P4Wsz(?lJ?{~gl9Q^?Vwg8mmU41$Feo6e4^fUN*=Yx93K#;9N zRa+IICK_)0!-0NRcC5n%=Cr-vft3ur0k5Qe=>S%Mpg?mmR*$xzK764qxEr_N_q|7M z!R0Ne4_{~t-WIpuz0MY#&083a9=QcaY(akbLR+vmZ(%%q6IgNtp#O5P5~lI3*(}<|{;=wi zTjx39f9?$IfhM47R=UFz#qQQ4xIaT&>uZ&2aehSefUBXa-?szbd(;kWwFCO_MRwqg z$M^2Qf5Z-8tZE1T%k5xv-wuYON9|ywb})P)Eb3#h^*uYdhk!PAKx6e@o|<~&;g)tl zWA$ExA9!2a0gUy+JLr3EzuoTl$mQceJ@@6MgP(8cf5h6`ao@iebG%34zF%R^OYs25 z8QzQg{YT(Fj-__cw_j`rqv7^;Fd9Dd4tO13Q;v5tHK6cEnpn=@1?TjidCpT=&dl&b zFQYsUv0K^W{Lju{^dSGU{^dOm4IWC`7We-*ZoAFc``6fZ!w~#e*mjF|;xDr8x~K0i zvhBK?`LD6v2VYk!^c_&29Jpl9ap=|M$Os+bzK$&$#Dv*7#U6 zbiRf~5yJLouFKd_VS!F-;DbIuq-Dw<|R zR4%REPd^S0_v&=--l6SkXe>K*gaHrc_?Y7{p}g8T{C4nh4~q?AS-(1oyl;o^4%bC0 z`ff-&(1+pw_4@Dj+kVlqi3C-bKUoLBCYFo?# z(9_@LtfKcjB1aKzcG>%#;ZAGpNDm`_@*>Cz3<45CI`|}A84qSrj#;69G+(WLseK}t z-PbUg7Yi~T%Hj*(4?i1kQgRAHg5gX*1IMD8eMwO0kFU&=`56d@4+o(;Xo9echq)hy zo(@9}i_u^xA2eau{H6BE&tWLvd=?m<9sYKVgFh687-#jXFm!boatxIQL;0Wy!{#rw zPks(V`R1>~P-Jjj==I&1+KM_;BB^Umaeq1mt7mWhG=J;2?nQ7-X7;oz^QSx1zaa_t zi$xZu38wwuLUKNk6X;e!RPztXb!oNPZ^wQ2GAC?9IFX*?NJxw zN`#{!SjB0SwoTV^Qg%0QiQO*N1{Krf$#D97~N*YY|xmd4uS!rKy^a;fLQZ zzJ6E~#VUWZzkhdk*Gs08sF%dcUJ@;O^YnHf&f;I;w8-~o6Tr3oKeYX!fQ!85(uQoO= zKdF=SF^UrRz(c9W{!nPt6oB3KmeAvmPem%P-yaT!{LMFdx?$umM`wp`9D6L@#XoC8 zWDAe@d6v9&Fqr_fK{!4-I6v=LE-t;^7LC91luYf4Eh5=^ugngurc-L zj|Vhq{{fTQSjk|xg-OpYj}G?_4-dM>hlez~`}5gd#~%*d;dZh1BQUZxjBkC+%DMG5 zm|%>%Yr9@M5+l=^;U~**W-u-JaDN8l94FUlZ+gA_&m7dc+577-bV7gPbVDZ?bo+32 z-SM>>c7vfE+SjAmWEKWL!S&WAdPm%o`! zF3--jj}+lm4{Egwp1WLvDQQ=*J3Me)&+iL{gn3uMl+?o2vXxBp?kWqSqAEG@k!yl6 z5c;0uYZWEvRW29dc|#@n$xnX0dc>)qoc9R*YJ;cW&OVYMd*GmUg}bCjHNIFojtASZ zZo>6ln&AS;7Lsr6zK$8~QmU8PbEt4Fn-~3?5cIX@>ZolGYU}LC>31x7Ti%GyLR8g{YhNb;_h{y2 zJ&L|R@`95`GS6bp<0CJ zi!k9X&+R2mw9)c#nkKWxc%$Jq$Mc87t&TUOwDbp#_imqPvn2A8rsLo5_AjIX3c11e zAVb9~lc)c#{*ad7fv7kXSS{bUEGOu4gje8pk-&Tst_}A3$LXi92WQ_-K{`A=_;8;XO43D^pmhvF{WP*Kj!AgDP znn{;jGl{omCN~NadH8+D`j*WJ0Q9dQiTwdtIsb=qWd(b;zu^^ulC4iGq+>{O zBN3|Onab*<6_7stAd6`X=vRqx4GC|9B)I~#vYwbefRSzR;qaNUw5 z+AY%VJT9$9MRTfGPIWc|)oYZ>_ucXhYj!3S4HL-NgI#q)Dlmzn30(BScnlXEEB;a! zhP|G30zft@{Cj(PPbMIZ*wQ`%!9~ufcaW9w60(SAfOs{{!c~BtJ?vJY)kLETksM$B z)|yb=FVSmG^c}~xjAB44&WO#UQ?4!>SX1(Sa*IAyWsU3qQ{m2Q>{OurH11owIQGHS z5!amDLn!{;6Q$H?%6ye_LrZ!=P65mQyYrL6h(E3B?}BFTxZ9}e|L_9((o|9j{J&D9 z@4+3#f?KSssG*KqYYwpqVMUC6vllTprv?d{s;CAVE(b8S95`4hyAx^M>#(*xh}DQm zXn`8Le`~aZP;l%I;bvt zdWU7%!(b`OzcPqs&CJG!-X{~6Hg3f&sa zZ@Cz3v=ptcjT0nT&dRK+4?JBL*x7;f$|%4d=rPDqH#OQe4YCZe6B{3DO&htj7Sbc? z12DtrFCs_^Ke(U3ZDi%dNOIA?PPIX(R%f6?)hY>lJ_AxuqnUxh)GGzc3ybD!zFo~( zH8AU7Kb4WquMAX&W%(xeJLp(t68>1x!m(a2jO=^xnaiIIhqM7kZfZ+j>sBDQfTd;| z#fmQuF82ZE`&3|9Hc3Hhj4;8wLDCL2T7(f-a_^!ssu)O}6bKLMs6@L|ta?mUgq4%9 zpi8WvVU(4B@>S^fhu+YALHhg^m`e>AuY)M&(&^Z;yRE@2b$yr%+0X2+?r}fsL0@3IIai*|;A>Nvw#^80ot5HsN1NQL}a{@4#M#w}fsibs0^@zZ0^yXeo9l4*Jy=}xTP`f_*uFRdh8?l4ys#YDeU=y98TMwY zT9{gh*DS118{U*^`ipA$0*I<2G|4|zooZ1qUK9e?d*D_I;tayae}ef!rq7bEJkbFPzUS;I+0VM$y? z!_0b7_l9`Vq5f8Qf|D>zbb$&fDtEvle5>xHpwK5kqD8xPXKdR;(MgM>qmmqO;Sazr z>Z6$HQ7_nAJIx9%2(Owh=KP~;qS-&&V&E7Xmqhu+>(v9LdIJkNKwiiOU2H1LL79n$aj2YvS!MKU_( zXGiR3XiTZOe-#k|gc&&`uy3<5rF8(;Y;HMzH7ZOLV5Cvlg){4WvJY;w-|(TUjcB z-MvPZQ*{iWCBfa-nsWgog56|%W>)nB36 z`G;hX@py@)?KVu++s#vfH{%(Qw#~+DvSZt5?4)sH8*OacO&hgo@b0$H8RvX>|HB$o3KGb zrqnkn1^0Z^Vx}H=_`QZf4Q9PgE4o#UAfoCtPs~E4Ml~k%#xvXBu4n3w4EqYL@zfh6UCS$9k;-V2~ zaIX|>ZMg5I)n(S~r@&cetwVRdC6&)2K8XPnR|=^quiarAKGGhAy0;)`Z&0p(IR}6w z8Yks8wXoUtcir!tmfc%+h1R`*nZ@drdafgEV*sT_*IN&wL_^dXdHq#SVS(#+#|Da; z$H}&eG90LA7R8!%3v49Owh2Eq0sDWr)0K%9%gv~eAh8JYD>bY6Rr_7NE3uykJrbZx zKxGcuo|H=6!@c@*f|NoL5IBc+2h&P7azdUcV)7iZ&HgFTflkNIrD<9~a>g6~rRa}! z3P}d-ZKZm*aaeLIF2|6At&{I>uyxXc9_!AFGRUC39(0P2#sR!wW&&3ha&fwXG{pI% z_+s#K;f>u>RrIag-$4-OiDf)CusxN@tuna% zaZUmXI7sd$|KprghfAF?O*1#|zEnSzthV{;zWi06VAZ|D4m-Kj$=5^Hdn)HNoiSZtxA# z5oz}omDr594tZw4`)$fwS@!sVZk#8qQdU-nX1P#4a*ncTpviP`OrgaCBkoT~7H?o8 z7@bgejv*qT1`E^@My3g9GFbC|E8i9XWU<$?3BU)2)SvUeDn)V$pn0jDvb1eRnlukl z4C~=)!tDTzDl*4MjC|JTnWzUuODttzF`?`WZsjI&U?Hw_)SmTv}QOa(Bu2Fw2|xb`a-4lYqKk)p#23u;rMJVN3|wV;P5X<9cBB13|ok&uZ9_gN*c}WKbx!n$;^{P ztzf@(#^F)Bt)xN=;X7((ZL8l{)+P0Hf&6XAsVMb}bFMhcALJyw3Pw&cXov4_#R~Xg z-!M-rwCn&-smdhWwwa|uT^Vr2K6S%-gj|DL$LY_%p=Ae1;1~(UxER!VN0ZEn+@<=% z@|OBj6$+_F{Xm4{N0@Gw9HWq))kOLyP^q^GOR8)zTR202J)GQrRSC(!a}drFH>L>z zqB>YONkk;b;~s@+h_oEOPBc#Ouea|zF)|Ev z;{ahW*4Xx17l^tKRmu^=Lx8$wOo#Q`Gu zV#JIyO_gPL_**qHoC~OyG2WNi!MHdO&WO|8KR_4P{5(<~z($4=iXl28W+;Vr-l9BN zv)a+ddKHmeo2GD*n>6J{!kIduub_k|nUF<*-P$0UuNsU%bG{+`j|{j9g`jD`nm z$f2}Ssf*;2yy31}Zh5!8ZWQDFUKQ8st)sVPUun;8Z;`SF3U;fQ%zFWVJ_zSgzZIoT z+YD~~RVuKZjx6Lg zK87j-ww$Sf%jRz1$g{6xA!XFPj7VNL!}s#VKM3J9_NEhtfp3l%yyF^bzfL)RBTMcQ z96ZjX(q_3Qi+Dcixq{OiG{NDSwGyT-zYu(u8HM8 z*M!6^6QbZ+_MdBVDAn~V6#e6x%!=L}Hk(pQsQz_L?MDB(rmp{SO%rebToWD$?3$t^ zl)GXaA|uh20tb2{yn zD+)w9%vvH)7(%t6T?da6$kYn#MfQf9{E=&W5EKzGRZu{kz~ zEs7BO(Sw~+Et8cxKlze!wo_D86G_@>z3!F#QR+NAlOhr!7A3@>NnDZgFBMz=uTKEU zYCEG&qd%7%rLxrrg=nNPBzh2TR9^CVy@1e& zfblNs7QomTb0PWC2Q?aa33}2K0Z#{lKC{#A!l5So5;VxTF^FvLLDxn$$k8qgwWikm z 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 literal 4620 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 CjoEMj diff --git a/tests/clinvar_xml_io/test_clinvar_record.py b/tests/clinvar_xml_io/test_clinvar_record.py new file mode 100644 index 00000000..0db57f90 --- /dev/null +++ b/tests/clinvar_xml_io/test_clinvar_record.py @@ -0,0 +1,19 @@ +import os + +import pytest + +from cmat.clinvar_xml_io import ClinVarDataset +from cmat.clinvar_xml_io.clinical_classification import MultipleClinicalClassificationsError + +resources_dir = os.path.join(os.path.dirname(__file__), 'resources') + + +def test_multiple_clinical_classifications_record(): + # input dataset with only one record + input_file = os.path.join(resources_dir, 'multiple_classifications.xml.gz') + record = next(iter(ClinVarDataset(input_file))) + + assert len(record.clinical_classifications) == 2 + assert set(cc.type for cc in record.clinical_classifications) == {'GermlineClassification', 'SomaticClinicalImpact'} + with pytest.raises(MultipleClinicalClassificationsError): + print(record.valid_clinical_significances) diff --git a/tests/clinvar_xml_io/test_xml_parsing.py b/tests/clinvar_xml_io/test_xml_parsing.py index 3b734e06..6ac96677 100644 --- a/tests/clinvar_xml_io/test_xml_parsing.py +++ b/tests/clinvar_xml_io/test_xml_parsing.py @@ -10,4 +10,4 @@ def test_parse_header_attributes(): input_file = os.path.join(resources_dir, 'test_clinvar_dataset.xml.gz') header_attr = parse_header_attributes(input_file) assert header_attr['Dated'] == '2023-02-22' - assert header_attr['xsi:noNamespaceSchemaLocation'] == 'http://ftp.ncbi.nlm.nih.gov/pub/clinvar/xsd_public/clinvar_public_1.60.xsd' + assert header_attr['xsi:noNamespaceSchemaLocation'] == 'https://ftp.ncbi.nlm.nih.gov/pub/clinvar/xsd_public/RCV/ClinVar_RCV_2.0.xsd'