From bfa924efafd332316ac1aee0a1f0a07f8e685de5 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 18 Nov 2024 13:07:28 +0000 Subject: [PATCH 01/41] remove *.pyc files from version control --- .gitignore | 1 + .../__allelecontainer.cpython-37.pyc | Bin 41357 -> 0 bytes .../__allelecontainer.cpython-38.pyc | Bin 41766 -> 0 bytes .../__pycache__/__backend.cpython-37.pyc | Bin 23817 -> 0 bytes .../__pycache__/__backend.cpython-38.pyc | Bin 23748 -> 0 bytes src/ScaleHD/__pycache__/__main__.cpython-37.pyc | Bin 173 -> 0 bytes src/ScaleHD/__pycache__/__main__.cpython-38.pyc | Bin 177 -> 0 bytes src/ScaleHD/__pycache__/sherpa.cpython-37.pyc | Bin 21465 -> 0 bytes src/ScaleHD/__pycache__/sherpa.cpython-38.pyc | Bin 21426 -> 0 bytes .../__pycache__/__alignment.cpython-37.pyc | Bin 10794 -> 0 bytes .../__pycache__/__alignment.cpython-38.pyc | Bin 10872 -> 0 bytes .../align/__pycache__/__atypical.cpython-37.pyc | Bin 20897 -> 0 bytes .../align/__pycache__/__atypical.cpython-38.pyc | Bin 20814 -> 0 bytes .../align/__pycache__/__init__.cpython-37.pyc | Bin 413 -> 0 bytes .../align/__pycache__/__init__.cpython-38.pyc | Bin 417 -> 0 bytes .../__pycache__/__generateHTML.cpython-37.pyc | Bin 22832 -> 0 bytes .../__pycache__/__generateHTML.cpython-38.pyc | Bin 22861 -> 0 bytes .../genHTML/__pycache__/__init__.cpython-37.pyc | Bin 196 -> 0 bytes .../genHTML/__pycache__/__init__.cpython-38.pyc | Bin 200 -> 0 bytes .../predict/__pycache__/__init__.cpython-37.pyc | Bin 314 -> 0 bytes .../predict/__pycache__/__init__.cpython-38.pyc | Bin 318 -> 0 bytes .../__pycache__/__prediction.cpython-37.pyc | Bin 45897 -> 0 bytes .../__pycache__/__prediction.cpython-38.pyc | Bin 45787 -> 0 bytes .../__pycache__/__snpcalling.cpython-37.pyc | Bin 5803 -> 0 bytes .../__pycache__/__snpcalling.cpython-38.pyc | Bin 5821 -> 0 bytes .../seq_qc/__pycache__/__init__.cpython-37.pyc | Bin 239 -> 0 bytes .../seq_qc/__pycache__/__init__.cpython-38.pyc | Bin 243 -> 0 bytes .../__quality_control.cpython-37.pyc | Bin 7074 -> 0 bytes .../__quality_control.cpython-38.pyc | Bin 7073 -> 0 bytes 29 files changed, 1 insertion(+) delete mode 100644 src/ScaleHD/__pycache__/__allelecontainer.cpython-37.pyc delete mode 100644 src/ScaleHD/__pycache__/__allelecontainer.cpython-38.pyc delete mode 100644 src/ScaleHD/__pycache__/__backend.cpython-37.pyc delete mode 100644 src/ScaleHD/__pycache__/__backend.cpython-38.pyc delete mode 100644 src/ScaleHD/__pycache__/__main__.cpython-37.pyc delete mode 100644 src/ScaleHD/__pycache__/__main__.cpython-38.pyc delete mode 100644 src/ScaleHD/__pycache__/sherpa.cpython-37.pyc delete mode 100644 src/ScaleHD/__pycache__/sherpa.cpython-38.pyc delete mode 100644 src/ScaleHD/align/__pycache__/__alignment.cpython-37.pyc delete mode 100644 src/ScaleHD/align/__pycache__/__alignment.cpython-38.pyc delete mode 100644 src/ScaleHD/align/__pycache__/__atypical.cpython-37.pyc delete mode 100644 src/ScaleHD/align/__pycache__/__atypical.cpython-38.pyc delete mode 100644 src/ScaleHD/align/__pycache__/__init__.cpython-37.pyc delete mode 100644 src/ScaleHD/align/__pycache__/__init__.cpython-38.pyc delete mode 100644 src/ScaleHD/genHTML/__pycache__/__generateHTML.cpython-37.pyc delete mode 100644 src/ScaleHD/genHTML/__pycache__/__generateHTML.cpython-38.pyc delete mode 100644 src/ScaleHD/genHTML/__pycache__/__init__.cpython-37.pyc delete mode 100644 src/ScaleHD/genHTML/__pycache__/__init__.cpython-38.pyc delete mode 100644 src/ScaleHD/predict/__pycache__/__init__.cpython-37.pyc delete mode 100644 src/ScaleHD/predict/__pycache__/__init__.cpython-38.pyc delete mode 100644 src/ScaleHD/predict/__pycache__/__prediction.cpython-37.pyc delete mode 100644 src/ScaleHD/predict/__pycache__/__prediction.cpython-38.pyc delete mode 100644 src/ScaleHD/predict/__pycache__/__snpcalling.cpython-37.pyc delete mode 100644 src/ScaleHD/predict/__pycache__/__snpcalling.cpython-38.pyc delete mode 100644 src/ScaleHD/seq_qc/__pycache__/__init__.cpython-37.pyc delete mode 100644 src/ScaleHD/seq_qc/__pycache__/__init__.cpython-38.pyc delete mode 100644 src/ScaleHD/seq_qc/__pycache__/__quality_control.cpython-37.pyc delete mode 100644 src/ScaleHD/seq_qc/__pycache__/__quality_control.cpython-38.pyc diff --git a/.gitignore b/.gitignore index e43b0f9..9f2e848 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +__pycache__/ diff --git a/src/ScaleHD/__pycache__/__allelecontainer.cpython-37.pyc b/src/ScaleHD/__pycache__/__allelecontainer.cpython-37.pyc deleted file mode 100644 index 6f959cb8e1ce473fe3474f416fce0f2163d090f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41357 zcmeHQcYGYxwcb^;Bula^+j8%?$TkMsRAayxn~o_4Yy|eQ+B1^YUhT@WE7>x(X{HlO z=-u>|Kp-KIkX{I>FE8(-wh|9wYX6O4g&w9POUI?#@WaWEcj3rv7cw5>1^CZTPE z$uI@&7?=vv(6+;Ln1OaI%!FBJJ76}Ja3rimI}?tARcL3y(Qpjf z*>EhZMmqkPK1-t&WDrX6toNAR5%UoLO31PqwRt-;7qiO z;4C;B?PAyf=b&8z=fZht4}^`d3GGriAG*;V1Q)=CXb*+ zuoLYuFao>K9t#)4ZnUdm59~#I99#mIqFn=*!R2U=hbv$o+O=>cT!nTWTn*QtJprzT z>(HJE*TW5HPlC6>jc8AXo8V@&r@$@ncC@F$t#BLK)8KZv1MTT>C)|a0J=_iVpgjZb zh5OK+3HQSTXwQNNVL#fl;URb!?FM)R9z}Z&JO+=WJr|yUC()ho@Ok(G+BEzZd=c$d z_;2_U+HLSZ@V{s?@MZW4+5z|~d<|_Dz77uB9DD=5iFOda1>Z)y9litKMQg+N;QMIv z@H+eeZ2^7=KSDbMKZc*69fqI6&(Q9GpTjTE?u1{$uh5Rbui-ancfoJrcW5t$-@_l! z?uI|YpV01sKf_aT1u?9h`t?ri z1Su9GX&o!0EBzvrk)0)oa3rVcM#9*?}@3eXwx2O1PyZ1GjN%v)WO^3H; z(6%6*D)73d{=z`U+uzFO`|WhrO7v!ueNJ0`sK?uZzmLmj2Xj4n%N|Y^(z)z#GBaeM zAUt%YFK4F<{R2*OkDW_`yL)q=of{f-mphZgxiqdHvJ3r|Eys1*3b{fulgL}!Z7T_R zr>S>GGLs!lWeZLd(s+x_yf&2^!qv^Z+Ff?N?SWzNym_&;H)roi+K@EvyT9BvhzCvDBZ*`t zV`VI-J#VFQSqQJOcBZUB5}L4+={yQ)69%^|TWkyIG70zF`2O5LZr4a(ZYZDVPY!07 z+8LX*(tZ6sxgl&LeS=nVTizK1>49`Mo$t56UD1|G^$lT*vokQ>0Jol30TaAwd(XM}4Mi#!QjMhX9@ld`#CE0Yj) z*eHhZobmKJA>G?+*?4+stVyhr-l06TmG*%&HZMq6I|q|lUNgSm!a~g>cW*jVuxu_N zryZ}VzH}D%#fHMIze8l<#pJH*iaT|AE7SW_%wB`N#2eE#U_DwVtvx8=H3#ISy0Gk} zgCkC30R^kn2|7uG7!fR+b{N3P~yCxRjewO;$QgrX3KeBQkqKD$$ z?tO^*$N2iLxLt?qx@zp>&^YznCvqXUKzA>vDQ^|r-trXu`g{Duc1?=#iiFxrl~9M0bPR=|^=mlMZ*&X`*1VqQLbr=N=W} zZjw7pl)jbdW$WppV49*p+{{HZhD3}G-PEEo#*mEAc6_VyDr1HsB;;r=r70w(k#4C} z=}{dmq{H8MhKQJ|i15A470~tJUki0~!;Z3ZoAht-qK!wfqO`Vdf zgs;c3R0PtCk&3fK#Vn;9id@ZQv;;DoT6~a*mc>MtQTjY{wuqUpi7DN|TvBUDQXM}s zMT&;&C;T|ILBweF6ZSM$(H5vs>xZvceu|^IIY$&|SFE_1(~mKs=LbikKer;ixS7+p zSD~Lw&4Qm*qmQcRiIjy(!%%lImo+w!<<#??KH6USvf+p2y!WiUQ3Pq1O_8g)kdBa$ zI(iI`>fq)f59ymkgVsF6&76N+z;B;V2ajALOfN46?7{a`jb7Z$>BooYxqKt^ZPYx1 zmyvEc{pm`Z2>i?iOfV4;ya8QMQ9$5lE}*lDfS~^AUF$;O*D8PufZWXKCz{andC_q< zSELs=bNWdEy;GZpozdpU50kw+a7#j{wTDS|F{hmz(ApQ#fg{b1Y3*U8<*nS}HClEt zr=1eeI`#bi8zmm?ws+o^BwrJvoCY0`+hZeaQY)lAA0D?Yp*WTafAN`(TmHf0BpnFfq`7g3JCnn zi(y6}z^UVR+%+PDAJzEXp?3#uvV>iGcW`~onP&#fc3L=@Js`G+iI_`rBk-n13z;Oa{>+a0G*&( z1lvupr(Y1zJ2;d;Lzuaq ztM zf(D(lB`^2mNR^E8x`VuV5j95<6>&D#v?$PIYfs5V7mz!k%;i|N-TD`h>uWA#aTOul znYiQH{8BX}<)L*ZAxCowO9BZ_Jr68Ln+uOL1jFevnm#OIw2=m72Xj#e2BK^l;*Cn0 zKy9UDEPsaxIzVYHCGO^OmR6C&O*axp*ePPPrW|ieGuqu+KP&?A&l$VLBE~OFa`^pzrUu_CctRNGwKsw(2Rdi4HKIkk9wW}?QjQHt(H`u%T%tJ%*NPUcT%z9QVpdlX(@9NR zqJxqSah=GSr!?wP|Dv3?IPOd%Q27Rg-Lb93c?NOLGl)J+H}#k^77eEhYDv4+$5yY7-7`YjGNB940kgf(ajUMSy-dPa)FN-->C9@t#mAY zRH9h=EkYR`b|`W)W97sFy@WB-swh|B?ZO*xk^b9+S8GpUPcv@L2hdA;ES-s5kreN}UC5&`%@S8Lc1{D(*(dTLO9T3M zh=%B6YMC9(lyLN4SHg7=j^^Jf60|x9dzx``%wOl`YfR$!cL}j}jftBXD~I|kuuAy; z?uxA9X2!}9ew}qPRiGNFzef~AgZpK7F;g=Z4_xNTmE*FMTX26xR&g_99bdkr+o%$QX#4|08x7?Z`fX@&ajB_WN|Y)R}RbT=gLoC9BzL^7`3M_HzIFzjvS4zz{XMbM=P?qfp{afDdkIawA&sz z1pb)tMWgQ_C$rMz$h&@i%;?3_AFs&h2i=X%kpu2JqknDY-E{jWgtW}H>G_$RDTmlA z@M1Icj`w6m-oVd{mm}*sFF!-O@$#pHQ+tN?V(Lca%klIId~TTh>56<_DBXx}79Ocm zFVMTg3ynV`lzQLextW#f?DDMKih^kPJB3zjMS-6g@0{|yZcpn+!Jidc^=y4NGuFA~ zS@{ix-;3OU>~q4ay`h90&6qhpuD7NR>TLXxT9^D?LLLo|l=_ZJe5D7kiKs0FALd4lj0Y|BI1>6G{2c17C0<<2u*w>7ogVPoH!*S^kRpAf&n(=c$ zSmz&46&nqoepnPlhoCiYGYuRE)-_D$qxwJ4jEeb)h>3=PEAD2d>To=GnI|vqB|-t+ zkBSbhmk2qUF>{`PULMRGiu`}9B6G;mjG5!jIy3iAerKD&bMivX9~T8$|D^0-ri8=H zCQ5>U@h8lb1Ur~1;pnoiWC}GiJxKgX5fhE7mEXrq4+oWXJ=|wTLd36%5UtOQIGZWp zShB8wOGFO|e@cXCC8FUjL*GYC6-DH4ny{O$SHn!eh#vCU`iiI21uYWLUejz1KS$2P{cchtoS_1l{5 zo_K6eO+2=1*6y0E4TXlSqqf!9XBKL=HtugCU;E5#&E@EiGdH2%-kMFZ^Ksuz_@Q;~ zuiBe!iox0x7_})*>D1)%PUApw8_wBb=bce!Y*=U8IoqkXY&)BC8rH4bbn=FCoI0M( zqduKSudZ4*c>E>Wy%yhwqG4ogIyfN!PJ@teC-^AXmPO8a3KhlTo$;D@Tf99!DOMBT zRoakBc3t0f3C%2!O%7Ozgwvcz4CG)aL(46R#P%Va$Ah=nYw_Ur2?Qq*oJ?>E!KnnN z5v(UTgWxQJ4Fu;9oJ(*X!6t%kf(r;PB-l)_g&;wYBG8^MhPHxb-Sa0|iP32r60jo@~II|%M1xQpO!f_n(=CAg2^eu4)G9wgXL z@DRbn1dk9rO7IxL;{;C-JW22r!P5lK5WItc$Jd^vrRNBE25>V8Cv=* z!RH9ROt7XH=yJAe5bE%g{|iE6ym1|VO#aoBc!k%{a`?N+5$j5hsP9#J-0+%ziDd7I!PXPySU_D;~e}liHy%hcd|3rHk{Ojg8 z#Bk|yv~m2@;PMq{Yw=Tu%lpvQ?buSYv>Lu}#rI6knnpPYp>{@NLz8;;{&gUPcQZVi4^dmnz| zS=MmYOPtHipO=nip7ZCWqvd*@Q>}q<6#F;wauZ`S<7mbf-rCB`ZM-~&v7K=&qdzYl z9eF%^PT(2rI(c~_FHd5e%s7SDPi36OIGu3@A5EE;j_0eJ%Xk3eJjVHq3m6yjzFmC$ zMU39ObiDOI#^QPDXr{eo%)6YouHYja!pn#9vOg~!o!1fUc_bfsCF4nZ&aTA@i%w#<)@kU+Nwh56X~v0J(!i6+Ib+<&Lc)*OIAgsPG;LNI zXp%ylFD*TcQ{CaOV=bpCmF!D+Q_!W7$?&&SUn-UKwvES0$Z*0sizXMtD26-RA?}>= zcg6|eXeP0Om8J0`XUtHxcm=u^e4gQ?ola&837RO)Y4yM5Q#qXLYc$UGWBKA*J?->^ zupyiz&9-NNe>RxEZD%-qf^8T(S-kcvrCi=i~npkCsC(4)o68@GJ;!hqVCjIBrtY)DmLKMt8l; zQ{1UQ{TaI&y}rLrHX%71Ts3Pxof%JRvz-f_anpZ(n;BT(#+kq;lrgt!^#+^F?Z%}-HS}ZbV zs50nI#*XIv-wF7;Yq86V3gCu{>#+qa5q7A>0R!gG9qDwGo6|%~iY} zQc;UtcT|Mi3{un#4i^40&49hkseceq+f(V-YjA}N+*R@fcJlay+Lnoc*=hlVea&V3 zu!;=ssY1_jC}g<^(R-?5Z*wU>3Z&pQnJSqs&8v(8R>lgEP_8n_%bfbh0ku;{Po&Y> z;MV9pq|)|7M1bBJL$2m3eiBkq&riZp8QdtmW3$7dqC;;KVPA70KMe_~DU|fid>7?M zbnbNz6Ak)ZH1IT6@UxJD2KTvDEerRq=sFxBI`py#`2<9wwmxC8SJ zO)s{`hzh*}3%Qz$pciQW@n35XJn^Rge$5qftjL(7HqIhvb0NPC3DHV}E5$!H6~}7P zp;t=S*IWh-9{A;<$l$o!al))O$>0Y=6j`7@vh<{ZUP57(U{lx5qn|H~TVN~Mv*O1%BJ{nRSFpTP&sfRH1gBTwe6y2P{w_FAr=AB^BWLP()3frn^};@_Ogm;b^RD9e0lm#Hl53=_ z`DM?Wpp}n$Mip|bW*Sa5C;vkga=sa2$4f`$VdyhOfqpX#`F&vU9871UL`r^^{vqg*kNMG!2uIA4nHBB;(A8nglL-^(`4jV*{UPEDD zb1{_2;dcgy6GNnxx-yc1jB~2V2>F`J_-jbUd_@LVk3SIVzWSXfLiFk>_BNODMo5Zw z-SUtGh7+*7H;RNZy(D{?Q~%ABI@!0$fI8V{PW|@~wN?TAKX2eE;}A<4&KL6OYG3L5 znbZHnh@Qu0)6iXw-t#l3|7Sq&G*V_U#lE7K0=GdPavbzsE)XSp8!YxV*YmHCo_Z|u zp%O9tTII*)__gXn(W1Xr6*-$rq2IE59Yi<9n};w4iv<)d93F=#i4ATRP5L-Q$qwea zFm2S=Wq%2aD=Nr+-Q#eJ`$NkuqDAjN-Jv4aw@5}U9-ZI2+HrwWS@*}?v{gUhw8qAa z)tJ+e92~T=;1!lr&jHX1vfKb^!bq0uTO^B~_sYHw)`m)l-!>!NI#2%Oo^d!ndy$A) zpkCOedzeeA3nV#pJ-J-Tcj^pa#9n6bq zR24xSQjEsvEzzTg6r=v;avH11;qs9L0YB*9D|+Jwh~Lne;8LvigVq!iG&rZgv!5*GS@;WFO{@V zwtYslEH0x(`|lggHMCZ#D4qaQ&&nC7B2C}RTv}UYY5Z%3tZ2}m4)HNr(uN8nMM;{t zr+ag6zy*5d@c+X`hpI*YF%eIV;-=8he|uXl(T0A$<0aY<3;mHT%g9MKIwMaT!fyne z&XpI|(m^s}4Lr@xmD6f;W_KcuqGXzau$PuLvAfSM{#&7$0#2mS6?i|SE>4~q5)Gwy zxajYALycW&WSU+3U>k(KNn_L=u1bQi9Sf0G2-Qf8J%|&9S*Z#b_nC7 zk{-ZwF)Qis;>lNP*KWqkPN6G(;dbdLk=2axd({{@!DXZxBYT-Ke!n~;{{%dg&az8r zOG9;Zq9Io^?$^t6yLFlByI45O)FqxOy6_)a`l#)d(_Hj+!&5bevsQKsY3U;eooLwC zjGePvbaw7Fic?kg2*2KI6nmQ~;G`B^fmhary+T{AtP5sboW-JZahnPyrCcJMdYcNl znsIaPiq6gT98N^JRLJ#u4*Qz1bHa+w?oMY#`Q)6ea+z@J_oYy>3VQM%S^B8$n-f)Z zW#mpIx9~tQ<70~ zrO=l?zY~4Q4rV$y{Y2N{*1F`ATqOd^)Vg?@F>}_5&dhgV$s@U1i1oX$IGXWtvWd>i ztu358a*a^ytu5?p#?Cn=Iy?6-p`4LxgfxvZ2OVzMq$%0-B8Ae(aCYPh)(wq(%n>rj!(`_6fvUXBoUqN zVWhiR=ql9QTQQ|9|1$lSDr|gm=4_l2qMzHNcy6}}TWK~3U4?JSG_w3V``fB8y@@Gg zIRQjJrzi28ZWpGqt-*$r;+YXq0?Wf z-kNz-dGdq1s!+X|DO5SRLp>|UoA#G;8}1g$(yR`*J-Ue%=*E9!>7!O0XLMBH#G0Xe zihHVXdTwU-AWr1aIr$zG&da!0i1m9=*w>7m^EWE6d#Az=bM$j!cg#s=5Br+2bJ~W^ z&Tn$X2^jYazy2my>}{riQ#Eu2T>3bIC;1r<2)};*VP7+L&dsRA9!j-%uo`>F*NmN0 zGIVw>eSaj)eb0GF`1R5+_BK=S240Aj#)RA?jE9A}Ob6yBVW1cPk)@A%g>wRie$MX1 z(|D|8-y;T$rlU}tccC-#+nk?D@u+a>Z*#t%S;=y`MI}}*gW|DjtbV2il_>p@rH@*= zoMoZ2a*Ha?mUvva^%hm^ZFb(ASD`E5H;SShi6=yW{zg&cY{pMzQ>I1pTXac2#FL^y ze~T{J!Au7yPw3^~zS)HGCY}-rdcWq5V0d{G=*xd(>7%v-%9bco3L$nk<>6`ct#nuM zM)mo(=4R(!Tb`Y#LUWH8&Wd)G9lh6VoF1h zBu3rIOb_Qk=z6$hN;4Ur6)}3rlaCA>!@=rvKY zgIOtX27|7Hdk!@Z;YHD+_Z;d@W_mbrLDgfwie>p;VJ^*Cpq5Z2XMw7N{>aitts2f( z&?UTv`@AF)N>deF32Lf>5jCeNsMHQmyQ=3Jyj(?*zL!~jaCU;K$bJP2<9$M{KU(3# zWGFFVeb){4S8y-;s{~&oSW7U5jxv^@gJ2xN1cFY2i3F1frVvacm`*T*U?#zAg1H3q z2o?}5B7l=q3dh5L`&GnP3Y+f*?uILqI>?iB4L%j1I7zUMB|~n&23M)dXt@jwe`4a3aA;1g8+3MsPa8dV(_u_~#X8(bCxj z8wjo?X_Q7|pF>~w($cxKbRNM*f=vYH6Lb@By77gyw3%QF0q2iiL`z8mPL!lZVgrFi z!1-={wA4?KCfG`_jUYoXK=1%5pwV7?Kfyx;G+-H>;gSZ$qVrm2=>R!`L4xfBHbI`C zKrlowOt6DsC&388E&`4|@1~_a1bYcMVtFYoT}E&@!4(Ai2(BdHD9_cjbPWOZHtxKZ z*V0#N?4fkCHT0Ew5Buu~luDDQri)Wao>pZ#$?&9+r4vn-->PrX*V1vTO>0?xPujcu xR(*@NwmIGs`N!qcRcC*m%Ib>*H3U>@T5&p!tK11|=@N+$HDrw(2ucSK~>1bBs$*=Fp`q8->>FB=it-AM~tK)OKyE`-d zciYM5U7A^w$vh`Nf`85YViwv z!#3)mP7bHgRO;feow})q!w%}DJ`Ou+8cpYLD$Sso9Cpzxn$2N1&7rv*_Ru_<&tWg^ zL<>0VqlL7H!)dfLE#`1K?Ltd9oIy)z8HY1zIj!Ju7VS#AaX6d$X?G6i&>plWhjVE! z+MC09v=8me;e6VU_UCXXI)D!3Z~+}e2XnZP4xvLiTttV_;T-NvN6?WRE~caCXbyLw zV`wFZOXyfSj>DyNJgwqz8Lg%hI9yIA(n%bypp)qo4tJ$f=`;>^qtj^(hy8R0oyp;U2V>&f#!RI+xDla4$NaF5qx)x{wAq+=niri#gnvE}=^~+>df}8Hf8*p4M@A z01XmxcpzCc#Nk0SOd}i~Oa)rc;UTnviX0wFqg3MXFe=j+hlkTfvN=40Dpck0NE)XJ z4v(Tuw3)-BX$x)T@EE$BwsE+UuAnP9JeIc8RU95iSJO2d9#7ZObsVmu>*)p#SJRDj z6Ne|z&2$TgC(^BS8;2*+?Q{o+C)1sD7l)_N-Si3$Po-DVt2jK3?x9z6csjj?Ud!Pc zdL6xZPUrU&Uw9ImA|(_1(^hu%tWSfS_WryN%4XY_Lp$LSaJOAaUKdHNNHo9Nf{0*9OFH}qQ$x6tqC z_Z)7eKhPgJyqsR7KXJH?{!D-2@Cy1X{f)yb>F?YlyyWz(wKk4hr9o?Lesrv84Q2x! zgPcp>aJ=*lHvgjRwoJ~IS)6k(!B@C-0eO;z}RnG|1_YGZ4c}$XSrHA?HBOg`78>;dtgl?u07~aJ~?75#-Lez8G>B$R#9C zErnc$E6ahn0&-XI+zqlHa(Bo*Aoqma3vzGBeIWOR+z)bp#CZVD4}?4j@?gkAAP|4~j?1*MfM=GR$8r@K(9RP$Vhxv_k8#Od~K zZXCqVKJRC~SQswh%5?9_m~ByEu!?IPBh}HO_qQwpxLqBw zY!k23Q!Q8X#azYOXj^%zI2}Wq^2O5FV5#bKFpPKFiK~OyDp%)>;CNX@8&BNYjj<4i__;nZm!Jqb*NCO+J$xF zk{QlSU$5?JudmCBqoWo%Q{^OQTVrLL(=xPi&_C}&Dfz#8^VKb5g+b05Pqla0DwX+~ z^cVy;FS~4u;fgu; z*UXXfXnE_F;qrJTH&oL?M~26&{Dz9tONG%wsZbfQ$i2`rI5<4c zHPWu;3#IB7hksXtzo%9vMsq{?LUG(?qDb_pRjP7BFTQ_Q~5zw<0ivT_73H_>PfMCxi&jLUM-Ju)gH{PT6<=#jxGw8 zC}(+|kViQ`*14tfgjLKLc3dgOInA8B*;E)BvTROXflHE$WN5r1b)|2#z}1U#*5|awvFL*`HgTE8Rxphwc zD`;<@+n~IA{69H2?;;r_Z*$*8(nn_3?8w@Zk{wO9ysMm6^oz&{SFDz+9TltU_L!&e z&lmZ~Y@HjzHY!QM?Grf~hHvAb--X9{7@QkpmGj)~r=`a|l^?KA6An8o9DE-ziF3}H51!p6UOhM}^PLdDlg%VR3X>bI8` zd9jB%pvL+`0Z};9@X%^(P%|x;E{XX95z=?L0!?QvLR=;o-s zl5v`O)Db2#2V|mypR*066&j`J18Ir%1c){qGO~f=;t;>H)?6or^TVqlbV6CZFp#Vo?)_^Qa98`(z2S) z5Pm~$#rPmxanXt}#IATF?DGvRtrgX&rlm8ZMrWW!hVWrFs3>NDkE(>%pjJ_gT3Qw} zC4nUop{4?KGpD%Fy! zrNy5UhhHmunWJ57;I-UG@o;Nkep_mKJ>;N9#Ao<^pXFS3rzHhc@w@ zoFSl%LZOlte7;0^xrk7;2dX{6sFyWFg-Tk~oj6J_$pfvJL5Ja|y7eCS@`k{CwbDZ= zErkUV>D3}a2?w%0fk@XiMD~=@A}{1f{lvHXs{>&ce!KUU6J!P69dpODdtWc%49K5^ zERuL{5E04+;B%Gs;-!Z8HNCWXu`|c(*ZmgEmqR=-?-<%7w=Ba!n|HW6Y4I0JoVSY5 zaP0}f9cqZ{>ZHZpMdH+IpV!}c%RaJ#=N7x(-+5|j(U)+vey+E<3q|4dqx>a$_jbV$ z(du7KFD;X$67MTTg!13bhmC_SJl>4)?|fe^E&8%JbjtQ3hrfpC#y#4QN4;)Z9=pczKz-n8h)V(EwAlJ! z)Y78w#^`>}%&QQxCR$#%K3p|XH1#W|V-2&UUNsXK#df1%vQ()g zVePbBcIVjp-8HL$E?$`$wsNwRU5YFP%n)v7?buy9t{7@d6lx*mw9NL9$h84)};g#(=ynLBlbJ@RxDYED-M<=Y76ydoqobl z(v~HZ2hvj8TL2yr5vt0wq`E|m1e*+>WlDvKsHbJKPaGRm-jID_v*Dsu-jH%yCbBm} z%@^BLIpg~4V0PfI4=MmIR%VYmI9%w`XLl7n|cn@feD4>{@ z!2xj$(COlZAFw`cGkKwPx?%0KTn>!mvM@MCaM#5Hq^>Ze_EcJrKU|1BmX_V2aqLi5 z0mIpqhL2WO0mZZo4vJ%de$FJ65j;EJcEd#L=adK1GCDXJqnhRMs$`6652R&uNF1a2 zQipW2C#EN8T>VM~(w|Ao>##Up=z~L+)@w{IX?<`=IW3dJ=n29ySZix+JII+9mZ9u~-a&>CSao0+O8ZPhLs@q@i3WFRTKZO*N zW5epHVRuQC{yqUL&!Jj{cM}A)lB2?usG*`}1$PC!$|PBC$+=t>mlr6Bpwa&|hJ;oULCxgouxV&H zffi>h{;xIAT8lGk$#G#vh{ZLw{?|3cHEPLmv6!#n&Xa;vE&H!G9KxFcVjoG)2W$Eo zAGCxzll~hF53MECsV2wAIVTna_ zE$eS@i0E106C+~Ipdotwk!MhUhXK|5BTp@P-eT~s;i5j(?C0+^z*>E(=_NHc6t4k6{_9Pnn+IG`N-Hh^K@ zhYSO)9O_h)~7_pM=Lj;$E7KVmp&b8O{-H-Q)~V-8BX?rGUe>;!he5;ni(TJ2?wfGmXVU#Or@V8m9BOp%b<# zH+(EPFO<5*3%y3bNc<_oMC&yIipjCj5yxT&HWNJ25Idll92@1VVWY?LyWkwB%d=sB z+HlZ%EaidZT#&VsbEz4lKa-40?SbT6kW7use5r7{4f?Z&PS`3N|4ed5h+SiZel%o+ z{+ywr^`jx>3p5}6<^1}lq2^VYOU}L`yS1m<{6NdL?1R~C=KRdY zzLre2bwj6pSvGS;Q#P}8@wTS*ZPm8*Q#Lf&YpTub+aKr4RtgkXwrH4f( zJ2Tsq?aB6K=VqF+Tcgz}^5~lW?cy7ulpnQnIj1w18!gj#QO>(^xsBuOi3E4-6F74F zR1s@LoGIdL5$A|FPsD(Ti$&x_tP?>ZhD1nd*hLX#5gSERM2w5rBw~w*D@1G;agB)U zMBFIiW)ZiGxKqU4BJL3Z+a9c4F!R9falhPnlL#yyV|Ddm`HAH?EEZwm1j`4Q$A3WX zVkU{n6lM?@K|d}xo)Cd?0$zzv%1`uTXuGJhUzTf9686_cd{e}?M0{JscSJZMzANH; zBEB!;2O@qb;zuHWEaE33o)hs?5kC|0a}mD~@kbc_{9eQ# zMEp_2iz5Cc;?E-fBI2(i{wCt@B3=^l4-x+q@h=ho7V#euVy&>b&17tDCKJ~*~FrSkBYEu&$h4T zBgwy}h*t25oCkj=iLoUTqyAJh-r$v&g^XMnlJ3ITI zGrRxy&g|$VF6Zs^5BevESJA)d-yB{||G6tm89uv)!z>?7e10v5&3v@*`E?w&^3lfU z*K;_9k9I!4fx`|yI{ExY4yW?b#pgG1*v&@|pWn=3FCTq;ehY`w_?XV;w{kdRZDzNb z+x~syF%`UXZsQV?vS2`J>1<3vfjg<_zc_+xclSo0f^y1oF9a9 z-@~4y>riMOhL{hBJOVju+$nng5tO906STVaI^eDN8xiI^{9p@>Byb{4Uig=3ul ziCx;U(>?ZW zCpp7)K+@0O4A`!>zUjVd;My!KpM_Vb?IWaUkCLLD9W<_R9p-M8+n(igOQ9Iog4#M7 zW@|FfsNBS2Qb%`<-Pgt6)$iz6*)87IdEqxhYR7lJ+lO8F#o>L&Ys-0Pw&1|)g4=2B zDz~w&u~P(I_Q1PZY4_DHsS$A6y(-VY0Kzq^6mc&C(j1fp)(shS|6u z5~Jz(D*cAhQk9Y0(0uK*td<2>b=KYrVP4)BiSn^}O=)+-Y_FTR}5-`e7<=^%#`uv9DnfQ$3)SmH;Y#QKGJ2jS)w? zyuH*Ph%vSO4E`=^E(CScvOv|>SfED>jQ^O&{)UI%qt$DtWg^wzuccfd5l3%cfmFFb z4lqRG3PiNh5|C|lhByJdxk$ZF&Kv%GAq~B)W&zby9PEb#T+0Xe5%nkQD2rnZhb3yQ ztW!?Kf?aB(AJB3F<>5yyS+Ua4(90vJn~H@!DtV!BfZ6U?1FP4>+Sg2Cl<@!LTn<=e zJNcyM$=v-mc#N;9m|`V7ZW2s<t3Q`u3R8TR;pE?O>;47X~ zptlI;1$EO>!r<#Z0SHxBmqotD;+)A?1a#A~z}V|bg$0VpUoCZK zcIO!$dJ)xYr)7fSm{xhQ5Wyx0&g}CIgP86TT4|9nC~JtEAHE;~a(*~1GKOYa?&E(D z;RN%tN`Wpk*bCME($`Cijxkz1I+kh+G%y*urleym*OWrY}209!SehMxtIJVwY!_mOIyjoyPsb^HRe|?@`^=Ay+p-MlAT4clGg! z?1p_H>+XE5?OJWJWw7eB<;TXX68ZJ3753Byx7^7}LH-07Bh8 zoL9NY4qAJ82d`)9+f{A!(I?U}k^!$@)mzq;%aQfho!}}s=QgO2)p?zvw`YW2gU8Zx zl<~38u`NF;|9zU8f368UXaSdvIQ4FP@5SUPjx(;;3{vISmhLGMC zVXzNAkT$O{tk(FzC>gf>TZWEqlnm>qWrKmX#s;}#_8s_p35E7t?(wXT`rDb-mpeW4j$2fCJ zhJrp~hQflVm@>nUko=O_-JwW;@Ac|I%AP?$ zi7_z`(J7sVt|u3N6U~Gn5M7ZL0=3;XiIFk#(vYV}guHcT zlfj%D=?grSts>#vu9?{UdG+nhfr4*=As)!w*Sp zP&YaLn8d2#qaUf?)pCVlp!Xy7+Q~VfU)DHyIbOxO2;Qi1IbKbUhTc#^L+uLeTiI@s ztJki8YI1aR2pT%dJGf8fDg&>VcThJuKAMb%@A}&^`&h0v==wV=u#bf`&7Yjhq1JWO z9}OF6ci%$p>gKfuT~B*ZH+kBTCJi56eR+Qf_NrWGu%jz{ZaZ`JlH(#Z8ZJEDM7En; zZwN#O_d+1@KyoICT4UmtzS(bbgP{;p`bIN3_Kgy!hK;wh*=hncv~~S0Z4{Fu-yDaG zdKcVba+687Uhjgs$?G@Yvc{ETu~ycARK<@>Mr7AL-MZa$v_v;sj#1i3TWNkl$$%r~@K_?rxLB zfY@oG0Y1-wcNsvv$3P1IJ6>|}VLYr;4QdP$m<|s+OtkdLH~6nK>5J|#kyiO%@{Eg5 z{a-Z+R!Xn9*jb{bOZ*7jW3cp!!sX`gEMZOaC+BjgO&z;RG(hoL@ajnbx!_U*Vn>Mv zDE<*%V*vHZ*ni6{Ec`IMb`r4E9$_UuC1sB1(HPq(US~j~+eh5G>24okHS;Iua;OD} z3K)yZB_x|SUOx${r;$>bFwn~&sGA)BL5VdUKd`Uk{>ktI zy2@aAZ;j*Ndb-ArjyIXq$8>7$z7N(ie{wE|+TtIQ z__TaCnE7Gk4bmHr$vZw0WA2E<#7xlN!|@i;0;)a}^!1YCzN0a&w|fJn&8WOCr|sk<2Ya*QnxPyRRS5H&qL~zlQVcMjsfQ3k^L7Bn~c!s z;gJWDGx=hI7Y~w=yr*BiGG9Ji9V5>%V*=bxPF?$TPq$lRPb&cNOj-Xwwk26>{}o5K3ZbL6Qw9>|f%?g(r&0uen&A`c{I@`X4i@CF&$Nbz1# z29M}Q3aPUp_2ev`iDTi8o&(z=-e)N2y;$P-?}jt@cc9dLAT)6h##&@iEk}`Ouq7Nka`Ek}VA>a;!($a=WH)O;6AC zbobnVoOADMqoaic{{HeC?|k(?Z6y+aOfTV| zj)yt?98XImM8Zr6&D2Cvq?$=H$@i3*;=69@d{3KczGut~-?L_x?>RHa_q>_sd%+yx zd(j-_d&w-}t~bZl%Vt?iaN4*zj(fT}u|8=|a$2T2wZ6~X$Isd3{`CXq0e;Rkr`Hdf z2l+YQJhc9p`Iwfto)85w@^M0p+)J8=@m3V0cpJ4Jzn3zP;JqZq@IGcgfp-J%WigKT zapXQKCdA~&3GlI@whmG94EyS!a&L? zaa0_`eMUSfp2GdKI4(}${BYFk9MTpk?or8I7mXmh_IKfZqpTwnv2(B?P}9rw_EO_v~9enZC5>HYF6ux z<ApuGNO`xVJkpdOypnx<+KZB9aV$^dO@w{0OG~y#>QP7Bw zqD3V?Gv98uyRrt_;y=36ZyrB~NKYh?Ph0r=xcItbHkFtp%4%8bcD3pks@3(j=r-xOSgme!t)}`Si>QE~>2zhMY5RI@ z%WC;KVaYXq(wCZD+fUcg+but{h0$!^^;6OozP{9M3Nznr30pQBE!$6_ef`F(rxDmn^O7}B)JiX844krxHr zF_|$WMZY-TZY?+Jw`>bF?ESk!esRS%YMj=UmfL8zjO9ktHY8`L6!JGZcFQrWm^`zF z)e=Ty*|=jh8)BAn+_9zW7%otDEQv05ZRr?y8}3S@#i@+iZ37*VFbcEemeF2XwQKHd zA+O3b@O{ZP?lkPXwg8lDck3&LRSTv8<53l^5)zay8`e_OMqL+b)Isz|clyoa=e&c9 zr+JBL!b{?+d797;B=Cf{Rm_96oSb!2JK%#4G%e98x#^t@eR)7ISue4ZdjRsrJs;c) zbY~h*E%YYNs@ep%if~2GSZK#|nBjEnT4T9ki=#6me%`6cMhA0Q`eQ+RsC$V1{34K4 zqqkJEUB~2>vMpr>ZSm9SUB~uE2EhC&9tF#_t5)4=v>dkz9Dd2M@euX3KNjUuurn!1 zLPQcJ%E#yuO;pKbO7nn-&6&U@p=Y#=R?rJtF7NS>WM(hFX4b!4abAR$r* z6AyTl+|iwq&}qEF{2~nk&-6Yh+Myu0xgOM#XuAKG$VO@#Kc@!nf+8jdTwHMc8mHmf=9&6^T=001m*;2 z0&}XmEZgf zPot%Jj>iR^#N2|UBEGn7V6u$Pmb(Hj*Pj%|himrMMZ(&(w=2ftO2ZladbiPR5<}`b zC=l2qhT{O;0B`FwIySNVigC-Xb)`d9u!Pld35=?;;<}xSXU{?lsI9fZDVHHNDz*0d zS?lae7hZh+g>x5xFzQ&64{7`+sbhUy&_z6VWT}y8B}Jm4(G2p?Wi)@}rkirnrQ4~^ z=Uttkw*M>1?euoWOL^#I@VE@kpN-evO>8{-F5_4FfR|GF z?`c#9%GKm=5&vAp!0cpM&~Qz{Ts#XCt2z0H$de;3xt;Zr&W{-K-*{Q1XG3_AzLaPc z!uLy;~97{bf8Io zN;8kAAM}nCcwYdeZ zGG=uJ`Q=%@;b*!X;=;a8uzng@8=aY)uerV^{9J9N(G(I2 zR8k0NqP2krUIa#r;8a{MtJbq@nnSiF{G4OBB$88FPS>?uvaC&L`?YDvDkBMLg@3wM zPJ*qy^ylu9MbxGUZQOOYsvYR(E)&_pOisz;oZF_xwd%~yB~4p11>O`%XreUv9Flz< zyv7L`^@xJ1lA3Q8ZKcdtiCw9Y*W?^vlpv-yei=W95?bzXGAEJSK2<7QWLLdP{P(hqc+;v%!HZ7#WmodV|{ z!3@ljFa~4!!gdNwqbPE~u)t6G=&2ArjnGpJ{$d~h#zMe|Mo}-F%Ng}hy7QA5xKbYu zEUqZV9%%P9C&SNW^&A5=J^%%lCMHl)Iw%PW2ryiVftnnEN~5eP&M7^5R%OW;sC@%a zIuw@u4>WNA(kWnq#5sojaSZJFK5SI> z1wfTic6q4mpT@vH(}zddKOHFhSzsCW#<^@=#?k+Ceb}h%=K(e0P3(+4NZi+*IQoAf z2I@sXO?s07)c0cQ`BDtjML9Y(|&KixQv>l9S={hMNjkUX&25b%tNmZ;3T>sOxJ+vfOjCM z$?xofQk?gCAF6f1Eqe#9B=n=L{RZmo`Kf!sKmI{hrgfMcqO1TDm$KDGxFmYEVp-I`FH@! zB3f|Bop2|;L+Fvod&&D5=YPgv_);H3kauclANubd@$Q2ZT90v!fqJhG74W)(oZ^0X zA1yFZ_G8{-JZf2IB?kV>1MoD~1oag_9rg|fP`?!e^+62OR{`}nxAXC!omt0^fvv{C zehsikydzv<&MC)0Sus#cfO>*Ud?F|@=e!vMTZ@4efHgcLDDfLHP<9N|GN6unM+2xI z#X!|#pjH5N%sUo9#qs|}4Ad&1p7frC^j(A5;VJOVraJLR1U@O>o)Uuze>uMFU81K*4{6W}`ye5V=T)85koz8}Qk z>+Hh!g8_USz&Gp7GHk(_ih-4V*g&op!;OQSZX8 z*>Y{RMp!PJ7qCWjq5JPL8o-3G+yxX&NNhea!c0ej@meoGhlP;^!<_8pKHOISdKqU$ zRI5hsMY3urt^T5MJ2V*@1OdAQ)b`Q0)pZ;I`_1+n5>9u#+qIK(?eoY)>#kvCvmHxfu3c^O0PUeZj%O9#+XX3Y&U!JgUD{% z8(ks~<876TOstga^UzMtIRRu9*y}CwbM;=1#e6yZ{&_g}%~|eNhq@5-(8s!K!d(t> zz={{;*oAkn?Nf8GkhB3|!z^G2CdJ;XgSEUKWH4?<8DhI&5N&bJX?3coChUpz-q}HP zw-;_2^XkK1weq#gzINT$=6!7uj@bTS24ql<=B_eN%HG0Y32(r42=lp7TLG&BE0Bg5 z;0fWh4?yuPqNtEoD!uWjgihOOke_EJWhS4yX(pe!=_fDW?A;hF@OtBp4SVbmBE?<; z3#v3iL|{c+y`#pLpSwA0JcHlmn;(D(!-@XqXwwkrBUJ(YQ=7tUVO4&CR)p*z?5z-V zk5-J#DN{e}iS}M%pr0e0lU5*NV3xA$wya>DuQys%GBG!;&hDw*XUhXw_Cz$oh+s#p zyDNTTuNo01mFKe%S(`zoJ<&ut_CjM8U}RelsJJl4FEsbeY`|O0>}58%OH+TX?XL8o zo|8m1de0Bi+ai#l&UJ%e!?FSlX9EOh37+If$lKtDw4D_=cYD7wh^yb9K}K>|s*D(8 z9uF!ZAliy@4V{D~Znqr%MbNb2$$~lo^`mYZr_P;u=Ea#xFL}nouii_Yv7W-E{#Cp! z^+x9cg5$p0tH#uS+z%mjk^l|0l_s@fg5F(90RY~jF$vjg51%dOOZ^Ec@4f4o}%aO#KG|ETxztG^_=bCq+( zC1VpmM0Hfmu}DTl%$25tW7{Gm_g$j3KhQ(j;;5xJGPeZpR`qT}fS8{h9Ed)ievVia z`yoc7x>R9@hc_SNIwVOWB{McN1P%IbkdAwY=A5-gN41lDVAXaFJ};#Q?iT-j)O!KM z)_Y_Q@&`RBWV_S55a0BEj+h!waKq8`ey+WSni{<;V{DDeq>xnM8m_HWomyKCDNX$m z8Oj!mY#f}`QMlkLv6}8y?_zu-hx5gvE6p_!anh^?xE^Y=yOofB<#y!@ah-BzFuJEK zY2&WsYo#|iSGPKfVnm2|FRt}y&d)@BU=B_c1mM(~jZO!gI70t=d>_Q;{>&l{&UITI zgj-=;={8u%9;*9Z`NLuc>EAE+)xBWiV@5z{YcIi%e}}^ds*xm+vtan2l2>Sm0-?*@ z=mQY%?UJK9P;hQGZOdW5vvK~+`E!-tSR{>VUAMiw+WgS6@Iqi zh(;X`DM(a5%>vLbG*BP4y7AM@pZsL21J5LXmgG5>Pje9*5-^3wnX?@0;%6cF>QIyr zxEL(L)souGEN^hREvQxKVjKI=EfA7S$5*bt*O%fr0Z9N)z*tJ1n_j_yh_C-K_2(-2tP zB7hM*7%bS_|0q9X*ojBecOj@-ETDyXD4ydnlKt`DxUOMMI+9qcIlUK8GpB}`Rj1Z+ z%~CjkioXm68ptOxiGB?~=Oc=<$mkRYrX7NJ{g8GFzZ^X6>V2A13tEZq>g@m_Z9 z(|*A_LXrpz`S1?~Pr#ClPobL7m+(IHgxZ!6-V}a4gh;lqF2hi}%Pcbtw}NpnUao_x z3xEHN_u>518cHh!?TCK{?v$zr*D}JN0c0`RJFE3val~Vm7A56 zUc)c?OX6t`KZkhwuBcQ*ej)Z0p~bMorv3aC@Z0%jcfHfIP<=#0BXlMR|AYw>h6a3x zg~_=%5Px;1*;uo~Ozw*177X==h!+ISVQq!5FAAzKVD(dRDP0$qu_*R|YDXa|U!)q6 z=y4M}2xdYYT9Se~DGW3SS4Omu&XF1%{EH}IdZ|`tl1gr5)?o0gI|F^i-9(ukOb8Bq zPGAP~CZcvm0g($c>7ntEtAJBu(U0fpcm5i_wnMDBq@I+N65s6j?UlBBR_*CGMq7lf^1R@*X+8|?s0ZqTLQh^SY3 zVf_<`!|6@-s}OZdG)Ut>K^V9$e})HgK_I>jvAXxP@02(~G>N#f)VqkgeLHb0@gC(! zQHY4ABggj;!~!=Yg|Hx)BjcqJ35O6CIO`E4hUY9lBiIGcITtZ%h(OCNQ|^3}JGWDy zh`8-S9YF$!q8lMBaKD^Jm{xIn)EgCuKm8_fczLgg$hA?5Muoc{k*QvZoDmz}em6mJ zsi-F$`8#7C0$xC3V>@Mf+AjC&?v4XT84i~aV+xLc{Sz9i^a2|RSZ9>4qbFw4rhet} z;$<_lc=^`#tBYp#+TyK+%WqvZ(^szEp1)!UZA!%^@RL!~L zXXxFQzK%FCS5}cOiIfmjyxwuQ{In&dwdLpSR(G9(*=+eDWl7;;7T>g%?B>;0txX70 z%h1E}2)1lFn6T?ilr!TzKOnu4rO7nH@%6s8i~v+3nJNW<1?m=QSo|5g3qV*LNB~p-@sjb2NBoSj3n$salS&HRQ|O5`OoSO{hpKma_t3 z>{eBtA=2~Zg|4u!LBxDlaN#X5mdhZ*nAm@FiFsQ0y5(q2XB|ix?vJ0|aEI z4_si^z?$t%9vQ^^hZ#s!H%_|n1e~13oi1A^4P-sJWjiMk^$*9yRs#gV-U4{iK*%Y4 z^0h5Q1OiN6LUVlW{7ll%s6zcTLSk(>qnV?-sz1u-_VqZvQ7Y3A%D9;R0s>WU0Wk}2Xsf; z=P=Ma*p>sO4{oxht^S%Q>=_vRcwSZDTZ3~2%Pt5K%qrz@4Eh(D7H%vC84GzVu+)AD z82B1!o4r}HJG>o(tnU(vIEgyAB8_)wm3Wml17J|XOfXH7I}olw2Ip-V1J#XcR3LZe za0&RTDoUZ$F;KHSg0%isL}Uw$rhFT{d9AnqDshmo<)NqGCMB=~wXuQXvsU@q+#- zDwipd)R&6dUh^PcgcrS?pc5+a(FQWdS^4x+_GXlyjF378>2V@aq4~An79*uE$`by|iqyT&f zAlh7^cI%L|uNYU=jt&T=qp%Y78XY(`Ht<`vftI-$qG(#8KRv{12g$+;zr@ItvvGmh7X{(N}-+bfdRbRhrVMC2>PN^xzs!A09 zU>02rJa$$w&6p)pszB*gm1<_XEmU}L+aF7SBf(x-V^v*iJ!n)!j5$4zyqo!@VEUZ7u1%$3w0ML!Ra#&nWie z7I(0Zm-N*8+U9F+Nu(6KNZw0bK^oXtra?i&DK7(UH%E7oJ%F7c2;}9ooiPggCgzAI z1beeqj3C{evY>$PKjgBDyUHH*at{z{jXhpb>3zJh_z<)Ue9*EH_IxS^P^@IE&1K8g3TmxJCuiU8j+uUyYy z4MFPweP`UuQfoGvVB@sZol=v5?Wn^{FO2fJ@y6L37qL&Ubp{bCEd!eO5-o<8gIU0o z)sMib!0F#2CP!*nqXjcDsbAz046NHn)q;W8HLn_ctzxFCx6bzpecs(W!(~F9qsX0s zf(+4y2q5&Z!6MxNV8tN#3jxe66-M*fFc&I@uT}l@ZF}SGc{6>P!ZOV4O%?Zm^-0V` z#XM#yB+pRAqYZEab2Ty+_ieiJYn1r|y6_aI#mdZhu%2M+^H^vbu1W+(HKaK*ASunF zDpN6F6I&#euaX@FSb9*`!;sQNn94`MZ00$E`q?aE(Zf+sG=3c!LQCP+-{{s6QB{^QXjH zo=4RaY3zxCy^Pi~us7*s`@*w+7#s&b-9S3sr&;WjA$q2;&XGHSjQ9^}&%`vD|Ikv2 zHuddb-x)+nnYJoHi>|2MvpZOMLrqHIimfAxzXZE7|4$)5Pud(*;9cr8HE6a^=DXEt z^--xlJON0*4OE=LP&g+>Be(6?C@{yN5Mh+gD(khUMA8YtC8R;mwaoW5(+Q{kwSn8Gewter6CB^qpKrtE-v_p*sC?SWoPf;JHA!iaa+AeNL~$l^3MeyzO^Msd ztxKfR3mqY)*uw}#Bzz<3M0<$Uw=CXhYpHt6;VtuSqV=o}C26f7Jq@Z08UILX5e`vM z&|*Zvi6TL~kL`u9Go{2RHXQ!hRuRjC(mn8bV44P|BrttnVG?88n##?&%0sz+5}Rv$ zDA)fQ&gJP;GPVbOm$6O+`46eGk$!3zg7Zy2lJ910zNts@!45(HhFY`lk$gMD`Jij4 zn*MLD!K0Y}{V)XQdld7-;e60G2y;@t!w~z2>XMYR!w{Sg8zp$0!oEt%#o>Is;F?~z z7SSCoBuUfat*5k=6515ZEQ;>cCOIg#Igmx$zdOEo)&)03}4ROyzR7Rl7i}qKq0Oz$gmZGz3^KIgK?^?<5@<(tw!6TB_Mtk~jwf zcK>LLZiGR;0pOT0)z}Er_=wJ30h#_-Y3bF5^EazH-R6W6rn7r$pq}G3D{4E?kc4me2Ke1 zn&Xj_r2NMyJ(6KmY5K9|0v1ei1e?Wq%PV+3_~)@em%-bEBvwwO*`#>8>DOX5A3&Zw zP^%WD0V$}Yi_N~X2o|-Tj;Y(o9(7YK!uk$yCAYLX1{sFalsR3*htoVDn<~!qjbCX$>_FF>7AH|O`h%ZsKc2EBMPVW0)>ck#mIU( zip3Lx%z{%sde^>$z#>?r;qv5#8q{d`FQ{I_n1cF6E5^Iv+i;+A`zyWNadGDO`kCYJ z_cF&{KmOM7+Yhxfz3Hf%R5pWFCzT48fFr?SO}DzOYVTN-EnK(Y92GiABiNGyfZqO~ zqr*h7WaGvckIo$E>#)i~^B(%`bBr_D^);ZW(zya;=Tt@=S`R`Tu5@7~gnMir(vnJs0 zUdzZsIKl{u2_`c3`or^`g7Jb>4nzy!@Gy#A*@VSwWE~<1p#*fc1LAiALl;LzA%2IB zpW&D~#A)GNqS|mPOYkQ(E!uLz5Pz52NXmSI-Dj|4{!6MUQu&Y6t%ybc6faPSG$`AD z1t)!gQQu2`XO*p&8o7Q2+`U4FI?jrfd=~0)`UNQcIF}&3dBRPKj0?NuJsd3r>*dro z%v8!ig_ghWAR8z8jaA5aJ@hsREZ%HpJcRlbrA5o zL;+OAM2gH1bk3Q&PVt=%>nP$VDLQ*-B2`R-LjC}GYed8JN5Li$g?t0O6^M}}6myI= zs92+Mc#t9iE^xdJs3e0Q4xFLmX|{8C&f`~5$3$WGzeeW_Z5M?Omp*6-qa;8Sxp$S1p-*7}B7DeSxU^EQg z$6-mI@L)6)lb>X^$Ai=<>@d!VeOO3LMEUlM128idINxNHIxP+&B%z4?mwe@~Q^D6m z2u7f?_H90eu!3=K!khG_ynXcyWnzQpl|$WPuJsM9mMlIAKFP zj=6Rid&H-AN@_oxgpbIi@KGA!jkiW=UByEj^7z)mqx)>!hGhyQur73?D zo_Zr>jjVpSP3|1?-1YhEm*?l_7q4HxjKAx6xQzdouU|*nBEDT;q_6Wx!2?oQ+M6fl zDJ!MW!+iLkz%BooC9#FkpEHFFg637Q1fGj-_kW(wtCPX1c}`RTTV)?n&bI`JVl zG=JE)0GlJ2C5*dJ8lanV=3KZ^Fw-cfEq(0*kj-EBwP)zQh*^sLBdQM20_0^XLA$$v z7>jqPnrzi!5629BDUuTcN`nf17^Am=Ld`5C)N1wc!Bt0)Wv{A}W;n_N=ZCpck{D7f zCe8YSpAj~%{g}BNfm4_1eYWR53-+E@}BypAK*$BEi<>=p6IXj31tju^$$*w1 z39hcn;7ZT^R6HSca3WFA%Q%{djwd>TBZ>CoM=~Faq@?`Mz!h;ll8a;>wt@Z!PO&HnBJI0jCGA%2Lt zjx!{|3x6BF?u;nnc)lFNjY3+&Pkt~_PkEFAeh4U>;SxacabJLjeDx@NskFc@WA;JF z-$c!oa?U$@3lyG+KnU#emH!BzAO#!vwPCINdz6!m8}eV%g;W7VE?GEnO4<2`h<~@~ zf~^%yy^_hbVCIzU?OsLT<}5ezrT6+-JjQVBfif6U(~_NW?Ybt4#UY~|!LSIvxz z)a8abf{KKR!{jj$kq3%U8=)T(Z4d`Yz_31nf8@`l;Gx5y5uy(HU+^u`fk;y$X~$zt zI|ZQ2V5||?hG>I8m%*|Xk`5XRbeS?OeKt6l4E>!{;bQ2*uK|?ZD6kp@=2ueT{ESlJ z*q1R}dw3k|wZLsThKd9>SrX0ikAZJjC8?rZN#zBg>cN_tlc%Y2@cuz02LzhJiV;GY zR5#d)aSAT0LHfc&?V{3p*j23fr;0`+XAWYz$UfA8R?l~|BQPolCm8+_Wh&JutO+Kc zpjksI$aJJ-le&YjnR+m^`>1(2U`W8HPI|Tc6wmQGa|>VSpvs_u%a{SAm0k)Piso?> z$#E)hwjt}1&?OLnh?z+)5!E3>`7%MiWSFm|i)_$2xrI-uf8}4I3n_)Fqu!>c<8*G3dc*p09C>|YC4hB_{)q2X}dV{j6YJX(t!aGWa@Ze zs|z=)MAY;*0i|T{&*|w$bU95I_GEaC#8$mhl?i{Rju7624=$^Vk2a{23t ouP3gT^QF_dLZOs<^bqN}`66C~~Rg@JIi&ewMfVk#y3MBKIU+pLOHjsovSy zot>Gj?%_|X_s%fS@#PZPIvYtG$3d2N@+C3?ColpBF^m8XE*B&@IK~AC&J6=WVBi7; zFmgF0jzQQ-zW1uT=VyDRJ0o09b#-;USO4$5dhbRVs@<2#AOpU}(T zzZ6~;@v}WGkq`+ZAv8l1x=6NkL+5AGNb)mfr1+UO()`RA8GdGsEI)Hbj-PoW&(DG} z#?PWL&d-uj!ZX#H*eV-kEx~D%#w4EU*3{OtG0kb2*38xc;{eCAt=X-E#zBtfT60^6 zj6)pHw+?STU_78Dt|UZ3jC~~`#_sCIgLo^7alDON58X`~NAO+}6L_Dn9>#kG?`1KG z_etbFDyGErR}#h};%RX}%zi~P9u)`098w+=hs0q#kBJAwgLpnJo)Hg;Bgk=FJS-|m zIU$aUNAR2%kBZ0eJSmQe$MJkZ92Y0>ToCi(B%Y_l6Ji0+syHR8c%Bxg#Th)$h_m7x zo@d3A;we1OiR6vM{IhQIZ1s$L#B7V@V%e;NdYPA*{NN99!Rn2;}xA`#*-txTW z=WaN%*>1dg74h+|w7RlWw`?0pNEj2=n%QePqFHzFTC87cSzA`yS&^27_q64xNTzPK zZ<=d1URzkQ)ofdS67B0Z zUd4*QEprGC<7dwz$mzLcN*n#-JbMN+iTsNMcn(2-Ffov0A}Las*eQ_~84PV&WJM0o zjL3@uo>a^FIjq8?)TDpHw$giwhl{%;Or0Fy}?aErSWmP0+s21{9yH?w- zm@#=4DrQ?$nroGtW~(U{D924pI(EeY%C0HV&5kAQ%B`le-fVL!qjow#N7EUFnetAh zv$|o`orOYPm07{}RjYEdY2C5}pk$}lSg)A%U`a3@RpA;TLFuw-uC^@Hb-7L*6#k<) z{g&{v-$LMNUZR%pbcC9x3GHA4F}!VHO>E}mf|J|@UA(7hiFV0J?`G)BJ%Y)4iQU{i zOmjT*!LvY5n(On)Wq0bds!{N$3P-q=}{haddvn&)ap`>|$+8e`0kGTl_S7*tYz!0Wg1tN5XWhn%OX$ZQH2wws|ocCx$RM!R{W7d(Aq zY`1tXA(Dp@_jsh7@!gV0(U^t#MH*w0ao;WE^Ka3_M0Zr0?QYKrMxzq+P37Xs#frrG zNB1CavuagU4!LD>q$9V~g6OPO-g)EoitWf=-RVgSxqVH#4-xseCm2wW@?Zvmm?Ngv+34Utf^x3lXO|Ep z@dN+G7aaR-RLy1GQNI}yus9E<<(e*3lrln|1yK^h-e^3e< zA|goEZAY4QkfCN<^&XE6I*KU=aZ7x0I>2OBx_6v)P`v))sJypn-8oNKd)7|1va;T^ z2fyBGwpv7-dNv9K_K5h{KsP|=y3MXdETCGsZq<9zrYe}i>^cNSRatkO?)lTFAsy5= zJD{3t;3Cy}XX~_i`uTIuJ@f3Db3i!Mxx_Ql_)Syieh&eqvH;q=RnVNjfR+VEtm^-N$xPUTQaeFM;^%PwPABos5_C(8-{68CpZzFKwSAyh=Xs zk}B_Ajc_AflYg6N=mrL5H_Hr%YY^t*RhZbw$v;4z98t=htf$-mlp(+EWs#l@;YIp| zM7t2azktDJ{V9z>c$=mt;!6@5De zRQkuX`gjRK4oQNhew6g}>KR|#^tGEKi@-0uKvMwy-+ie=g%O3#;7?DuZw#%0VNzOs zq1AyvW4~0DYM5_YwT9KU6eI0_?PK55K3*$SBrSThj)n!T6X+1ICw)zS3nXwGt)n*G z*A|ym)lVb$Xe073P}#S;@+F55(n?IB4esmzKUxr?cGHhKSYh)8Kk1kaBV#&{U03~7 zs|oT*Mf%Atv+Jjujfzlg}dA zPl3|dA*miyP&E?%jiRMQ{2CD})ymHRLUL0dMeGas*(6mbvc-qXT7jDMz$Y|mXx7ox z$>T?TW`7%4gj(F##vMxR=#I8Q>=?||e$D~05Xs$)likRP6vDhn-%HUl7a0(n!ft_c zk74y?i5-Kiytb1B*(iz};9uuhK8h8h*cioPuowH-w_hhhBgRpu6qhpYp=A5JF;JyG z6v$gqOd#L?;aHhsF%Xjj5LC((N=XN$K=}ATv!9BAnjU~kqnsJex#3Y9B>`$92I{~7 zR0_h%>^)5!1XoM<in*3G_?A!n>)$`DyggA`4f?SIS2+IBeVh^fV4E#ee@J9fj z^|B1F+m9ng@QB60KO6&J0esHO1@LQ#DfqP*_@goKj{rXJj%j44*{%#~2>582ICTcyWSM0pf{9YNr_Vep=4)99_Vu zoC4NyZyYP2x>NE>UU4(~K~gTq;H>s>evsT=jxd}Ch6!&%oMFg+76W;<4;ew7160{7 zkCgkJ80aVa&?xsi5r(ILVbYuAa#Pp@?OpRv_aP&wX8<+jP3=zHOMH;BS7V@_je&X& zP}AOY0QIXeP|wFeod?v6HzR%u^E73D-=N({p#A4uI z8h}@8BrD6@>X$Lfi@-GN&58@CN!o59_L(TQq+&xjFEY)%G=P&M;Y9qa)<|&nX4dov4 z4taCHk&%aEaJ<>a5w$yRc4uPe%Jn|<2bt}K2t(XF&B%64`8S3zv?B~FsQF=M%9-{K zqbH{C>K|lmKL-ADefS{n%P9G|uZF%`aF8lN7H3MZo;61>j zm9?+Mz<*%?p2nD;-597Z0_q`d=R-j|v-ZD=fvv^B{tRG`ct?T~ z|2ziDjDcDO)Wclj!$FC;SpHX!ffazQcoi-&7t8;w7^pQs9rca|P`fepY{WpV1L_g) zkpL=g{x@TwHURag_b9mSCR7ZMd5@cbyI^j06w2f6JZMe|?_xE}W&4{#m# zjx(+k-U-H)w_l3E)gHi=-+n2=)d8+~Z$7|v(mTnxp75RsaD6QXS9b{4*CJfoz_s8l z1du!gl=W;Rku270on@Txnt2rb7gQwr=%)-3GN>Xy4W zh`C?yAR{!#K}MxWj;a5B)ZGl~Zn~fOiO`|LsV~gRnt2*+=Q608(t;Mnwm!X@LB=1W zn!b)&?o+#cu*BBxzIOWK|BHX_TZ1jV{5ET=uD{iOq?M9_(+dZr5mq^uXVrXh>Ewf>}#YJIl!DQ5g{o=NtT9IV1xC}jo z2hqPRm^yM0Ug4?$Pw9ys2`L z3YBtw8rtbaJAkYKd!tRZE%(J(%ooD%pMujpuxL7Wy3~c34}Gjd3+_UY0~)$0#}MAZ zw$Cg=|Iq=2g-yQ|s0-bf2WxpH$WXZ!Wr*#9LA1q1yWOp!n$Q(C+|z^TZY*D`EU6Fs z)ymf{_}Ud;Tk^FP7+?E?8BBw6w04#1QM${6CA?w<*}|&)7_A7&L5Mj7-KP~}i%OLbJ)yN9 z8|dc<*Q6P+7$}{rh9#?5=UdHojnv02vpc-B`($|_%f5)l7!h=&4QJg?>{lbgr1E?c zBJ*~TX~Tx)tLH^ z`yrg2#6Ux7r3EPf>$d8?G}zovrVYfpRR2TVQB?mYf(Osr3ttY_gwtt3e@=47J_EoD z1I#JN77lE^$gC|GTn22~p zL|kb(*p?;2=|03-`vX0aEpE2BV~eY>T-9zh1t#-Tg9FjW)6Wr$Vis&Ps!I)8c3AB( zt|Oc@5;9{mgVCVx2IsMRc+uW$c2zsczE$hgVb4->;4u5|quvi7w%%il;6La|Av<07 zTzu2}Ibv!!#SKT(`?>ZTYHIXQ#@HH_Ng%1g^jlxA+4YVb5t{lVGLkJ8*(4~dt#H8* zVz!(+?)msej^>L+S6XbsyQEbMaNXAyhJ}!R<%aTwxK7yf7~K=5wD45?wdzhUHq5S? zF(O2~AJy8M^Ak}YT!e`P&NlT{v)ctFj?ljn-v{xzKe32Ii@kOiE>;*G&?2i0dK0!q+a1m_oKY-}`Y4&IFvtWD;2ug5P3^w6vOKoJDh2ehQ^bVZ4|?{aN8$T*%8a;3Jmg2qtNGW#(`MgWE|R; z5gCO(={F|#5P4%_gisshQCe$E4w1{6G;bN>LrKOoUYj?Y0-IYnD}n}t1RJw^=^?{T z?M)v-&@h=n3*&G+#bYG1@!zt0A9Q^F3a16CAsagcNGErRuyh!ik?Z?XfRxl36hB|~#{{9K?VZQz|z|AoG z#nGB1)wrN)8R5^MHbvciP$>wZq=3I>l{p4BjiiEs7w5c2jtAtpB_}8-Bk;4en&{MP zHFjf_?e!#!#RI9mZg_)dn z(hLE^zMXhF@n+(B;vLG7B-aoxg&ZHkaRr8J za$A8TN5)IT^9^n*Fx0~>4Dl?-;oyRJ&Vg?lJkN4#lsg~g&g~Y+3vQ>-fP;WDMmS)S zISGfXBANCDyiR`}7`(h!gumK2`Jlqo4}VmzL?(*uuaYk+>IftL?t}*y7fiQ_-7>{? z%KdsflfY1h5hlc!gwa3sK8;vZ17ZX#Whj~kqc zx4O<9KW$2B-tqHRySGJ-YnD7uS&|5h;_K$B)w4}TL1Aqhg?4QX zdTzMVLdc|i#(40~u<$BtQMGJeYs#0oCH(H|Er?C)ro9dZ?bKABk*Vnyu)`JP_iwen zOf&Q;z$cQ#(WW>ofHM^F#~eb3w;2AAq7(I=!Ml#9uIYc_9vGO>eVj3io)&|ApMw4> z;TSLa_weC%xKwZwAT23nXldYU-;JF-uoXRn7m>D^*}*a(&p`LXLSE@tP4ZJF5&pz# z2Rah7UE}S>`~vI@=u`IP555HZ2J$(B6EtiNq$`8(et>`!_CFeC`e5m>Zok#tKV>0CZUJLSkBkaLMwxr2lL57t&(YNp)#Upx0bkl>= z+h$T0=}?5H$vFpW@vioBnqpq&U2?xx$bZgh!k-pHO#~Be{h*MT3bfBwy}_14?iY|c zoNQo13Kw+D3=3-#f|eR07)den7BDxlP%&_U%7K<>uWk3b-Hv3BbSkA15P?G8?krTc zdrcgQxO`u$$gAkc`^C_DO;7nMlH7&9vgx(XYGoM{7xv43A+%p*yAnjaNA7EntSH|0 z3c=3zVU;AiudUckRc~*#_5(nh*c#ks?|dcLLtZ#DdD$ zwqy|F?G$`^X(N%*{iu}e{=+O%S=C$oPhI$;rRB>ZmWatc;$a{JtjrWo$;M@jz_rm87xtGNl zI8Sj|zJ?lLX_LGyU#EbESj~|qE`}yW@~sD#p>q@JFb*;HYUPqTtO3q*6iOnu*}boy z+J2{XE;QjJWnjS3H* z@MG3g46_lkKcX=vrp-?2JhB-G6NllmE&m}>hu9Pe!9?Sje*HQiW5yL?E}eSVNkT|X zIcZQ92)rIfcNQ#cQ@7vY5l#gooaPbEC@SL=cX51|#8Rguk_uMncas+rA84RdnI`!K zC%p{B+8jMa_8<;=z*m>kb|=WEo2VaR@Z@Ik7k;?AWkKHD-{W$NL*z5h{dh65dcvX^agpL8GY3R5aGW*+*rdWa|L-7S#37p7h|G@-<*K@|-~Z zY!tDpVOJ+wxJEHlq4Ha#DrO2n%*BXQ#r>o)wS}q4PRH?~Kn%QcsMf|9X(6F3=9dC1 zi&dOXld#%>zKrq=s51xZ4>D>0SE4A-pz4Vj5{(8No^TS`Y*${rs-$a@cS#l(l~WaLI;~Ke9}VkgpBRPUeA9dK zU5m{(vnL-^4fHqCnge_C?T+SygrREs7hHqAn7=m)!TI)LzCW4|at2}62 zE{sBOKAeW&aSHoNmn)QUPBGX7bRho-+=QcDQs4o zbcw?G%6;wV{J3JR-=th0Qt%rH=;-0rRgcar{zZR{~??sL?i0 zTRJ9!^C>^0%7I#j%Y$E~4G+`kRqfS{$>3{W+#Vqi&>xZ$MhgA zf%7}ix7mIUSV@m6jSP99O56ythGU$oCKO-LoU{47bb(LJ!gj&kEB`slna}!t!G_K6 zYG*0`1-0sT5g7S?-3vk8F$3i9Q3jgRD&S2B_A`6C@Fev(?H3a;^5iCTY)9yC3O~BX z0d_YC6M6#L&jhrbBTto54!*=a_vYA>qRZb$=|jwjHnP!X_PY>nzzlG>n-8OcaDxaQ z2?P?n-P5t@A@L;1he3aabpE3URFO+@!p1SVF}Zty-?0J01}-Beac~@4H`qB8 z?j&!g)KQG#R!^xmpd4%?QC`qvV%p0cq`qdG%1BSmL@?Z+13fbv^h|Kz)uRh%W|vt* zs7TmH7-~hm9$JD?g7wW;dk$UA!K!2|!?ot^^A#4R+~-KEv<6R1?3_THV8pl+hB30%%wmk3$3ohJnFMkd`Ku{0EGRfO32)-~YP`ySm1@(*edAC4u zVdvzgSKZt(aq8ICsbimaGsj*z_QtUr_q9{*T+~A{{etZ{M@H+e{Q-O0;e12SVm zH9AtV8CkT7j?oxn!56i~3iLB74d?QEP}e|3+G*A;`R@r)?iQSEs0{gCYTO8wEJqqS zg~4tTgQ7=jaCY=5~mUE#L@dSZLNy%cp%S(DGHN^b@_+D zF=TTk!6Op7sgud(x``v0xKjvmtX&+shj}^)RYPQ+#{FL8Y;qeKsz#Ftg-@@SdoGt&Ek-J3W9C=S%~YnCLrzTBI7`9co%p5 zKqWb|1I3b3DWUb3J>=NFjCaspFLf|M>KnQ>WEbKV33!pUmsFAb6&q5B^{C0Wqngap zf%ILfNgmHpNL*Sl09gR!cLO1etWeD@c?_-lm^Fs?5L3DqFrtJz{^E<38@;V9x~1yH z7g^H-dns!^(hU+rVB15O=O(e8?IowlGRAmz*g+aaq(>~ZGSR8%BRf5>h zl;4M@FLtVPGP+13H^q=&mylHR7JFu3)QYhmr`2=c)8zD3ZI@?u1z?pz9D@3giI=Jyd zJcNbzAkJ^k?UvMOamtIg^2qWajpPyc*+Tw2%Z_xE5xgk4^XYt)%+!SSPL!ssM2NX# zq)4p2w?oDjNI?WC%;Sy6mnbWx zP-H3mPhb~^LqB)M*X2zkdG^d1`Bh3zfqpfid-xl8k>8;d zDCus(NDVR$OL2-8o&jG_v%P7IV?9BVz;>ho>D@@89IVMd29TfbNQe$b3OgUZa)#Cq zJEKBf2xg^_cC7#8GMMJUKhtakBP9Pb+JuC9 zm_YGp_y{F~z*zhQ{WmRc|=$<_$}dQ|04v%yx#?T8byEUm7WQ7IWt$*{>8e2w^-mxhrI#%DG_7eFp>MGFx_EOuP( z2txQF%+DE7#C>@=h8qXJgyH&7qLK6{1;h|gxL75C;`_G%4gTtpZ&7KPExBwzkW_Oe zlfQwOTOfZz_$J_3uKbsjpr2~u*MYY2w<+g0DDWxx69jN8GI0Tv($SABfNxT!4=MN! z3cf`F&0wjRp^;Zy5Q8N@MzS%6b%G(slm*xNZ_%d{#1e+C{JGGLY}6pB0NS2FeM*9y zpHYGw+ag9=0E>aO85kQUP?12VO8i;=2&nc{lq$;=J;xQLNudCZfg0yc3z1;uTfPMAyqD8O(ekaiu62P$oTw3TLQ&!aJAqMDO0IV zUPn;f1WltQqngH=iMdGTCb+xSYiNCu5u!)hS; z^o6joXd*Y0Or8Rl4zgqkp#ollSfymvP@OW8FB9a`!+hJ=qJ1%O}kC0*KK2nBMotW)?b26#2+*0T|#;Vho0u}e`8Wfva!mW6u@=R`|(2M6G z%JB`TFbbS+>p#bmxIn%yY40SIBAlj-a-z*L8}xHqxS0_w$M&;VZ?IyEFDz|${LH0G z@4R|rg&8GwG&trV149Px$h|$fS&{!5bv;R~qzRLNCW`KHn@i{#f0@^?7hjow(jTkU z=*&OZo4U={?7?U%;rRS@Kq=1rmlXRq6g)wJMZv$L;F}0wa_KZ0q(P(Yn30B+wtI(t zBjuOrvkp0c`q0l@yL0X0<#R^X-XvW?o1G&`tqRV|rzdT``Oe6LtX26wW_9sfszr2; z*o9wKaz8|ZYFDg*G|4QIS>x(wg4J`FQf^alivn^4WM4LRbYd?Mb{Sv`9vgMovLb(< z^3ev3_i!wwGv8uj#)N_w63-9rz=5g8la7~V?ge!j@=MfWn{=vRm3+OcQVkA(I6n>&EFTyFgT0myoGR{#J2 diff --git a/src/ScaleHD/__pycache__/__main__.cpython-37.pyc b/src/ScaleHD/__pycache__/__main__.cpython-37.pyc deleted file mode 100644 index c4c4ac3f4a0605d65a62a4503f0c2ef59f4976d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmZ?b<>g`kf*#MXIAb9F7{q}ACLqHBh>N9wL<&O`LkeRGQx0P;Qxp>;Ln>1<10zEU zb1;J@OBJ)Bp216?A_h&yTP(SWnR$Mi%u(F&@nxw+#hLke@$pe?#TltZ1&Ku!gU}5B81OQP;BdP!Z diff --git a/src/ScaleHD/__pycache__/__main__.cpython-38.pyc b/src/ScaleHD/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 629528cbe841903a3c6ccaaee608ffa93e172c1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmWIL<>g`kf*#MXIAb9F7{oyaOhAqU5En}Ui4=w?h7`sWrX0pxrYI&xhE%3z21bSy z=3oX*mMUgLJ%g7(MGTsZw^(u$GxPj3nWMPlBfS6s diff --git a/src/ScaleHD/__pycache__/sherpa.cpython-37.pyc b/src/ScaleHD/__pycache__/sherpa.cpython-37.pyc deleted file mode 100644 index 83cea22a0b4ab8091241c9b9f28c4a791caabeef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21465 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 a!vy|tzx|-U3|tz58i`aa|2<9p75^`c$bRks diff --git a/src/ScaleHD/align/__pycache__/__alignment.cpython-37.pyc b/src/ScaleHD/align/__pycache__/__alignment.cpython-37.pyc deleted file mode 100644 index 37f9b5f9c54172ffe46276349e0df271b445613f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10794 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 M%lg&g52fM%0nRj1Bme*a diff --git a/src/ScaleHD/align/__pycache__/__atypical.cpython-37.pyc b/src/ScaleHD/align/__pycache__/__atypical.cpython-37.pyc deleted file mode 100644 index da00e961304801d6978b52867b058ebd78ea9f44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20897 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
4O)nQJ>Yn!=NYbmx(@+7M6T1q0P0Rf@AMTeC&%q zlQp@$xJkcAU_lJWN55v)pgQBmIE&m+;2F^L>Pd5V8u_S2Lb-*0NZ(7J5UV<5XQedi za2fej?k<6r8D(UXrFuyS{ao9!koQtAw- zGz_ID&mr%So-y zhV3|mCyQqQ5A47q3n&KRX-V%qH19)r3V4Q98UH@VJBWh=LELcP1~-b&x`}56e}d1g zyCe9Bdjy(}A&ij0GjLmPA5y_$ZfiO)5otxxNmYVP%n!? z^+{N_;1}Ya;{0FGg$N`+#&W`2CqKxHLdJ!x*ODofsjg!?nWij?k_oSR*(we-jdW7W4ch_hG>Q zc_dt^VShO6=Pht~VgCL5Yp=&WlFsC<&CiNatkkt#D`n!BQtr1>rd+{FVMPEZ)`?bP zf>vk%XqnI{%=*4$3AlW7=yL%W(v=<^Ev^J@RPR;>r61sPR29 z1sJAjMSJ7;Ih6bqC5gQyVrD1f!6Sv>y+t3t!`os4I}Wi~oCj6Pvq33ZhouQF-3LDu zNYywAsErW02=R$7hnUfwM0d`g^9TJrbM^KG^`VdzB$Z(dZ+49=kA#M@zVEH#Jg%YY z$-khxmu_4(xRsKD^&(tig9P&-K^mVuN$>fy*Mj7^wxD<71@?JMUf!tJo15|-w7ha& z{v~zrBMN?q;4XRH#awa8zz20_&d3i_G5H6`A5$F?JYc%}2wo^(JX13JxOryb$*~i9 zdj^i%aW2^x6eE2Ru|R)B{sixB_jE7U=plMNvOTIloBH8^p=Xk5;R z*=rkcT5$v%>N;@CWv@V*Zh2z&P`&=r@HF7BrGljDqMU5uBie>jhGvZ4+_Z1HF(^ao z>sb7>jH@>1AM>R42ta$-&Obw)kD6dRe~O5fBu0zf0pJohf)8{G2AC$WO)4a(0gkad z0oW(7jcKq(q@LxHbL%rdDOPU_SEwsFmkXR z_4Pt*>M!brSh#GW2-xr>iby5F_R(+Lr13!#v2(=G9VB=um*GHY&cQ)&9P`>fxQm^; zue7*JWkBbU1ZtN~H)tU#3v~S8n*3)-VfNE=%y6L8g% zp8O7KmCSMmd;Nww;PHoebii?B@N;?XYGth>Htz8SmG7eUpA)i(_Gv6Wux$}5S=6_S zyH+nGX&2zinyysZ)itM539^;2XT)=r%60t2ohp(45*@MwO^_p}vSv+raLDgc{^uzm zX-v}Dkbg}9!O|zN@hY>_CQ$G(pv0xmtg*GPrk}~prJqQDUi-XOL^zW+CDnl{gT11D zP92eKp!}YWQS1*91PM6bt#7eKI7q>-3C`3F`7J8`6Dm%_w+o)#9m(!bzWYykAR)BUBk9v=J8c0!C;l(Vy&{JI diff --git a/src/ScaleHD/align/__pycache__/__atypical.cpython-38.pyc b/src/ScaleHD/align/__pycache__/__atypical.cpython-38.pyc deleted file mode 100644 index d7f5c27244c0881613c205b430d2ae2dc1459bfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20814 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 diff --git a/src/ScaleHD/align/__pycache__/__init__.cpython-38.pyc b/src/ScaleHD/align/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index ede626e6f04b752f9f0f78a6e447601da3b3a9ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 417 zcmYk2J5Izf5Qd%igJc^jBnsLz>;({FB?`8oAVA>)%fv%u$?*oqk#_eCT!bs7rQ!-y zj3dPESpN0>W6Lw+r?Sim#>e{k<-#N6%bDZw(cIu39#I4lUh7jLd=V(G^QrGS4v_;I z%p4(yG@Lm`j%YMHoN>k*lCRM&)FvgW^bqCsB_Pc$F-tKzYaE108+ai@W zr8T^-+%@FI1H6F&-N0H4*kb0iA59B~cUZ@JOrOKn8s1oDpa*W5kb^byb+mG$2Rtw8 zhU?W|G4#nGJE^$2ud*p|Z7Ig+$x(mCeTWI1giaz9njacAvCAeXpTuO2o&20(9R+u{ f%X!cxV^T}Y7>+lkSKUU83a;IpLxGZd#sB&RPV#ER diff --git a/src/ScaleHD/genHTML/__pycache__/__generateHTML.cpython-37.pyc b/src/ScaleHD/genHTML/__pycache__/__generateHTML.cpython-37.pyc deleted file mode 100644 index 95ac1047d557e77815e8e7b01300d60bfdb70b6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22832 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 P>VIlN;64v0hWq~lM713X diff --git a/src/ScaleHD/genHTML/__pycache__/__generateHTML.cpython-38.pyc b/src/ScaleHD/genHTML/__pycache__/__generateHTML.cpython-38.pyc deleted file mode 100644 index a9c555a792c32ed12559a0493dadfbe3bcb966d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22861 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 ZPq9w<$T)lig=6EVEW|ioM4Pbt{{b>z$`1el diff --git a/src/ScaleHD/genHTML/__pycache__/__init__.cpython-37.pyc b/src/ScaleHD/genHTML/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 721699c8202db0795db66bf2cd6f76a46e899680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmZ?b<>g`kf*#MXI2R!O7{q}ACLqHBh>OL5L<&PXV-!OQV+vCaV=hw^6C*gk6uCLOGco2O~za7Kyi?epCMIcL7 zG8C}@DKPO%IygBoC)LA6AF5kFK0Y%qvm`zqX!0!%o80`A(wtN~kQK!sy=;sCU*0Y9 diff --git a/src/ScaleHD/genHTML/__pycache__/__init__.cpython-38.pyc b/src/ScaleHD/genHTML/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 449f3341345814425dfc1de74ac3290e8e1535cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200 zcmWIL<>g`kf*#MXI2R!O7{oyaOhAqU5EqL9i4=x(#wdmq#uTO;#$2W-CPs!7=3oX* zmMX#c`1I7g)S|?aRF4o}AH9OgmyAI5nvA#Df#M(`KTYOa?D6r5IXUt1QGBS%i$IpF zWGG?*QefhjbZ~NFPO68CK2*1Ue0*kJW=VWJ(BxYjHo5sJr8%i~AS;SNdf6BOdlD`M diff --git a/src/ScaleHD/predict/__pycache__/__init__.cpython-37.pyc b/src/ScaleHD/predict/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 1c6bb837f0c2a728613992f345233e44eaca7e28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 314 zcmYjLJ5B>J5VgIZL?{u6mJ@74a)1Z{iUbtUM51uPXtf@#EP1__?G)h*T!fy}Y)i!z zsF*<@7|YMkZ$|Up^>TS6s9x?KpWf(w=g)5`BA0a2n!pGnd!>a@rf_mlT1n=mD~3{+ z-@GEd(iQP~Cz{#wF+hlDY?tlm&%=R*A$NwFk&6|rW{@OieQ gNO-$l{iIz1SWhlM5vTcP7|q_{1!qk>KbH$SUr}FE{Qv*} diff --git a/src/ScaleHD/predict/__pycache__/__init__.cpython-38.pyc b/src/ScaleHD/predict/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 0a20a483ad5702ed0ad83aa94d37a442edbd6ad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmYjLJ5B>J5VgIZL{TCTEhpH9aDWJb6bUGxiJ)-7Xtf@#EP1__?G)h*T!fy}Y)i!z zsF*=WWGp|wc^{||ooHsSCjcR$v0b*KzX%5wk`G<$ob^2kUy_U)Ii21(hYoMikNFVjkF>Kb zW{iXN_^{6{J0zY=!PyL5+e2<+kNL5wrx^fQ0_f%O!pQMi`d=9fl442lE@I7`;ro0? hk??N2{!6S`~Y_qR1g3F diff --git a/src/ScaleHD/predict/__pycache__/__prediction.cpython-37.pyc b/src/ScaleHD/predict/__pycache__/__prediction.cpython-37.pyc deleted file mode 100644 index a4f9b0f90f138e6365cf78dec106fc5f99395183..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45897 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 diff --git a/src/ScaleHD/predict/__pycache__/__snpcalling.cpython-37.pyc b/src/ScaleHD/predict/__pycache__/__snpcalling.cpython-37.pyc deleted file mode 100644 index 129920c1ef4ac3d0e307c5fb61afa68fbc6996f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5803 zcmcIo%X8bt8OH*AfZ#(CC0X(_X!=N$#I(mrCmB!bu_Hg?)K(PPO){K17>El}kU)T5 zfO^nid$3!lGtJcPu_vl<(!w*IvKyM(slHjP2QBWV@o)ws&_N z&%4|Lp%v`Z>_)A-^%TODl_i$smIE*7iUvK8e=0hcQNlM+ISSNMpc<5Xq(C)|XV7L+ z;q@%fa~;n)Uf>4WJTF3#Qc}I@M2={?zH_@9*^wLgVS`MkAGIW7872GcAL2;@sQXJgim4`H4*jt*v~8wUx28|AT#y z`+u~L_Q(l%b!y_NV^|6J4*r_v3vyMaVr{7PXW+eQ@?O6xS7?S-D5ida)B4QDvvPgr z(2mz<4(p?ezaOFh;t(!0&W79AFzIUyP5Q!vZfHguuHYtuL9}N^L4Xn0Hv_>P zfk(+}-dR}<%^eqs42!oNGb^;uZ+eZj`W{r=&}w zUx`vZdnX_Ct?R;Z-m&+b@WV8iF2nD&?MAa_HZihJBj(vADSY-F^NsW8&zonLQ}uhb z0~$@H`3j}xCrk(RS~@KQbTZR&BC8|Zwk`Ic1EP2`OQg_g1U}y<7Kqr~!4f2uv}=c< z(_Z)X5`&&M92?srnWFwEzIW_s11Y4t-Vs5=3Bx428gv{#$*kU7y_RHlY&S}#i9eN# z)Dv~V6m~oeWHtlWPt+hxGIE}r8#Y}imum*1ZAVG55wzP~AO4PoeoWi6866hy<(ac~sItS#+9i{d6kE{t`L!2TZr34dH|vfybflc-L9! zem+PUDKd442cqWo2?y*f?^B_X%=~FpqK{2SbsQ!1P$~H{ny%M`~$ zR#8jLpl6I3>;!r=x|pdjj2=Cv8ElSaz)gRp=idaa_wo_=;Ydg`0TZ*(kk+1{R@ed9 ziU>lmO~5!Lb|m+Q>}N=WyLUm=2tddd0Ad6%F7_GM`aqj~jc5Cr7T^R*4k;ASg6FnW zQAa%15;Or%AyzT|mpD795b!{<_!VHyr2ml;O#v#Dco_(}!lwXx zjK^w|@o7HuNRN%H@Ic?-RXi!9r(!f68xNs3!J)7h&G1<{S|nchi$&GvD9U4o8Rrhp z_*emD{vhShtbc{Y!0P=nN@e#VNxpZXKNXkxvB7LSwK<0}Po6`2Y^c4d0NhlNx@l}- z$nacw!tVe2BLXvvaakry2Jn-iozjz#0fL$vBALt7J%Z&jEhBK4VbF|rY-BE^YQ!ak zo7X6zhX+!=>v^WL8wtBX>6+5_2*yW9cWIy{XwVcv+eEMn8m>&^>2V=6HxR*U=7&i6 z+yz1inx;*7z$6*2hocKp{*@Kk%`1Y;CKp~LWqx}}ah3Qf10i)IQ>{i&Dx_rM21*1Ky(T##;Q)KWp z_q3IBS0qFbRXmCls1j}Y%8i7H6+wrHNf4miP2~ z(dmzjp)eJa1uf`yuS%zyP32Q%D<^sbDLawFf+ zF-=gM{@i%mbHfNZwsYyncn(QePU*e*AKEaUwE92>3?6U~D0j^hD z>{A6t0o+M^hC7JkN;p}DCG9C4ifQB6QQ*l92r#Ppoh0vu5^jihiQ$0NVAjJnfe)nN z!^S@b>rux4*V>7jQakY;nvrti`_#Wh)e)NIJzKQCsMx1qAMcpbgZ|k|5BisZWKFY| zO`Bsk#W>K6@NY<1SuEnk-b>fVSmYuh-tS2ee2MDIsA}d1_?~GbVaC!6iRSu|yk#In zNO8gP$_dx5T~D+tx9&=J$qz1>B5*nS(&&7JJCl7v z*a(CpUdFp(iK>^VGO2nU)zYj)pW;pQlUzjJj_Q?t^=P{K0bnL)tIzB+04g^&9A2N@ zCw2WX@c`<>0gd2$P5|}`n26NUa{WUnFg3c+2|{EYTt(FnAU*8OdZj(Oq_Rk>us$_; zmRg00J|-kaY6+G~Bv}AXfJq~jOsF^gG&Mn&d*U`#*Qt{Dv4Eb15`Kb8DbBGoS_V-t z!>E*i7fVVNNYDTlEGapZMFyH$$y8Z2TLFr!VjO=!n(2E6I|Wn;%m@T&Xmh}hgde9d zNAH{eMzCv8V5A?lRF+ZX)m3^QC-2-SR|xtl@nI97te0FLXU5UZ+H-LYCnmW~>C){t zG?|pZdZ&-lX2Lm%e&c*4^>{8{r8=;5OUYO=2`K3yF1_me?bk?6KO@p_Wl5(?J37Hw zR$^FIJK$Z9`X$Svyqk^)y3do)U(ouA7F8Qm?JF_SL{NI5wU%DQ;h>~n*Ozrw(wE9f z(XuEHQihiz?QXOY2+K;pA<8S_8>o`3-)(pHWc*6#kc92r5@%_oU|H*SV+)_MAm55} cRFTx9)Ilasjk-j|WnHWhk}{z~e<0LHT>yZ`_I diff --git a/src/ScaleHD/predict/__pycache__/__snpcalling.cpython-38.pyc b/src/ScaleHD/predict/__pycache__/__snpcalling.cpython-38.pyc deleted file mode 100644 index 43aa2304460382b90c817caf5fd1ab7200a775ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5821 zcmcIoTXWmS6~+P}30@^pl4Z+wf~IW}B{A)Bl1V3%x^`qcaqGy6?6?_D9Sp<;DM%nd zFF?tn!FHy0Yj-A@y8nRcbUO88|3-fT9{SRkdZurE%R}AoEJ%v7<@7eCw6}wUJ(pda z^X-0CE*BL1zPxhti$A=iD1V{B_)A-^#sC|l_i#Bmjf^8iUy4*e<}v&QNnjnISN!%pdJ*xuRuMG>uB{< zfqI7LxPft&=Xn8bju)X!DXCs`B1g1c-}$5)*^wLgVS~&c{isiJmQli2QN=7)EG1S^ zGfsW<2HKv&6tp_ET*1uhN}?=flG)I?-F5ti(}9^*u)gUuqGZbTVK#_X#};-wObVgj zv4qnJM3l_89N!UkzA)LY|r`l;^~fXxZ8+MTNdUA&2#-$t+SgLmgV|xWLagfhjJ^hto$=r@A&f$sg3v> z3@?m+^XSmK(la{z6f2v`fbFHNx~DYNi^?PQ)?fQ9X5^v1iWVN~YiKiQb@@gXy-bwb zG-%~wwWXn-r^2e zabZw}jzzRm6JCjnTUpW~njOsXsliM%zj=gDqh8=Md-@(@ij-20GrWqKOVQD|#AolR zzka1(j?05%agp>TzTs#t9sO8&SNT*~Q$EKolh)3|T2;I?wrSGZ$~RbB8Cm;3*ax}) zNBd~6EW)c(V^1ByO2BvU*EC;{t1=a9ds=@6-kT=x^{aA)W@v?C>gPGF&uly^*Jlpx zXnp3eKC1ZJA^IQPgN%vwIdK>lt_Z>0d7!Zg9&odv9L0 zU9T$~vV8T*@`bgFHFJ5xX>6I$j64ZdI^D?hf>y0pU6pb!2@!~^AVhbfgzY>bT@vF$ zl{_IdkTWd1^UTzgL^k zXgtj~C^bK3n$&CQqzuo=Ov{O^j&R$y*o6*=;mItKLZ=bQ-y`orknv7-$nknVa%1Pvz)lgw(+ar{JIy|Q{K(RXY&N~VcFm5am^ zb-@&NJUq}h1J_T~AWU>QPu2~aE|kkQ1JSmlq}T}B?XC}hM^2)_`$-0yArOhy@K7PY zB%0&%M3*8nJHiFu0!c&03D!eLY|EUwZF^lOsfKtk_Piw}Wg1=(I!p6{Qc03cm8@r* zI~JT!FQOfLAUm|)Aj?VJ`n24w*1qxe6RywQZELs*kS&{zp!93`cH61vX`dqL;?;7x zS*7WccO}BnB6=T0Z&isVwRE4-Emhn^KZgWFpoC~S(4v$!FgV}=&j3=WQGQT}ilCRklfS3#v3@SjT~u!U zq;Eh%e)u-c`1{z{ERQKP-=B!GKN~ObB7qg~WO9&{#n6pCnu&&sDo;)S1BRDFh` zJYbk{ZgR#43MliFlmoLSuooDHCo&}J~$!Q^(1_Mlz;(NVwxWVkh*FIh1*nBPa`dnSM2%8N3u%`SAq_wjQasC50a+N&8^y_UpWW)P2H! z133iw@FREIN%L$M5eymFHxau71UENCQkN-w2+n1CMgTFxpc(Dh$Y4m>h))PRZ&6|o zCsM!bd8TtG5_W^qHl^_)n2(V5(tt}4p(%p4iJ%uWT$#?(Lqlk8Ad=P0n@IiK1yTr_ zrcJoOBpI%ULkv>?wriUWN8n85OA*7!1MD!w9lp8lm|@3hxJ?)Q>p&dPLz+2+7Dz}< zx7Ec7gp~RGldA^-Lm)9o`+Ec%riUpAj8@aLTVZeN{Ov9jjCQR?Hwv0fq2t@_X)C8M zNT?vH7>Y$yiMD*1P+N;z-EsXJTGr zeBjybb#9;gDPH+Iiqd=ckg)UYF{<`~7Xa3T=g>*tzX9~4Z8^J1PB`0+K#C@9Mn5_W zl2H_#Lb9X<{cct1jI*gcRJL@X$Lu%iZT)(B8WTSN&to-FYiAO62bxz#$GV}1=*T80 zYA1ycJU5JxaXaUJhOx9gi||@ddhh**mW<|kPIZc<(y_D>QuRWr`h?0*Qd_-tU0eps zGhP-y1noXbNIoFtUMzuNpOj#bgo*jnIbSsUFTZj29G*(ozcHHql|GA*72tlg#U3g+ z9N@O%E8I>TgOVe;kF_UsOonB;9R;4;jE}WM^*c$<4J8B-KLUfa8C*u#CM1HKJYF7Y z`UCKc*Jbf}dpMX8 zY|je7eE}1ZN?NXe0R5##=Q}}&EQ3p^`UJwmHmq0L!|N%Fv>5k${oF=35o(@jjAhDN!*x6&q4_~Dy2Bb7B%2SMJI&FO2Bm`r3%z% z0QZ%YEXq;zOKM54vTCLR)L6xH`~h92?-}d__<=it77N-OkR|wOHXub^t+L*?|Bb-b zpqM-QQJ=~(ioCi?@8jssO`HSmF%$F=E~#>9>m}F6xp8>6_DnFtsYz~Gx_0|5O(*5B z-pRvs8gm{9c%xh;waH?RG*?MYghzU?bc@L_GL9wbVJ^M!``y2goPJiMPtm5zr)pn`u_l5N0B@zkhu#2Zoy!RAw;*YK~T{u6LKB254Q diff --git a/src/ScaleHD/seq_qc/__pycache__/__init__.cpython-37.pyc b/src/ScaleHD/seq_qc/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index d365295c9d7c2ffe46978cd76fa21279b3395ac5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmZ?b<>g`kf?m(CxC$Ws7{q}ACLqHBh>MkgL<&PXV-!OQV+vCaV=hw^6C;St3}iD$ zF$38w!3>(LRU+~6g{6r(nI)C+$@zIDMfo{;1(h!uf!Z}0Z?Oia76v*48G=rUCCM3y zDTxIo;YEoB1*t`Tnk={2g`kf?m(CxC$Ws7{oyaOhAqU5Em-}i4=x(#wdmq#uTO;#$2W-CPomO8OUai zVg|BVf*CYft3=}C3riDoGD|Aslk@XRit=;x3MyYR0<~*0-eL_-Eev!9G6bCxOOi7Z zQxXeG!iy3M3Q~*wG+A!3$Hyn;J!O4j^sU9x+ p#i@nyg~|Hy@tJvF)X6UH=9WCc%UZuwxT`6#^`=<0Jw$#$I7)z)gFqcRcIa>DlTY z?|L#LkTwz=h-3)~ahydU0e3D4MM!Yulw%8VflF?jeSz;)bnui+c=Yx<`ATE301 z*)Q!nzRNV^E#y7llX<&e-mUl*rbX4La;P0Lzt+|K`fD05ap!G~JBPa8K*{AEN*+op z+_51j)A-hjuu!G zD%+gemAb@nBy}go%sawy-q*$EM4Oe5G^XV|1H2vZDw3zos`TX=R%C8o&zuKgzsJKg z%FI;sc3ZVZ%M4qlpQJK8nvdsn9VM4vIvj#%<(nbYSv zW1cgQm~gmtOWSj9LtliS>NA7eGc&bzY+eGzwK+m7$lHayz0I-F-rgJAZFw^E-D`Kp zV^@`t-5NV@P1WDnPVVs_XpX=5#^pC(YQ8a$iqa%WH@9Q48>USjr8vTfH+uv8L&ojq z)q7FrzA7|Hb=uckjwoYmnG+3ovfqPV82z}LSyJYit%k~sPG4k(hi+RguWH zkr{yatUPvV#0;x&VBt6 z6U~G7QiHpxiB{GjJJcc6cc-#6!FV&9dq<3yWv+spixDhdot4nu;kAjy>qq9)UYetU z7Nv=Wo-3*+U&Y1#BU-pKZ(>U!uT2boW{n-n(-fN;LKIU7km4T}&RagX<^v`G`S%|DT;+z)s8j z;s4LbX$P)jvxy?cfVpQH9={vpZzCvRfx zXD5~O#65AE*pb@*(s@oW2Ily$=ZU^wLpyhN{6uweS8`7|QQ8C6<~?G2*N>g;b66?# z4QG4vl(T*L|2kW#MN4OP@~m<3mTH&J+7MTkYSdYuJU>I+kh{4sGDFvRvLvM|JC zSr$Ck-6zi5Jzak>>L27>$Isgk?@8_{=lw|E`^9YywRQestjZK;(#C9sUz(ND)txo| z5zsUI`6H9GJv}=+$3IFr=_p^=?2wV5pNnK6k;zGZ0+NzqvMxOGdCKdQxc-g-gJ8^z zBW-Hpa>0y{2cJTX$ehT$je3iGacb|BW)3_%6#Tq)q)$t!J1KGdP{-CCTyBdNB6ISZ zBd7ek&9(ck_%X+Y4=R%qEWERsIdFy{4~J<>&+H`S@V@d3dn?HDy-#Gk7Hs9t_oj5U z?erkc*#1_lnt93S?oh;?C`mHwMm&rLnR(-l8?R>NPQ1H28uU7Z7Hl4MVh+@>ANJBr zkCV)lPT$;#d*~p$S^J`grdFpPCy}UQ-j?gv0SZRD(I8ENWC*Y7SIMFe!rdqkag1UT z?hgA=jvD;6!mS63W%+Bx9s?2X7cJ`xrreUhU?_ayHx!Nt0QcaxV)4)~=Nwu>i%F-m za4QRFQH=Z|wvdp=(coLiyTiZhU_HNo`{lrai3=_$n=|^9P8^5(jHPQUcj#XlGLn7jH?@YPOd7` zuoQUm{ZR0rXwuja5&SOvcfm2%!%G+|e9SJ5jrHyw*8c3fVzt2iv_aL=LRCii*s_L3Ja5kZ7QQ zi6zxgd_q#q-#K_$ZN= zhQ}Da5}zQ}Wg@Q;k#}f?a$f~$*-FU?nZwGfP)=#CU(1Vyqp?-2=(|c)ul2-9jDHrR z{1hpn3#XZ+M-ICLt9t>uH$HP*=4$l0#OWl+DoBY_j6f1cVqG0hlWpP@X;eW=FVvkv z!6jc%Re`NqUUG!WM22x}yr!l;R!?KHt(HS!qI7LR<^{oS%tyG&K~_;ArK%AhpqU6y z2)bQ@HY&bJWU&O9lNXS%2en8ja&dI8>UmD&)CBQ6Nb?`9ZUZTyb99;?!Dl;jaoteU@7C}7Sm(p!YiiQ>Tr6wf`N zmZW&{LB1qw9Z6kDJxR-_zD*dLV|RlhNb=U=%b2iLlO|nULq2ngD)>hA%0Tw8e!>T3 zje;?Zlb(zq>nEI222h~9)VztOjIbb>Q^xy55Tijq9A+LB;5WMPRvw<0ioARtQOm&d z2#)Ra{#@fI99EDAx$YrlKQWZay((fjukHl^Dhv^_^BskGJi=prr&f$rEuOw zhV+|YDdHtviDrodFiK)t;sBD8$d)*Wv`BnQ9K^if*h?Hlz2KCVIF95vOB}?tXyc|H zc;!RJz4KJAq*ZD4v>n90$?wzZtbuyubG%*N%vP3;F-Ja6LX4F;=qQ8-98$=$KpiV{ z&b(1M$4w;Sab;sk#^bUqDIrr~A>~@Iw4{i)Ru<|gzK1gxbSp$aWJ5>BGk-4eOY@mW9O)_UUydOc8lhe(Rh|ukKND1kc25)=(uK_I@KmDY}ka5Zt zbgndH2MLo)rTHODBxh5Yhbf{L#4b=Eye1mr739FB95<$ja!@kyeDET-WGlvPl(ueS(Ja*_)QYKH1{cGgPDu-D}5zBIxpYw`r zRhF~-g;%Hcsk78h&VtvlzwtU;x!ei4@`)+_DLHahaA*Z3BvRK3&5ehKRcgVNGNKkW zc;%HfpO7^x#6e$&ucF-=9$VnP@yvpt5|F^+=a6gg^Pnq}(!@EcPF)HvCoVTAyp$p4 zg?CImqjhmc{Bdrhc$Zt! zO~S&t@;8B0Q6y z9AxK_FNK}T=g59VVcxG`JkhU)G!tfO+()Adh4y=cc1SI6hE0% z-y^aG;=5ZngPX73c=P6##A|XHr>!}2URGG~OEiH*2smaFi>E)6BBYA$F;lPoKx_OP DIC4EZ diff --git a/src/ScaleHD/seq_qc/__pycache__/__quality_control.cpython-38.pyc b/src/ScaleHD/seq_qc/__pycache__/__quality_control.cpython-38.pyc deleted file mode 100644 index 2e70e363fb233066f6eeabc5b648ef05f404a9e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7073 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 Date: Mon, 18 Nov 2024 13:11:07 +0000 Subject: [PATCH 02/41] fix fasta file extension --- src/ScaleHD/config/{4k-HD-INTER.fas => 4k-HD-INTER.fasta} | 0 src/docs/DataAssumptions.rst | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/ScaleHD/config/{4k-HD-INTER.fas => 4k-HD-INTER.fasta} (100%) diff --git a/src/ScaleHD/config/4k-HD-INTER.fas b/src/ScaleHD/config/4k-HD-INTER.fasta similarity index 100% rename from src/ScaleHD/config/4k-HD-INTER.fas rename to src/ScaleHD/config/4k-HD-INTER.fasta diff --git a/src/docs/DataAssumptions.rst b/src/docs/DataAssumptions.rst index 3fe4e9d..a417d0c 100644 --- a/src/docs/DataAssumptions.rst +++ b/src/docs/DataAssumptions.rst @@ -60,7 +60,7 @@ For the *reverse* reference library, we utilise a static 100CAG with varying CCG Basically, the only thing that is able to be altered about a user's input reference library is the forward and reverse flanks of the sequence. This is an intentional design decision, and ScaleHD will not function properly with alternatively styled reference libraries. Please see my other software, RefGeneratr, at https://github.com/helloabunai/RefGeneratr. This package allows for generating custom reference libraries with ease, and is actually installed as a dependency for ScaleHD. -An example of valid reference libraries can be seen on ScaleHD's github page, under /src/ScaleHD/config/4k-HD-INTER.fas and /src/ScaleHD/config/20TypicalReverse.fasta. Valid file extensions are: +An example of valid reference libraries can be seen on ScaleHD's github page, under /src/ScaleHD/config/4k-HD-INTER.fasta and /src/ScaleHD/config/20TypicalReverse.fasta. Valid file extensions are: * reference_name.fasta * reference_name.fas From c91f15d68c8de761660407ee2907fc4eec28789e Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 18 Nov 2024 13:23:49 +0000 Subject: [PATCH 03/41] upgrade to python3.8 --- src/setup.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/setup.py b/src/setup.py index 20f877a..822abcb 100755 --- a/src/setup.py +++ b/src/setup.py @@ -18,7 +18,7 @@ description='Automated DNA micro-satellite genotyping.', long_description=long_description, long_description_content_type='text/markdown', - python_requires='>=3.7', + python_requires='>=3.8', # The project's main homepage. url='https://github.com/helloabunai/ScaleHD', @@ -48,7 +48,7 @@ # Specific version of the python interpreter that are supported # by this package. - 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ## And so on 'Environment :: Console', @@ -77,7 +77,7 @@ 'numpy', 'seaborn', 'matplotlib', - 'sklearn', + 'scikit-learn', 'scipy', 'peakutils', 'pandas', @@ -86,9 +86,11 @@ 'PyPDF2', 'reportlab', 'generatr==1.0', - 'batchadapt==1.0', + 'batchadapt==1.2', # 1.0 was yanked + 'cutadapt==1.18', 'pyvcf', - 'fadapa' + 'fadapa', + 'setuptools' ], # These are the data files to be included in the package From 6f7c8c8af55c32f8f8c2a8b1df5071a333d0adec Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 18 Nov 2024 13:40:19 +0000 Subject: [PATCH 04/41] update PyPDF2 syntax --- src/ScaleHD/predict/__prediction.py | 19 ++++++++++--------- src/ScaleHD/sherpa.py | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/ScaleHD/predict/__prediction.py b/src/ScaleHD/predict/__prediction.py index 04ad853..9d667d4 100755 --- a/src/ScaleHD/predict/__prediction.py +++ b/src/ScaleHD/predict/__prediction.py @@ -1263,23 +1263,24 @@ def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hpl ## ## Readers and pages - line_reader = PyPDF2.PdfFileReader(open(graph_list[0], 'rb')); line_page = line_reader.getPage(0) - bar_reader = PyPDF2.PdfFileReader(open(graph_list[1], 'rb')); bar_page = bar_reader.getPage(0) + line_reader = PyPDF2.PdfReader(open(graph_list[0], 'rb')); line_page = line_reader.pages[0] + bar_reader = PyPDF2.PdfReader(open(graph_list[1], 'rb')); bar_page = bar_reader.pages[0] ## ## Create new page (double width), append bar and line pages side-by-side - translated_page = PyPDF2.pdf.PageObject.createBlankPage(None, bar_page.mediaBox.getWidth()*2, bar_page.mediaBox.getHeight()) - translated_page.mergeScaledTranslatedPage(bar_page, 1, 720, 0) - translated_page.mergePage(line_page) - + translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) + bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) + bar_page.add_transformation(bar_transformation) + translated_page.merge_page(bar_page) + translated_page.merge_page(line_page) ## ## Write to one PDF if hplus: suffix = 'AtypicalHomozyg' else: suffix = '' if not header: output_path = os.path.join(prediction_path, 'CCG{}CAGDetection_{}.pdf'.format(ccg_val, suffix)) else: output_path = os.path.join(prediction_path, 'IntroCCG.pdf') - writer = PyPDF2.PdfFileWriter() - writer.addPage(translated_page) + writer = PyPDF2.PdfWriter() + writer.add_page(translated_page) with open(output_path, 'wb') as f: writer.write(f) @@ -1494,7 +1495,7 @@ def idfun(x): return x ## ## Merge alleles together - merger = PyPDF2.PdfFileMerger() + merger = PyPDF2.PdfMerger() for pdf in uniques: merger.append(pdf) merger.write(sample_pdf_path) diff --git a/src/ScaleHD/sherpa.py b/src/ScaleHD/sherpa.py index bc75b95..735f5ca 100755 --- a/src/ScaleHD/sherpa.py +++ b/src/ScaleHD/sherpa.py @@ -476,10 +476,10 @@ def collate_graphs(self, sequencepair_object): ## ## Merge sample summary PDF with instance-wide PDF - merger = PyPDF2.PdfFileMerger() + merger = PyPDF2.PdfMerger() for filename in [self.instance_graphs, sample_pdf_path]: with open(filename, 'rb') as outfi: - merger.append(PyPDF2.PdfFileReader(outfi)) + merger.append(PyPDF2.PdfReader(outfi)) merger.write(instance_path) def append_report(self, sequencepair_object): From 66a9ba2e9c0f81c80c381da4be2d63799f160b49 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 18 Nov 2024 13:41:17 +0000 Subject: [PATCH 05/41] simplify PyPDF2 page merging --- src/ScaleHD/predict/__prediction.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ScaleHD/predict/__prediction.py b/src/ScaleHD/predict/__prediction.py index 9d667d4..84063fc 100755 --- a/src/ScaleHD/predict/__prediction.py +++ b/src/ScaleHD/predict/__prediction.py @@ -1267,12 +1267,12 @@ def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hpl bar_reader = PyPDF2.PdfReader(open(graph_list[1], 'rb')); bar_page = bar_reader.pages[0] ## - ## Create new page (double width), append bar and line pages side-by-side - translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) - bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) - bar_page.add_transformation(bar_transformation) - translated_page.merge_page(bar_page) - translated_page.merge_page(line_page) + ## Create new page (double width), append bar and line pages side-by-side (CHANGED MW) + # translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) + # bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) + # bar_page.add_transformation(bar_transformation) + # translated_page.merge_page(bar_page) + # translated_page.merge_page(line_page) ## ## Write to one PDF if hplus: suffix = 'AtypicalHomozyg' @@ -1280,7 +1280,8 @@ def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hpl if not header: output_path = os.path.join(prediction_path, 'CCG{}CAGDetection_{}.pdf'.format(ccg_val, suffix)) else: output_path = os.path.join(prediction_path, 'IntroCCG.pdf') writer = PyPDF2.PdfWriter() - writer.add_page(translated_page) + writer.add_page(line_page) + writer.add_page(bar_page) with open(output_path, 'wb') as f: writer.write(f) @@ -1495,7 +1496,7 @@ def idfun(x): return x ## ## Merge alleles together - merger = PyPDF2.PdfMerger() + merger = PyPDF2.PdfMerger() for pdf in uniques: merger.append(pdf) merger.write(sample_pdf_path) From 508895e749cc38c094932923fa3e9fb4e802ae29 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 18 Nov 2024 13:41:39 +0000 Subject: [PATCH 06/41] fix LinearSVC loss arg --- src/ScaleHD/predict/__prediction.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ScaleHD/predict/__prediction.py b/src/ScaleHD/predict/__prediction.py index 84063fc..acc32b2 100755 --- a/src/ScaleHD/predict/__prediction.py +++ b/src/ScaleHD/predict/__prediction.py @@ -96,7 +96,7 @@ def build_zygosity_model(self): ## ## Classifier object and relevant parameters for our CCG prediction - svc_object = svm.LinearSVC(C=1.0, loss='ovr', penalty='l2', dual=False, + svc_object = svm.LinearSVC(C=1.0, loss='hinge', penalty='l2', dual=False, tol=1e-4, multi_class='crammer_singer', fit_intercept=True, intercept_scaling=1, verbose=0, random_state=0, max_iter=100000) @@ -1267,12 +1267,12 @@ def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hpl bar_reader = PyPDF2.PdfReader(open(graph_list[1], 'rb')); bar_page = bar_reader.pages[0] ## - ## Create new page (double width), append bar and line pages side-by-side (CHANGED MW) - # translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) - # bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) - # bar_page.add_transformation(bar_transformation) - # translated_page.merge_page(bar_page) - # translated_page.merge_page(line_page) + ## Create new page (double width), append bar and line pages side-by-side (CHANGED MW) + # translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) + # bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) + # bar_page.add_transformation(bar_transformation) + # translated_page.merge_page(bar_page) + # translated_page.merge_page(line_page) ## ## Write to one PDF if hplus: suffix = 'AtypicalHomozyg' @@ -1280,8 +1280,8 @@ def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hpl if not header: output_path = os.path.join(prediction_path, 'CCG{}CAGDetection_{}.pdf'.format(ccg_val, suffix)) else: output_path = os.path.join(prediction_path, 'IntroCCG.pdf') writer = PyPDF2.PdfWriter() - writer.add_page(line_page) - writer.add_page(bar_page) + writer.add_page(line_page) + writer.add_page(bar_page) with open(output_path, 'wb') as f: writer.write(f) From 836355533c48c002bb5ffc65e192e676b56adccc Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 18 Nov 2024 13:41:53 +0000 Subject: [PATCH 07/41] plotting fixes --- src/ScaleHD/predict/__prediction.py | 3594 +++++++++++++-------------- 1 file changed, 1797 insertions(+), 1797 deletions(-) diff --git a/src/ScaleHD/predict/__prediction.py b/src/ScaleHD/predict/__prediction.py index acc32b2..74447ba 100755 --- a/src/ScaleHD/predict/__prediction.py +++ b/src/ScaleHD/predict/__prediction.py @@ -1,1798 +1,1798 @@ - - -#/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' - -## -## Generic imports -import os -import csv -import PyPDF2 -import warnings -import peakutils -import matplotlib -import collections -import numpy as np -import scipy as sp -matplotlib.use('Agg') -import logging as log -import seaborn as sns -from sklearn import svm -import scipy.stats as st -import matplotlib.pyplot as plt -from sklearn import preprocessing -from reportlab.pdfgen import canvas -from peakutils.plot import plot as pplot -from sklearn.multiclass import OutputCodeClassifier - -## -## Backend Junk -from ..__backend import DataLoader -from ..__backend import Colour as clr - -def split_cag_target(input_distribution): - """ - Function to gather the relevant CAG distribution for the specified CCG value - We gather this information from the forward distribution of this sample pair as CCG reads are - of higher quality in the forward sequencing direction. - We split the entire fw_dist into contigs/bins for each CCG (4000 -> 200*20) - :param input_distribution: input forward distribution (4000d) - :param ccg_target: target value we want to select the 200 values for - :return: the sliced CAG distribution for our specified CCG value - """ - - cag_split = [input_distribution[i:i + 200] for i in range(0, len(input_distribution), 200)] - distribution_dict = collections.OrderedDict() - for i in range(0, len(cag_split)): - distribution_dict['CCG' + str(i + 1)] = cag_split[i] - - # current_target_distribution = distribution_dict['CCG' + str(ccg_target)] - return distribution_dict - -class AlleleGenotyping: - def __init__(self, sequencepair_object, instance_params, training_data, atypical_logic=None, padded_target=None): - - ## - ## Allele objects and instance data - self.sequencepair_object = sequencepair_object - self.instance_params = instance_params - self.training_data = training_data - self.invalid_data = atypical_logic - self.padded_target = padded_target - self.allele_report = '' - self.warning_triggered = False - - ## - ## Constructs that will be updated with each allele process - self.classifier, self.encoder = self.build_zygosity_model() - self.allele_flags = {}; self.forward_distribution = None; self.reverse_distribution = None - self.primary_original = None; self.secondary_original = None - self.forward_aggregate = None; self.reverse_aggregate = None - self.expected_zygstate = None; self.zygosity_state = None - self.pass_vld = True; self.ccg_sum = [] - - ## - ## Genotype! - if not self.allele_validation(): raise Exception('Allele(s) failed validation. Cannot genotype..') - if not self.determine_ccg(): raise Exception('CCG Genotyping failure. Cannot genotype..') - if not self.determine_cag(): raise Exception('CAG Genotyping failure. Cannot genotype..') - if not self.genotype_validation(): raise Exception('Genotype failed validation. Cannot genotype..') - if not self.inspect_peaks(): - log.warning('{}{}{}{}'.format(clr.red, 'shd__ ', clr.end, '1+ allele(s) failed peak validation. Precision not guaranteed.')) - self.warning_triggered = True - self.sequencepair_object.set_peakinspection_warning(True) - self.n_align_dist() - self.calculate_score() - self.contextualise() - self.render_graphs() - self.set_report() - - def build_zygosity_model(self): - """ - Function to build a SVM (wrapped into OvO class) for determining CCG zygosity - :return: svm object wrapped into OvO, class-label hash-encoder object - """ - - ## - ## Classifier object and relevant parameters for our CCG prediction + + +#/usr/bin/python +__version__ = '1.0' +__author__ = 'alastair.maxwell@glasgow.ac.uk' + +## +## Generic imports +import os +import csv +import PyPDF2 +import warnings +import peakutils +import matplotlib +import collections +import numpy as np +import scipy as sp +matplotlib.use('Agg') +import logging as log +import seaborn as sns +from sklearn import svm +import scipy.stats as st +import matplotlib.pyplot as plt +from sklearn import preprocessing +from reportlab.pdfgen import canvas +from peakutils.plot import plot as pplot +from sklearn.multiclass import OutputCodeClassifier + +## +## Backend Junk +from ..__backend import DataLoader +from ..__backend import Colour as clr + +def split_cag_target(input_distribution): + """ + Function to gather the relevant CAG distribution for the specified CCG value + We gather this information from the forward distribution of this sample pair as CCG reads are + of higher quality in the forward sequencing direction. + We split the entire fw_dist into contigs/bins for each CCG (4000 -> 200*20) + :param input_distribution: input forward distribution (4000d) + :param ccg_target: target value we want to select the 200 values for + :return: the sliced CAG distribution for our specified CCG value + """ + + cag_split = [input_distribution[i:i + 200] for i in range(0, len(input_distribution), 200)] + distribution_dict = collections.OrderedDict() + for i in range(0, len(cag_split)): + distribution_dict['CCG' + str(i + 1)] = cag_split[i] + + # current_target_distribution = distribution_dict['CCG' + str(ccg_target)] + return distribution_dict + +class AlleleGenotyping: + def __init__(self, sequencepair_object, instance_params, training_data, atypical_logic=None, padded_target=None): + + ## + ## Allele objects and instance data + self.sequencepair_object = sequencepair_object + self.instance_params = instance_params + self.training_data = training_data + self.invalid_data = atypical_logic + self.padded_target = padded_target + self.allele_report = '' + self.warning_triggered = False + + ## + ## Constructs that will be updated with each allele process + self.classifier, self.encoder = self.build_zygosity_model() + self.allele_flags = {}; self.forward_distribution = None; self.reverse_distribution = None + self.primary_original = None; self.secondary_original = None + self.forward_aggregate = None; self.reverse_aggregate = None + self.expected_zygstate = None; self.zygosity_state = None + self.pass_vld = True; self.ccg_sum = [] + + ## + ## Genotype! + if not self.allele_validation(): raise Exception('Allele(s) failed validation. Cannot genotype..') + if not self.determine_ccg(): raise Exception('CCG Genotyping failure. Cannot genotype..') + if not self.determine_cag(): raise Exception('CAG Genotyping failure. Cannot genotype..') + if not self.genotype_validation(): raise Exception('Genotype failed validation. Cannot genotype..') + if not self.inspect_peaks(): + log.warning('{}{}{}{}'.format(clr.red, 'shd__ ', clr.end, '1+ allele(s) failed peak validation. Precision not guaranteed.')) + self.warning_triggered = True + self.sequencepair_object.set_peakinspection_warning(True) + self.n_align_dist() + self.calculate_score() + self.contextualise() + self.render_graphs() + self.set_report() + + def build_zygosity_model(self): + """ + Function to build a SVM (wrapped into OvO class) for determining CCG zygosity + :return: svm object wrapped into OvO, class-label hash-encoder object + """ + + ## + ## Classifier object and relevant parameters for our CCG prediction svc_object = svm.LinearSVC(C=1.0, loss='hinge', penalty='l2', dual=False, - tol=1e-4, multi_class='crammer_singer', fit_intercept=True, - intercept_scaling=1, verbose=0, random_state=0, max_iter=100000) - - ## - ## Take raw training data (CCG zygosity data) into DataLoader model object - traindat_ccg_collapsed = self.training_data['CollapsedCCGZygosity'] - traindat_descriptionfi = self.training_data['GenericDescriptor'] - traindat_model = DataLoader(traindat_ccg_collapsed, traindat_descriptionfi).load_model() - - ## - ## Model data fitting to SVM - X = preprocessing.normalize(traindat_model.DATA) - Y = traindat_model.TARGET - ovo_svc = OutputCodeClassifier(svc_object, code_size=2, random_state=0).fit(X, Y) - encoder = traindat_model.ENCDR - - ## - ## Return the fitted OvO(SVM) and Encoder - return ovo_svc, encoder - - def predict_zygstate(self): - """ - Function which takes the newly collapsed CCG distribution and executes SVM prediction - to determine the zygosity state of this sample's CCG value(s). Data is reshaped - and normalised to ensure more reliable results. A check is executed between the results of - forward and reverse zygosity; if a match, great; if not, not explicitly bad but inform user. - :return: zygosity[2:-2] (trimming unrequired characters) - """ - - ## - ## Reshape the input distribution so SKL doesn't complain about 1D vectors - ## Normalise data in addition; cast to float64 for this to be permitted - forward_reshape = preprocessing.normalize(np.float64(self.forward_aggregate.reshape(1, -1))) - reverse_reshape = preprocessing.normalize(np.float64(self.reverse_aggregate.reshape(1, -1))) - - ## - ## Predict the zygstate of these reshapen, normalised 20D CCG arrays using SVM object earlier - ## Results from self.classifier are #encoded; so convert with our self.encoder.inverse_transform - forward_zygstate = str(self.encoder.inverse_transform(self.classifier.predict(forward_reshape))) - reverse_zygstate = str(self.encoder.inverse_transform(self.classifier.predict(reverse_reshape))) - - ## - ## We only particularly care about the reverse zygosity (CCG reads are higher quality in reverse data) - ## However, for a QoL metric, compare fw/rv results. If match, good! If not, who cares! - if not forward_zygstate == reverse_zygstate: - self.allele_flags['CCGZygDisconnect'] = True - else: - self.allele_flags['CCGZyg_disconnect'] = False - - return reverse_zygstate[2:-2] - - def index_inspector(self, index_inspection_count): - - major = max(self.reverse_aggregate) - majoridx = np.where(self.reverse_aggregate == major)[0][0] - minor = max(n for n in self.reverse_aggregate if n != major) - minoridx = np.where(self.reverse_aggregate == minor)[0][0] - - if index_inspection_count == 2: - return [(major, majoridx), (minor, minoridx)] - if index_inspection_count == 1: - return [(major, majoridx)] - - @staticmethod - def scrape_distro(distributionfi): - """ - Function to take the aligned read-count distribution from CSV into a numpy array - :param distributionfi: - :return: np.array(data_from_csv_file) - """ - - ## - ## Open CSV file with information within; append to temp list - ## Scrape information, cast to np.array(), return - placeholder_array = [] - with open(distributionfi) as dfi: - source = csv.reader(dfi, delimiter=',') - next(source) # skip header - for row in source: - placeholder_array.append(int(row[2])) - dfi.close() - unlabelled_distro = np.array(placeholder_array) - return unlabelled_distro - - @staticmethod - def distribution_collapse(distribution_array): - """ - Function to take a full 200x20 array (struc: CAG1-200,CCG1 -- CAG1-200CCG2 -- etc CCG20) - and aggregate all CAG values for each CCG - :param distribution_array: input dist (should be (1-200,1-20)) - :return: 1x20D np(array) - """ - - ## - ## Hopefully the user has aligned to the right reference dimensions - try: - ccg_arrays = np.split(distribution_array, 20) - except ValueError: - raise Exception('Input reads individisible by 20. Utilised incorrect reference style.') - - ## - ## Aggregate each CCG - ccg_counter = 1 - collapsed_array = [] - for ccg_array in ccg_arrays: - collapsed_array.append(np.sum(ccg_array)) - ccg_counter += 1 - - return np.asarray(collapsed_array) - - @staticmethod - def pad_distribution(distribution_array, allele_object): - - local_index = np.where(distribution_array == max(distribution_array))[0][0] - local_rightpad = len(distribution_array) - local_index - global_index = allele_object.get_ccg() - 1 - left_buffer = abs(local_index - global_index) - right_buffer = abs(20 - global_index) - local_rightpad - left_pad = np.asarray([0] * left_buffer) - right_pad = np.asarray([0] * right_buffer) - left_aug = np.concatenate((left_pad, distribution_array)) - right_aug = np.concatenate((left_aug, right_pad)) - - return right_aug - - @staticmethod - def split_cag_target(input_distribution): - """ - Function to gather the relevant CAG distribution for the specified CCG value - We gather this information from the forward distribution of this sample pair as CCG reads are - of higher quality in the forward sequencing direction. - We split the entire fw_dist into contigs/bins for each CCG (4000 -> 200*20) - :param input_distribution: input forward distribution (4000d) - :param ccg_target: target value we want to select the 200 values for - :return: the sliced CAG distribution for our specified CCG value - """ - - cag_split = [input_distribution[i:i + 200] for i in range(0, len(input_distribution), 200)] - distribution_dict = collections.OrderedDict() - for i in range(0, len(cag_split)): - distribution_dict['CCG' + str(i + 1)] = cag_split[i] - - # current_target_distribution = distribution_dict['CCG' + str(ccg_target)] - return distribution_dict - - def close_check(self, allele, array, x, y, z, state=None): - inner_pass = True - if np.isclose(array, x, atol=y): - if state == 'minus': - allele.set_nminuswarninglevel(z) - if z >= 5: inner_pass = False - if state == 'plus': - allele.set_npluswarninglevel(z) - if z >= 5: inner_pass = False - else: - allele.set_nminuswarninglevel(0) - allele.set_npluswarninglevel(0) - self.pass_vld = inner_pass - - def peak_detection(self, allele_object, distro, peak_dist, triplet_stage, est_dist=None, fod_recall=False): - - ## - ## Status - fail_state = False - utilised_threshold = 0.50 - error_boundary = 0 - - ## - ## If we're in a re-call situation, lower peak threshold - ## Otherwise, threshold already assigned to object is utilised - if fod_recall: - - recall_count = self.sequencepair_object.get_recallcount() - self.sequencepair_object.set_recallcount(recall_count+1) - if recall_count > 7: raise Exception('7+ recalls. Unable to determine genotype.') - threshold = 0.0 - if triplet_stage == 'CCG': threshold = allele_object.get_ccgthreshold() - if triplet_stage in ['CAG', 'CAGHet', 'CAGHom', 'CAGDim']: threshold = allele_object.get_cagthreshold() - threshold -= 0.06 - utilised_threshold = max(threshold, 0.05) - - ## - ## CCG, CAGHet (Hetero, Homo*, Homo+), CAGDim == 1 peak - ## CAGHom == 2 peaks - allele_object.set_cagthreshold(utilised_threshold) - if triplet_stage == 'CAGHom': - error_boundary = 2 - elif triplet_stage == 'CCG': - if self.zygosity_state == 'HETERO': - error_boundary = 2 - if self.zygosity_state == 'HOMO': - error_boundary = 1 - else: error_boundary = 1 - - ## - ## Look for peaks in our distribution - peak_indexes = peakutils.indexes(distro, thres=utilised_threshold, min_dist=peak_dist) - fixed_indexes = np.array(peak_indexes + 1) - if not len(fixed_indexes) == error_boundary: - if triplet_stage == 'CAGHom' and (est_dist==1 or est_dist==0): - pass - elif allele_object.get_cag() in fixed_indexes: - fixed_indexes = np.asarray([x for x in fixed_indexes if x == allele_object.get_cag()]) - elif triplet_stage == 'CCG': - if len(fixed_indexes) > 2 and not self.zygosity_state == 'HETERO': - fixed_indexes = [np.where(distro == max(distro))[0][0]+1] - if self.zygosity_state == 'HOMO+' or self.zygosity_state == 'HOMO*': - self.sequencepair_object.set_svm_failure(False) - pass - else: - self.sequencepair_object.set_svm_failure(True) - self.sequencepair_object.set_alignmentwarning(True) - else: - fail_state = True - - return fail_state, fixed_indexes - - def allele_validation(self): - - ccg_expectant = [] - ## - ## For the two allele objects in this sample_pair - for allele_object in [self.sequencepair_object.get_primaryallele(), - self.sequencepair_object.get_secondaryallele()]: - - ## - ## Assign read mapped percent if not present in allele - if not allele_object.get_fwalnpcnt() and not allele_object.get_rvalnpcnt(): - allele_object.set_fwalnpcnt(self.sequencepair_object.get_fwalnpcnt()) - allele_object.set_fwalncount(self.sequencepair_object.get_fwalncount()) - allele_object.set_fwalnrmvd(self.sequencepair_object.get_fwalnrmvd()) - allele_object.set_rvalnpcnt(self.sequencepair_object.get_rvalnpcnt()) - allele_object.set_rvalncount(self.sequencepair_object.get_rvalncount()) - allele_object.set_rvalnrmvd(self.sequencepair_object.get_rvalnrmvd()) - - ## - ## Unlabelled distributions - self.forward_distribution = self.scrape_distro(allele_object.get_fwdist()) - self.reverse_distribution = self.scrape_distro(allele_object.get_rvdist()) - allele_object.set_fwarray(self.forward_distribution) - allele_object.set_rvarray(self.reverse_distribution) - - ## - ## Distribution ead count / Peak read count - if allele_object.get_totalreads() < 750: - allele_object.set_distribution_readcount_warning(True) - self.sequencepair_object.set_distribution_readcount_warning(True) - - ## - ## If current alleleobj's assembly/distro is blank - ## Allele is typical, didn't assign values in __atypical.py - ## Hence, set these values here (From seqpair object, where they reside) - if not allele_object.get_rvdist(): - allele_object.set_fwassembly(self.sequencepair_object.get_fwassembly()) - allele_object.set_rvassembly(self.sequencepair_object.get_rvassembly()) - allele_object.set_fwdist(self.sequencepair_object.get_fwdist()) - allele_object.set_rvdist(self.sequencepair_object.get_rvdist()) - - ############################### - ## Stage one -- CCG Zygosity ## - ############################### - ## Typical allele - if allele_object.get_allelestatus() == 'Typical': - self.forward_aggregate = self.distribution_collapse(self.forward_distribution) - self.reverse_aggregate = self.distribution_collapse(self.reverse_distribution) - - ## Atypical allele - if allele_object.get_allelestatus() == 'Atypical': - ## Data has been realigned to custom reference - if not self.invalid_data: - self.forward_aggregate = self.distribution_collapse(self.forward_distribution) - self.reverse_aggregate = self.reverse_distribution - allele_object.set_rvarray(self.reverse_aggregate) - ## Data has not been realigned -- brute force genotyping - if self.invalid_data: - self.forward_aggregate = self.distribution_collapse(self.forward_distribution) - self.reverse_aggregate = self.distribution_collapse(self.reverse_distribution) - self.zygosity_state = self.predict_zygstate() - - ## - ## Allele read peak (dsp error) - major = max(self.reverse_aggregate) - minor = max(n for n in self.reverse_aggregate if n != major) - for item in [major, minor]: - if item == 0 or item is None: - raise ValueError('Insignificant read count. Cannot genotype.') - tertiary = max(n for n in self.reverse_aggregate if n!= major and n!= minor) - majidx = int(np.where(self.reverse_aggregate == major)[0][0]) - minidx = int(np.where(self.reverse_aggregate == minor)[0][0]) - ## Check discrete diff between majidx and minidx - abs_ratio = self.reverse_aggregate[minidx] / self.reverse_aggregate[majidx] - - ## check if minor is n-1/n+1 AND third peak is np.close(minor) - skip_flag = False - if not self.sequencepair_object.get_primaryallele().get_neighbouring_candidate(): - if not self.sequencepair_object.get_secondaryallele().get_neighbouring_candidate(): - if abs(majidx-minidx) == 1: - if np.isclose([minor], [tertiary], atol=minor*0.80): - skip_flag = True - allele_object.set_ccguncertainty(True) - self.sequencepair_object.set_ccguncertainty(True) - if minor == self.reverse_aggregate[allele_object.get_ccg()-1]: - skip_flag = True - allele_object.set_ccguncertainty(True) - self.sequencepair_object.set_ccguncertainty(True) - ## skip the following block if so - if not skip_flag: - if abs_ratio < 0.05: pass - else: - for fod_peak in [majidx+1, minidx+1]: - if allele_object.get_ccg() not in [majidx+1, minidx]: - if fod_peak not in [self.sequencepair_object.get_primaryallele().get_ccg(), - self.sequencepair_object.get_secondaryallele().get_ccg()]: - allele_object.set_fodoverwrite(True) - allele_object.set_ccgval(int(fod_peak)) - - ## - ## Clean up distribution for erroneous peaks - ## In the case of atypical alleles, unexpected peaks may exist in aggregates - if self.zygosity_state == 'HOMO' or self.zygosity_state == 'HOMO*' or self.zygosity_state == 'HOMO+': - for i in range(0, len(self.reverse_aggregate)): - if np.isclose([i], [allele_object.get_ccg() - 1], atol=3): - if np.isclose([self.reverse_aggregate[i]], [self.reverse_aggregate[allele_object.get_ccg() - 1]], - atol=((self.reverse_aggregate[allele_object.get_ccg() - 1])/100) * 45): - removal = (self.reverse_aggregate[i]/100) * 77.5 - if i != allele_object.get_ccg()-1: - self.reverse_aggregate[i] -= removal - else: - removal = (self.reverse_aggregate[i] / 100) * 77.5 - if i != allele_object.get_ccg() - 1: - self.reverse_aggregate[i] -= removal - else: - ## if the current allele is top2, check difference between top1/top2 - ## if the difference is large, we need to further smooth the distribution for this allele - top1 = max(self.reverse_aggregate); top1idx = list(self.reverse_aggregate).index(top1) - top2 = max([x for x in self.reverse_aggregate if x != top1]) - additional_context = 0 - if top2 == self.reverse_aggregate[allele_object.get_ccg()-1]: - differential = (top2/top1)*100 - if 0 < differential < 50: - purge = (self.reverse_aggregate[top1idx]/100)*95 - self.reverse_aggregate[top1idx] -= purge - else: - additional_context = 5 - ## the percentage we should remove errorneous reads by - ## should differ based on the context of n's read count - if 0 < self.reverse_aggregate[allele_object.get_ccg()-1] <= 6000: - removal_context = 75 - elif 6000 <= self.reverse_aggregate[allele_object.get_ccg()-1] <= 12000: - removal_context = 85 - else: - removal_context = 95 - - ## actual cleanup stage - for i in range(0, len(self.reverse_aggregate)): - if i != allele_object.get_ccg()-1: - removal = (self.reverse_aggregate[i]/100) * removal_context+additional_context - self.reverse_aggregate[i] -= removal - if self.reverse_aggregate[i] < 0: self.reverse_aggregate[i] = 0 - - - ## - ## Check SVM didn't fail... - ## (refresh variables for specific distribution) - indv_major = max(self.reverse_aggregate) - indv_minor = max(n for n in self.reverse_aggregate if n != major) - indv_majidx = int(np.where(self.reverse_aggregate == indv_major)[0][0]) - indv_minidx = int(np.where(self.reverse_aggregate == indv_minor)[0][0]) - - if 1 < abs(indv_majidx-indv_minidx) < 10: - peak_count = 0 - if abs_ratio < 0.05: - pass - ## otherwise, perhaps SVM was wrong and missed a peak - else: - for peak in [indv_majidx, indv_minidx]: - pmo = self.reverse_aggregate[peak-1]; ppo = self.reverse_aggregate[peak+1] - pmo_ratio = pmo/self.reverse_aggregate[peak] - ppo_ratio = ppo/self.reverse_aggregate[peak] - ## calc ratio of peak+1/-1, if within range, add peak - - if pmo_ratio and ppo_ratio < 0.2: - if 0.0 not in [pmo_ratio, ppo_ratio]: - peak_count += 1 - - ## hotfix SVM results based on peak detection - if peak_count == 2 and not self.zygosity_state == 'HETERO': - if self.zygosity_state == 'HOMO+' or self.zygosity_state == 'HOMO*': - self.sequencepair_object.set_svm_failure(False) - pass - else: - ## ratios dictate that peak may be legit - ## check between major and minor for validity - suspect_ratio = indv_minor/indv_major - if suspect_ratio < 0.15: pass - else: self.zygosity_state = 'HETERO' - - ## set distribution - allele_object.set_rvarray(self.reverse_aggregate) - - ################################# - ## Stage two -- CCG continuity ## - ################################# - index_inspection_count = 0 - if self.zygosity_state == 'HETERO': index_inspection_count = 2 - if self.zygosity_state == 'HOMO': index_inspection_count = 1 - inspections = self.index_inspector(index_inspection_count) - for inspect in inspections: - if np.isclose(allele_object.get_ccg(), [inspect[1]+1], atol=1): - allele_object.set_validation(True) - ccg_expectant.append(allele_object.get_ccg()) - - try: - if not ccg_expectant[0] == ccg_expectant[1]: - self.expected_zygstate = 'HETERO' - if ccg_expectant[0] == ccg_expectant[1]: - self.expected_zygstate = 'HOMO' - except IndexError: - raise Exception('CCG Prediction Failure.') - - ## - ## If atypical detected, but zygosity was rewritten - ## allele CCG remained the same, but one allele is now atypical - if not self.sequencepair_object.get_atypical_ccgrewrite(): - if self.sequencepair_object.get_atypical_zygrewrite(): - self.zygosity_state = 'HOMO+' - ## allele CCG value was changed as a result of the atypical detection - else: - if self.sequencepair_object.get_atypical_zygrewrite(): - self.zygosity_state = 'HOMO*' - - ## - ## Check both alleles passed validation - if (self.sequencepair_object.get_primaryallele().get_validation()) and ( - self.sequencepair_object.get_secondaryallele().get_validation()): - return True - else: - return False - - def determine_ccg(self): - - ## - ## Constructs - ccg_matches = 0; ccg_values = []; local_zygstate = None; pass_gtp = True; ccg_sum = [] - - ## - ## For the two allele objects in this sample_pair - ## First, ensure CCG matches between DSP estimate and FOD derision - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - - allele.set_ccgthreshold(0.50) - fod_failstate, ccg_indexes = self.peak_detection(allele, allele.get_rvarray(), 1, 'CCG') - while fod_failstate: - fod_failstate, ccg_indexes = self.peak_detection(allele, allele.get_rvarray(), 1, 'CCG', fod_recall=True) - if len(ccg_indexes) == 0: - raise Exception('CCG Peak un-callable; cannot process.') - if allele.get_ccg() in ccg_indexes: - ccg_matches += 1 - allele.set_ccgvalid(True) - ccg_values.append([x for x in ccg_indexes if x == allele.get_ccg()]) - - if len(ccg_indexes) > 1: - if allele.get_header() == 'PRI': - allele.set_fodccg(np.asarray(ccg_indexes[0])) - if allele.get_header() == 'SEC': - allele.set_fodccg(np.asarray(ccg_indexes[1])) - else: - allele.set_fodccg(np.asarray(ccg_indexes[0])) - - distribution_split = split_cag_target(allele.get_fwarray()) - target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] - ccg_sum.append([allele.get_ccg(), sum(target_distro)]) - - if ccg_values[0] == ccg_values[1]: - local_zygstate = 'HOMO' - if not ccg_values[0] == ccg_values[1]: - local_zygstate = 'HETERO' - - ## - ## If the sample's total read count is so low that we cannot trust results - ## We trust the local/expected zygosity over the SVM derived instance-wide variable - if self.sequencepair_object.get_alignmentwarning(): - if sum(self.reverse_aggregate) < 100: - self.zygosity_state = self.expected_zygstate = local_zygstate - - self.sequencepair_object.set_ccgzygstate(self.expected_zygstate) - self.zygosity_state = local_zygstate - if not self.zygosity_state == 'HOMO*' or not self.zygosity_state == 'HOMO+': - if not local_zygstate == self.expected_zygstate: - if abs(ccg_sum[0][0]-ccg_sum[1][0]) == 1: - if not np.isclose([ccg_sum[0][1]],[ccg_sum[1][1]],atol=(0.70*max(ccg_sum)[1])): - pass_gtp = True - self.ccg_sum = ccg_sum - pass - else: - pass_gtp = False - - return pass_gtp - - def determine_cag(self): - - ## - ## Constructs - pass_gtp = True - ############################################### - ## Pre-Check: atypical allele mis-assignment ## - ############################################### - pri_ccg = self.sequencepair_object.get_primaryallele().get_ccg() - sec_ccg = self.sequencepair_object.get_secondaryallele().get_ccg() - - if self.sequencepair_object.get_atypicalcount() > 0: - if self.zygosity_state == 'HOMO': - if pri_ccg != sec_ccg: - self.zygosity_state = 'HETERO' - self.sequencepair_object.set_ccgzygstate(self.zygosity_state) - if self.zygosity_state == 'HETERO': - if pri_ccg == sec_ccg: - self.zygosity_state = 'HOMO' - self.sequencepair_object.set_ccgzygstate(self.zygosity_state) - if self.zygosity_state == 'HOMO*': - pass - - ## - ## Assign distro originals - primary_dist = self.sequencepair_object.get_primaryallele().get_fwarray().copy() - primary_split = split_cag_target(primary_dist) - self.primary_original = primary_split['CCG{}'.format(self.sequencepair_object.get_primaryallele().get_ccg())] - secondary_dist = self.sequencepair_object.get_secondaryallele().get_fwarray().copy() - secondary_split = split_cag_target(secondary_dist) - self.secondary_original = secondary_split['CCG{}'.format(self.sequencepair_object.get_secondaryallele().get_ccg())] - - ## - ## If we have an atypical allele in this sample, the remaining typical allele distribution may be skewed - ## e.g. something aligning to CAG_1_1_7_2 would have aligned to CAG_1_0_9_2 - ## where a typical distribution would originally be CCG homozygous, the CAG_1_0_9_2 reads are still - ## assigned within the typical distribution... clean these up so genotyping isn't misassociating data - if self.sequencepair_object.get_atypicalcount() == 1: - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - if allele.get_allelestatus() == 'Typical': - distribution_split = split_cag_target(allele.get_fwarray()) - target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] - for i in range(0, len(target_distro)): - if i != allele.get_cag() - 1: - removal = (target_distro[i] / 100) * 85 - target_distro[i] -= removal - - ########################## - ## Heterozygous for CCG ## - ########################## - if self.zygosity_state == 'HETERO' or self.zygosity_state == 'HOMO*' or self.zygosity_state == 'HOMO+': - existing_calls = [] - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - - distribution_split = split_cag_target(allele.get_fwarray()) - target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] - allele.set_totalreads(sum(target_distro)) - - if self.zygosity_state == 'HOMO+' or self.zygosity_state == 'HOMO*': - for i in range(0, len(target_distro)): - if i != allele.get_cag() - 1: - removal = (target_distro[i] / 100) * 85 - target_distro[i] -= removal - - allele.set_cagthreshold(0.50) - fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, 1, 'CAGHet') - while fod_failstate: - fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, 1, 'CAGHet', fod_recall=True) - - ## check that FOD didn't return more items than it was required for this allele - ## only keep discrete values from the inferred total of all calls in the current sample - if not self.sequencepair_object.get_homozygoushaplotype(): - for item in cag_indexes: - gtype = (item, allele.get_ccg()) - ## However we can get the case where CAG match between alleles but CCG doesn't - ## so we must add tuples instead of integers, and check the correct element against our observation - if not any(gtype[1] in obs_tuple for obs_tuple in existing_calls): - existing_calls.append(gtype) - if type(cag_indexes) == np.ndarray: - itemindex = np.where(cag_indexes == item) - allele.set_fodcag(cag_indexes.flat[itemindex]) - else: - allele.set_fodcag(cag_indexes) - else: - if type(cag_indexes) == np.ndarray: - itemindex = np.where(cag_indexes == item) - allele.set_fodcag(cag_indexes.flat[itemindex]) - else: - allele.set_fodcag(cag_indexes) - else: - allele.set_fodcag(cag_indexes) - - ######################## - ## Homozygous for CCG ## - ######################## - if self.zygosity_state == 'HOMO': - ## - ## Double check CCG matches.. be paranoid - primary_ccg = self.sequencepair_object.get_primaryallele().get_ccg() - secondary_ccg = self.sequencepair_object.get_secondaryallele().get_ccg() - try: - if not primary_ccg == secondary_ccg: - target = max(self.ccg_sum)[0] - self.sequencepair_object.get_primaryallele().set_ccgval(target) - self.sequencepair_object.get_secondaryallele().set_ccgval(target) - except ValueError: - max_array = [0, 0] - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - distro_split = split_cag_target(allele.get_fwarray()) - total_reads = sum(distro_split['CCG{}'.format(allele.get_ccg())]) - - if total_reads > max_array[1]: - max_array[1] = total_reads - max_array[0] = allele.get_ccg() - self.sequencepair_object.get_primaryallele().set_ccgval(max_array[0]) - self.sequencepair_object.get_secondaryallele().set_ccgval(max_array[0]) - - ## - ## Get distance estimate between two peaks in our target CCG distribution - ## set threshold to use in peak calling algorithm - estimated_distance = abs(self.sequencepair_object.get_secondaryallele().get_cag() - - self.sequencepair_object.get_primaryallele().get_cag()) - - if estimated_distance > 5: distance_threshold = 2 - elif estimated_distance == 1: distance_threshold = 0 - else: distance_threshold = 1 - - ## - ## Process each allele, getting the specific CCG distribution - ## Re-set read count for the allele, due to subsampling - existing_calls = [] - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - - distribution_split = split_cag_target(allele.get_fwarray()) - target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] - - if not self.sequencepair_object.get_primaryallele().get_neighbouring_candidate(): - if not self.sequencepair_object.get_secondaryallele().get_neighbouring_candidate(): - for i in range(0, len(target_distro)): - if i != allele.get_cag() - 1: - removal = (target_distro[i] / 100) * 85 - target_distro[i] -= removal - - allele.set_totalreads(sum(target_distro)) - allele.set_cagthreshold(0.50) - - fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, distance_threshold, 'CAGHom', est_dist=estimated_distance) - while fod_failstate: - fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, distance_threshold, 'CAGHom', est_dist=estimated_distance, fod_recall=True) - - ## check that FOD didn't return more items than it was required for this allele - ## only keep discrete values from the inferred total of all calls in the current sample - if not self.sequencepair_object.get_homozygoushaplotype(): - for item in cag_indexes: - if not item in existing_calls: - existing_calls.append(item) - if type(cag_indexes) == np.ndarray: - itemindex = np.where(cag_indexes == item) - allele.set_fodcag(cag_indexes.flat[itemindex]) - else: - allele.set_fodcag(cag_indexes) - else: - if type(cag_indexes) == np.ndarray: - itemindex = np.where(cag_indexes == item) - allele.set_fodcag(cag_indexes.flat[itemindex]) - else: - allele.set_fodcag(cag_indexes) - else: - allele.set_fodcag(cag_indexes) - return pass_gtp - - def genotype_validation(self): - - ## - ## Constructs - pass_vld = True - primary_allele = self.sequencepair_object.get_primaryallele() - secondary_allele = self.sequencepair_object.get_secondaryallele() - pri_distro_split = split_cag_target(primary_allele.get_fwarray()) - sec_distro_split = split_cag_target(secondary_allele.get_fwarray()) - ccg_zygstate = self.zygosity_state - - ## - ## Primary Allele - primary_dsp_ccg = primary_allele.get_ccg(); primary_fod_ccg = primary_allele.get_fodccg() - primary_dsp_cag = primary_allele.get_cag(); primary_fod_cag = primary_allele.get_fodcag() - primary_peakreads = (split_cag_target(primary_allele.get_fwarray())['CCG{}'.format(primary_dsp_ccg)])[ - primary_dsp_cag-1] - primary_allele.set_peakreads(primary_peakreads) - - ## - ## Secondary Allele - secondary_dsp_ccg = secondary_allele.get_ccg(); secondary_fod_ccg = secondary_allele.get_fodccg() - secondary_dsp_cag = secondary_allele.get_cag(); secondary_fod_cag = secondary_allele.get_fodcag() - secondary_peakreads = (split_cag_target(secondary_allele.get_fwarray())['CCG{}'.format(secondary_dsp_ccg)])[ - secondary_dsp_cag - 1] - secondary_allele.set_peakreads(secondary_peakreads) - - ## - ## Double check fod peaks - def dimension_checker(input_list): - - ## data - fod = input_list[0]; dsp = input_list[1];allele = input_list[2] - - ## casting - if type(fod) is np.ndarray: - fod = input_list[0].tolist() - elif type(fod) is not list: - fod = [input_list[0]] - - ## validity between DSP/FOD - for i in range(0, len(fod)): - if np.isclose([fod[i]], [dsp], atol=1.0): - allele.set_fodcag(fod[i]) - - for item in [[primary_fod_cag, primary_dsp_cag, primary_allele], - [secondary_fod_cag, secondary_dsp_cag, secondary_allele]]: - dimension_checker(item) - primary_fod_cag = [primary_allele.get_fodcag()]; secondary_fod_cag = [secondary_allele.get_fodcag()] - - ## - ## Subfunctions - def read_comparison(val1, val2): - if np.isclose(val1, val2, atol=1): - return val2 - else: - return val1 - - def ensure_integrity(): - - ## - ## Ensure integrity - inner_pass = True - try: - if not primary_dsp_ccg == int(primary_fod_ccg): - if read_comparison(primary_dsp_ccg, int(primary_fod_ccg)) == primary_fod_ccg: - self.sequencepair_object.get_primaryallele().set_fodccg(primary_dsp_ccg) - inner_pass = True - else: - inner_pass = False - except TypeError: - self.sequencepair_object.get_primaryallele().set_fodccg(primary_dsp_ccg) - inner_pass = True - - try: - if not primary_dsp_cag == int(primary_fod_cag): - if read_comparison(primary_dsp_cag, int(primary_fod_cag)) == primary_fod_cag: - self.sequencepair_object.get_primaryallele().set_fodcag(primary_dsp_cag) - inner_pass = True - else: - inner_pass = False - except TypeError: - self.sequencepair_object.get_primaryallele().set_fodcag(primary_dsp_cag) - inner_pass = True - - try: - if not secondary_dsp_ccg == int(secondary_fod_ccg): - if read_comparison(secondary_dsp_ccg, int(secondary_fod_ccg)) == secondary_fod_ccg: - self.sequencepair_object.get_secondaryallele().set_fodccg(secondary_dsp_ccg) - inner_pass = True - else: - inner_pass = False - except TypeError: - self.sequencepair_object.get_secondaryallele().set_fodccg(secondary_dsp_ccg) - inner_pass = True - - try: - if not secondary_dsp_cag == int(secondary_fod_cag): - if read_comparison(secondary_dsp_cag, int(secondary_fod_cag)) == secondary_fod_cag: - self.sequencepair_object.get_secondaryallele().set_fodcag(secondary_dsp_cag) - inner_pass = True - else: - inner_pass = False - except TypeError: - self.sequencepair_object.get_secondaryallele().set_fodcag(secondary_dsp_cag) - - return inner_pass - - ## - ## Brute force zygosity - if not (primary_fod_ccg == secondary_fod_ccg) and (ccg_zygstate == 'HOMO' or ccg_zygstate == 'HOMO*' or ccg_zygstate == 'HOMO+'): - self.zygosity_state = 'HETERO'; ccg_zygstate = 'HETERO' - if not self.sequencepair_object.get_svm_failure(): - self.sequencepair_object.set_svm_failure(True) - if (primary_fod_ccg == secondary_fod_ccg) and ccg_zygstate == 'HETERO': - self.zygosity_state = 'HOMO'; ccg_zygstate = 'HOMO' - self.sequencepair_object.set_svm_failure(True) - - ## - ## Check for potential homozygous haplotype/neighbouring peak - if ccg_zygstate == 'HOMO' and np.isclose(primary_dsp_cag, secondary_dsp_cag, atol=1): - primary_target = pri_distro_split['CCG{}'.format(primary_allele.get_ccg())] - secondary_target = sec_distro_split['CCG{}'.format(secondary_allele.get_ccg())] - - primary_reads = primary_target[primary_allele.get_cag()-1] - secondary_reads = secondary_target[secondary_allele.get_cag()-1] - diff = abs(primary_reads-secondary_reads) - pcnt = (diff/max([primary_reads, secondary_reads])) - - ## If read count is so close (and distance is atol=1) - ## Neighbouring peak... - if 0.0 < pcnt < 0.20: - self.sequencepair_object.set_neighbouringpeaks(True) - pass_vld = ensure_integrity() - return pass_vld - if np.isclose([pcnt], [0.25], atol=0.05): - if 0.0 < pcnt <= 0.30: - self.sequencepair_object.set_neighbouringpeaks(True) - pass_vld = ensure_integrity() - return pass_vld - else: - self.sequencepair_object.set_homozygoushaplotype(True) - self.sequencepair_object.set_secondary_allele(self.sequencepair_object.get_primaryallele()) - ##no need to call ensure_integrity as secondary allele is a copy of primary object - return True - - ## fucky bug with homozygotes not filtering properly - ## leaving unassigned value from FOD - try: - primary_fod_cag.all(); secondary_fod_cag.all() - except AttributeError: - secondary_fod_cag = primary_fod_cag - - if primary_fod_cag == secondary_fod_cag: - - self.sequencepair_object.set_homozygoushaplotype(True) - self.sequencepair_object.set_secondary_allele(self.sequencepair_object.get_primaryallele()) - ##no need to call ensure_integrity as secondary allele is a copy of primary object - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - if allele.get_peakreads() < 250: - allele.set_fatalalignmentwarning(True) - self.sequencepair_object.set_fatalreadallele(False) - else: - allele.set_fatalalignmentwarning(False) - self.sequencepair_object.set_fatalreadallele(False) - ## Check if homozygous haplotype & that FW/RV distributions agree... - if self.sequencepair_object.get_homozygoushaplotype(): - inferred_fwarray = [] - ## infer CCG from fw dist (sum x200) - for contig, distribution in pri_distro_split.items(): - inferred_fwarray.append(sum(distribution)) - ## get values for 'peaks' - inferred_fwarray = np.asarray(inferred_fwarray) - fwidx = int(np.where(inferred_fwarray == max(inferred_fwarray))[0][0]) - rvidx = int(np.where(primary_allele.get_rvarray() == max(primary_allele.get_rvarray()))[0][0]) - ## compare against FOD - fwpeak = peakutils.indexes(inferred_fwarray, thres=0.15, min_dist=1) - rvpeak = peakutils.indexes(primary_allele.get_rvarray(), thres=0.15, min_dist=1) - ## suspected peak match, inspect for further noise - if fwidx == rvidx: - if len(rvpeak) > len(fwpeak): - self.sequencepair_object.set_ccguncertainty(True) - if len(fwpeak) > len(rvpeak): - self.sequencepair_object.set_ccguncertainty(True) - return pass_vld - - ## - ## Check for diminished peaks (incase DSP failure / read count is low) - ## Primary read info - primary_dist = split_cag_target(primary_allele.get_fwarray()) - primary_target = primary_dist['CCG{}'.format(primary_allele.get_ccg())] - primary_reads = primary_target[primary_allele.get_cag() - 1] - primary_total = sum(primary_target) - ## Secondary read info - secondary_dist = split_cag_target(secondary_allele.get_fwarray()) - secondary_target = secondary_dist['CCG{}'.format(secondary_allele.get_ccg())] - secondary_reads = secondary_target[secondary_allele.get_cag() - 1] - secondary_total = sum(secondary_target) - - ## Set specifics for zygstate - peak_total = sum([primary_reads, secondary_reads]); dist_total = 0 - if ccg_zygstate == 'HOMO': - dist_total = sum([primary_total]) - if ccg_zygstate == 'HOMO*' or ccg_zygstate == 'HOMO+' or ccg_zygstate == 'HETERO': - dist_total = sum([primary_total, secondary_total]) - - ## - ## In the case where the peak isn't 65% of all current_ccg distribution reads - ## check that there's no diminished peak (i.e. very small expanded peak) - ## Except when atypical; due to re-alignment occurring to a specific ref - ## diminished peaks won't occur - if not peak_total/dist_total >= 0.65: - if np.isclose([peak_total/dist_total], [0.65], atol=0.175): - pass - elif primary_fod_ccg == secondary_fod_ccg and primary_dsp_cag != secondary_dsp_cag: - primary_target = pri_distro_split['CCG{}'.format(primary_allele.get_ccg())] - split_target = primary_target[primary_allele.get_cag()+5:-1] - difference_buffer = len(primary_target)-len(split_target) - fod_failstate, cag_diminished = self.peak_detection(primary_allele, split_target, 1, 'CAGDim') - while fod_failstate: - fod_failstate, cag_diminished = self.peak_detection(primary_allele, split_target, 1, 'CAGDim', fod_recall=True) - if split_target[cag_diminished] > 100: - if not primary_allele.get_allelestatus()=='Atypical' and not secondary_allele.get_allelestatus()=='Atypical': - ## bypass integrity checks - secondary_allele.set_cagval(int(cag_diminished+difference_buffer-1)) - secondary_allele.set_fodcag(int(cag_diminished+difference_buffer-1)) - secondary_allele.set_fodoverwrite(True) - for peak in [primary_reads, secondary_reads]: - if peak < 750: - self.sequencepair_object.set_diminishedpeaks(True) - return pass_vld - - return pass_vld - - def inspect_peaks(self): - - primary_allele = self.sequencepair_object.get_primaryallele() - secondary_allele = self.sequencepair_object.get_secondaryallele() - for allele in [primary_allele, secondary_allele]: - - distribution_split = split_cag_target(allele.get_fwarray()) - target = distribution_split['CCG{}'.format(allele.get_ccg())] - linspace = np.linspace(0,199,200) - - ## - ## Here we set the peak reads for this allele (i.e. number of reads aligned to N) - ## In the case of WOEFUL atypical re-alignments, sometimes more than one 'peak' is found - ## Hence we detect for that, raise an exception as this allele is un-genotype-able - try: - if allele.get_peakreads() < 250: - allele.set_fatalalignmentwarning(True) - self.sequencepair_object.set_fatalreadallele(True) - except ValueError: - if not len(allele.get_peakreads()) == 1: - self.sequencepair_object.set_atypical_alignmentwarning(True) - raise Exception('Atypical re-alignment inaccuracy. Cannot genotype.') - ## - ## fucking weird interp bug filtering - ## Interp a gaussian to suspected peak - warnings.filterwarnings('error') - try: - peaks_interp = peakutils.interpolate(linspace, target, ind=[allele.get_fodcag() - 1]) - if np.isclose([peaks_interp], [allele.get_fodcag() - 1], rtol=0.5): - interp_distance = abs(peaks_interp - float(allele.get_fodcag()) - 1) - allele.set_interpdistance(interp_distance[0]) - else: - allele.raise_interpolation_warning(True) - except Warning: - allele.raise_interpolation_warning(True) - pass - - ## - ## Calculate % of reads located near peak - spread_reads = sum(target[allele.get_cag()-6:allele.get_cag()+5]) - spread_pcnt = (spread_reads/sum(target)) - allele.set_vicinityreads(spread_pcnt) - - ## - ## Calculate peak dropoff - nminus = target[allele.get_cag()-2]; n = target[allele.get_cag()-1]; nplus = target[allele.get_cag()] - nminus_overn = nminus/n; nplus_overn = nplus/n - dropoff_list = [nminus_overn, nplus_overn] - allele.set_immediate_dropoff(dropoff_list) - - ## - ## Sometimes, alignment parameters can result in invalid genotyping (i.e. 2 peaks when expecting 1) - ## Test for this, inform user.. - if self.zygosity_state == 'HETERO': - major = max(target) - majoridx = np.where(target == major)[0][0] - minor = max(n for n in target if n != major) - minoridx = np.where(target == minor)[0][0] - thresh = (major/100)*55 - - if abs(majoridx-minoridx) > 2: - if np.isclose([major],[minor], atol=thresh): - allele.set_unexpectedpeaks(True) - self.pass_vld = False - - ## - ## Slippage - ## Gather from N-2:N-1, sum and ratio:N - nmt = allele.get_cag() - 3; nmo = allele.get_cag() - 1 - slip_ratio = (sum(target[nmt:nmo])) / target[allele.get_cag() - 1] - allele.set_backwardsslippage(slip_ratio) - - rv_ratio = (target[allele.get_fodcag()-2]/target[allele.get_fodcag()-1]) - fw_ratio = (target[allele.get_fodcag()]/target[allele.get_fodcag()-1]) - if not self.sequencepair_object.get_homozygoushaplotype() and not self.sequencepair_object.get_neighbouringpeaks(): - if np.isclose([fw_ratio], [0.85], atol=0.075): - if rv_ratio > 0.65: - allele.set_fodcag(allele.get_fodcag()+1) - allele.set_slippageoverwrite(True) - if np.isclose([rv_ratio], [0.80], atol=0.150): - if fw_ratio > 0.65: - allele.set_fodcag(allele.get_fodcag()-1) - allele.set_slippageoverwrite(True) - ## - ## If we're not homozygous or neighbouring, 'normal' peaks.. - ## Check dropoffs are legitimate and 'clean' - if not self.sequencepair_object.get_homozygoushaplotype() and not self.sequencepair_object.get_neighbouringpeaks(): - self.close_check(allele, nminus_overn, [0.25], 0.02, 1, state='minus') ## inform user - self.close_check(allele, nplus_overn, [0.05], 0.02, 1, state='plus') ## inform user - self.close_check(allele, nminus_overn, [0.35], 0.04, 2, state='minus') ## warn user - self.close_check(allele, nplus_overn, [0.15], 0.03, 2, state='plus') ## warn user - self.close_check(allele, nminus_overn, [0.45], 0.05, 3, state='minus') ## severe warning - self.close_check(allele, nplus_overn, [0.27], 0.03, 3, state='plus') ## severe warning - self.close_check(allele, nminus_overn, [0.60], 0.05, 4, state='minus') ## extreme warning - self.close_check(allele, nplus_overn, [0.37], 0.05, 4, state='plus') ## extreme warning - self.close_check(allele, nminus_overn, [0.75], 0.05, 5, state='minus') ## failure - self.close_check(allele, nplus_overn, [0.65], 0.05, 5, state='plus') ## failure - if nminus_overn > 0.75: allele.set_nminuswarninglevel(6); self.pass_vld = False ## failure - if nplus_overn > 0.65: allele.set_npluswarninglevel(6); self.pass_vld = False ## failure - else: - allele.set_nminuswarninglevel(2) - allele.set_npluswarninglevel(2) - - ## - ## Somatic mosaicism - ## Gather from N+1:N+10, sum and ratio:N - npo = allele.get_cag(); npt = allele.get_cag()+10 - if allele.get_header() == 'PRI': dist = self.primary_original - if allele.get_header() == 'SEC': dist = self.secondary_original - somatic_ratio = (sum(dist[npo:npt]))/dist[allele.get_cag()-1] - allele.set_somaticmosaicism(somatic_ratio) - - ## - ## If we get here; alleles are valid - allele.set_ccgvalid(True) - allele.set_cagvalid(True) - allele.set_genotypestatus(True) - - novel_caacag = allele.get_reflabel().split('_')[1]; novel_ccgcca = allele.get_reflabel().split('_')[2] - allele.set_allelegenotype('{}_{}_{}_{}_{}'.format(allele.get_fodcag(), novel_caacag, - novel_ccgcca, allele.get_fodccg(), - allele.get_cct())) - - ## - ## Check DSP generated allele label vs FOD results - if int(allele.get_reflabel().split('_')[3]) != int(allele.get_fodccg()): - allele.set_referencelabel('{}_{}_{}_{}_{}'.format(allele.get_fodcag(), novel_caacag, - novel_ccgcca, allele.get_fodccg(), - allele.get_cct())) - allele.set_fodoverwrite(True) - if int(allele.get_reflabel().split('_')[0]) != int(allele.get_fodcag()): - allele.set_referencelabel('{}_{}_{}_{}_{}'.format(allele.get_fodcag(), novel_caacag, - novel_ccgcca, allele.get_fodccg(), - allele.get_cct())) - allele.set_fodoverwrite(True) - - ## - ## Homozygous from SVM, genotypes match, allele DSP differ, hzygous flag False - ## aka this sample has an large expanded allele with almost no reads - ## and the pipeline missed it - if self.zygosity_state != 'HETERO': - if not self.sequencepair_object.get_homozygoushaplotype(): - if allele.get_fodoverwrite(): - self.sequencepair_object.set_missed_expansion(True) - self.sequencepair_object.set_diminishedpeaks(True) - - ## - ## If failed, write intermediate data to report - if not self.pass_vld: - inspection_logfi = os.path.join(self.sequencepair_object.get_predictpath(), - '{}{}'.format(allele.get_header(), 'PeakInspectionLog.txt')) - inspection_str = '{} {}\n{}: {}\n{}: {}\n' \ - '{}: {}\n{}: {}\n{}: {}\n' \ - '{}: {}\n{}: {}\n{}: {}\n' \ - '{}: {}\n{}: {}\n'.format( - '>> Peak Inspection Failure','Intermediate results log', - 'Investigating CCG', allele.get_ccg(), - 'Interpolation warning', allele.get_interpolation_warning(), - 'Interpolation distance', allele.get_interpdistance(), - 'Reads (%) surrounding peak', allele.get_vicinityreads(), - 'Peak dropoff', dropoff_list, - 'NMinus ratio', nminus_overn, - 'NMinus warning', allele.get_nminuswarninglevel(), - 'NPlus ratio', nplus_overn, - 'NPlus warning', allele.get_npluswarninglevel(), - 'Unexpected Peaks', allele.get_unexpectedpeaks()) - with open(inspection_logfi,'w') as logfi: - logfi.write(inspection_str) - logfi.close() - - return self.pass_vld - - def n_align_dist(self): - - """ - Function to align alleles of the current sample to the same n-point as any previous alleles processed - in this run. Append the padded distributions to the same CSV file for in-depth somatic mosaicism - studies.. - :return: fuck all - """ - - ## - ## For each allele in the sample get the target CCG distribution - ## Pad it so that N (the determined genotype) is at the same index in the output file - ## output the padded distribution and close the file - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - if allele.get_header() == 'PRI': target = self.primary_original - if allele.get_header() == 'SEC': target = self.secondary_original - fix_target = ','.join(['%.5f' % num for num in target]) - - anchor = 203 - anchor_port = anchor - allele.get_cag() - anchor_starboard = anchor_port + 200 - left_buffer = '-,'*anchor_port - right_buffer = '-,'*(403-anchor_starboard) - padded_dist = left_buffer+fix_target+right_buffer[:-1] - - ### - ### distribution fixed but csv writing incorrect list still - sample_output = '{},{},{},{}\n'.format(self.sequencepair_object.get_label(), allele.get_header(), - allele.get_allelegenotype(), padded_dist) - - with open(self.padded_target, 'a') as distfi: distfi.write(sample_output) - distfi.close() - - def render_graphs(self): - - ## - ## Data for graph rendering (prevents frequent calls/messy code [[lol irony]]) - pri_fodccg = self.sequencepair_object.get_primaryallele().get_fodccg()-1 - sec_fodccg = self.sequencepair_object.get_secondaryallele().get_fodccg()-1 - pri_fodcag = self.sequencepair_object.get_primaryallele().get_fodcag()-1 - sec_fodcag = self.sequencepair_object.get_secondaryallele().get_fodcag()-1 - pri_rvarray = self.sequencepair_object.get_primaryallele().get_rvarray() - sec_rvarray = self.sequencepair_object.get_secondaryallele().get_rvarray() - pri_fwarray = self.sequencepair_object.get_primaryallele().get_fwarray() - sec_fwarray = self.sequencepair_object.get_secondaryallele().get_fwarray() - - predpath = self.sequencepair_object.get_predictpath() - - def graph_subfunction(x, y, axis_labels, xticks, peak_index, predict_path, file_handle, prefix='', graph_type=None, neg_anchor=False): - - #seaborn palette - sns.set(style='darkgrid') - - ## force fonts because matplotlib spam on some people's systems? - plt.rcParams['pdf.fonttype']=42 - plt.rcParams['ps.fonttype']=42 - mpll = log.getLogger('matplotlib'); mpll.setLevel(log.WARNING) - - - x = np.linspace(x[0],x[1],x[2]) - fig, ax = plt.subplots(figsize=(10, 6)); plt.title(prefix+self.sequencepair_object.get_label()) - plt.xlabel(axis_labels[0]); plt.ylabel(axis_labels[1]) - if graph_type == 'bar': - ## ticker forced to integers (instead of floats) - from matplotlib.ticker import FuncFormatter - ## format xtick labels correctly (CCG vs CAG distributions) - if neg_anchor: xtickslabel = xticks[2] - else: xtickslabel = [i-1 for i in xticks[2]] - ## plot bar data, remove x labels - sns.barplot(x,y); plt.xticks([]) - ## re-add x labels above respective bar plots - for p, dat in zip(ax.patches, xtickslabel): - ax.text(p.get_x() + p.get_width() / 2., p.get_height()+25, dat, ha="center", fontsize=9) - - ## ticker forced to integers (instead of floats) - plt.gca().xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x))) - else: - plt.xticks(np.arange(xticks[0][0], xticks[0][1], xticks[0][2]), xticks[2]) - plt.xlim(xticks[1][0], xticks[1][1]) - pplot(x,y,peak_index) - peak_index = [i+1 for i in peak_index] - plt.legend(['Genotype: {}'.format(peak_index)]) - warnings.simplefilter("ignore") ## ignore open file warning because they're closed after this subfunc - plt.savefig(os.path.join(predict_path, file_handle), format='pdf') - plt.close() - - def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hplus=False): - - ## - ## Readers and pages - line_reader = PyPDF2.PdfReader(open(graph_list[0], 'rb')); line_page = line_reader.pages[0] - bar_reader = PyPDF2.PdfReader(open(graph_list[1], 'rb')); bar_page = bar_reader.pages[0] - - ## - ## Create new page (double width), append bar and line pages side-by-side (CHANGED MW) - # translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) - # bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) - # bar_page.add_transformation(bar_transformation) - # translated_page.merge_page(bar_page) - # translated_page.merge_page(line_page) - ## - ## Write to one PDF - if hplus: suffix = 'AtypicalHomozyg' - else: suffix = '' - if not header: output_path = os.path.join(prediction_path, 'CCG{}CAGDetection_{}.pdf'.format(ccg_val, suffix)) - else: output_path = os.path.join(prediction_path, 'IntroCCG.pdf') - writer = PyPDF2.PdfWriter() - writer.add_page(line_page) - writer.add_page(bar_page) - with open(output_path, 'wb') as f: - writer.write(f) - - ## - ## Return CAG plot path - return output_path - - ########################################## - ## SAMPLE CARD FOR GENOTYPE INFORMATION ## - ########################################## - sample_pdf_path = os.path.join(predpath, '{}{}'.format(self.sequencepair_object.get_label(),'.pdf')) - c = canvas.Canvas(sample_pdf_path, pagesize=(720,432)) - header_string = '{}{}'.format('Sample header: ', self.sequencepair_object.get_label()) - primary_string = '{}(CAG{}, CCG{}) ({}; {}; Confidence {}%)'.format('Primary: ', self.sequencepair_object.get_primaryallele().get_fodcag(), - self.sequencepair_object.get_primaryallele().get_fodccg(), - self.sequencepair_object.get_primaryallele().get_allelestatus(), - self.sequencepair_object.get_primaryallele().get_allelegenotype(), - int(self.sequencepair_object.get_primaryallele().get_alleleconfidence())) - secondary_string = '{}(CAG{}, CCG{}) ({}; {}; Confidence {}%)'.format('Secondary: ', self.sequencepair_object.get_secondaryallele().get_fodcag(), - self.sequencepair_object.get_secondaryallele().get_fodccg(), - self.sequencepair_object.get_secondaryallele().get_allelestatus(), - self.sequencepair_object.get_secondaryallele().get_allelegenotype(), - int(self.sequencepair_object.get_secondaryallele().get_alleleconfidence())) - - ########################################################## - ## Create canvas for sample 'intro card' ## - ## Set font colour depending on subsample/invalid/valid ## - ## invalid == atypical allele, no realignment ## - ## valid == atypical allele, realigned ## - ########################################################## - if self.sequencepair_object.get_subsampleflag(): - if self.sequencepair_object.get_subsampleflag() == '0.05**': - pass - elif self.sequencepair_object.get_automatic_DSPsubsample(): - pass - elif float(self.sequencepair_object.get_subsampleflag()) >= 0.5: - pass - else: - c.setFillColorRGB(75, 0, 130) - c.drawCentredString(360, 186, '!! Genotype derived from significantly subsampled data !!') - if self.invalid_data: - c.setFillColorRGB(255, 0, 0) - c.drawCentredString(360, 196, '!! Atypical alleles without re-alignment !!') - if not self.invalid_data: - c.setFillColorRGB(0, 0, 0) - c.drawCentredString(360, 256, header_string) - c.drawCentredString(360, 236, primary_string) - c.drawCentredString(360, 216, secondary_string) - c.save() - - ############################################### - ## CCG heterozygous example ## - ## i.e. CCG two peaks, one CAG dist per peak ## - ############################################### - if self.zygosity_state == 'HETERO' or self.zygosity_state == 'HOMO*' or self.zygosity_state == 'HOMO+': - - ## - ## Render CCG graph, append path to allele path list - ## Merge intro_ccg card with sample CCG graph - ## Append merged intro_ccg to heterozygous list - hetero_graphs = []; ccg_peaks = [int(pri_fodccg),int(sec_fodccg)] - concat = np.asarray([a + b for a, b in zip(pri_rvarray,sec_rvarray)]) - graph_subfunction([0, 21, 20], concat, ['CCG Value', 'Read Count'], ([1, 20, 1], [1, 20], list(range(1,21))), - ccg_peaks, predpath, 'CCGDetection.pdf', graph_type='bar', neg_anchor=True) - intro_card = pagemerge_subfunction([sample_pdf_path, os.path.join(predpath, 'CCGDetection.pdf')], - predpath, ccg_val=0, header=True) - hetero_graphs.append(intro_card) - plt.close() - - ## - ## For each CCG allele in this heterozygous sample - for allele in [self.sequencepair_object.get_primaryallele(),self.sequencepair_object.get_secondaryallele()]: - - ## - ## Data for this allele (peak detection graph) - temp_graphs = [] - - target_distro = [] - if allele.get_header() == 'PRI': - target_distro = self.primary_original - if allele.get_header() == 'SEC': - target_distro = self.secondary_original - - if self.zygosity_state == 'HOMO+': - for i in range(0, len(target_distro)): - if i != allele.get_cag() - 1: - removal = (target_distro[i] / 100) * 75 - target_distro[i] -= removal - - if allele.get_rewrittenccg() is not None: - peak_filename = 'CCG{}-CAGDetection_atypical_ccgdiff.pdf'.format(allele.get_fodccg()) - peak_prefix = '(CCG{}**) '.format(allele.get_fodccg()) - elif allele.get_unrewrittenccg() is not None: - peak_filename = 'CCG{}-CAGDetection_atypical_ccgsame.pdf'.format(allele.get_fodccg()) - peak_prefix = '(CCG{}++) '.format(allele.get_fodccg()) - else: - peak_filename = 'CCG{}-CAGDetection.pdf'.format(allele.get_fodccg()) - peak_prefix = '(CCG{}) '.format(allele.get_fodccg()) - peak_graph_path = os.path.join(predpath, peak_filename) - ## Render the graph, append to list, close plot - graph_subfunction([0, 199, 200], target_distro, ['CAG Value', 'Read Count'], - ([1, 200, 50], [1, 200], [0,50,100,150,200]), [np.int64(allele.get_fodcag() - 1)], - predpath, peak_filename, prefix=peak_prefix) - temp_graphs.append(peak_graph_path); plt.close() - - ## - ## Inspect the peak (subslice) - slice_range = list(range(allele.get_fodcag()-4, allele.get_fodcag()+7)) - if allele.get_rewrittenccg(): - slice_filename = 'CCG{}-Peak_atypical_ccgdiff.pdf'.format(allele.get_fodccg()) - slice_prefix = '(CCG{}**) '.format(allele.get_ccg()) - elif allele.get_unrewrittenccg(): - slice_filename = 'CCG{}-Peak_atypical_ccgsame.pdf'.format(allele.get_fodccg()) - slice_prefix = '(CCG{}++) '.format(allele.get_ccg()) - else: - slice_filename = 'CCG{}-Peak.pdf'.format(allele.get_fodccg()) - slice_prefix = '(CCG{}) ' .format(allele.get_ccg()) - sub = target_distro[np.int64(allele.get_fodcag()-6):np.int64(allele.get_fodcag()+5)] - ## Render the graph, append to list, close plot - graph_subfunction([0,10,11], sub, ['CAG Value', 'Read Count'], ([1,11,1], [1,11], slice_range), - [np.int64(allele.get_fodcag()-1)], predpath,slice_filename, prefix=slice_prefix, graph_type='bar') - temp_graphs.append(os.path.join(predpath,slice_filename)); plt.close() - - ## - ## Merge 'allele sample' into one page - ccg_val = allele.get_fodccg() - if (allele.get_unrewrittenccg() is not None) or (allele.get_rewrittenccg() is not None): hplus = True - else: hplus = False - merged_graph = pagemerge_subfunction(temp_graphs, predpath, ccg_val, hplus=hplus) - hetero_graphs.append(merged_graph) - - self.sequencepair_object.get_primaryallele().set_allelegraphs(hetero_graphs) - self.sequencepair_object.get_secondaryallele().set_allelegraphs(hetero_graphs) - - ############################################## - ## CCG homozygous example ## - ## i.e. CCG one peak, one CAG dist per peak ## - ############################################## - if self.zygosity_state == 'HOMO': - - ## - ##Data for homozygous graph(s) - homo_graphs = [] - page_graphs = [] - target_ccg = 'CCG{}'.format(self.sequencepair_object.get_primaryallele().get_ccg()) - ## Peak data - peak_filename = 'CCG{}-CAGDetection.pdf'.format(self.sequencepair_object.get_primaryallele().get_fodccg()) - peak_prefix = '(CCG{}) '.format(self.sequencepair_object.get_primaryallele().get_ccg()) - altpeak_filename = 'CCG{}-Peak.pdf'.format(self.sequencepair_object.get_primaryallele().get_fodccg()) - ccg_peaks = [int(pri_fodccg),int(sec_fodccg)]; cag_peaks = [int(pri_fodcag),int(sec_fodcag)] - distribution_split = split_cag_target(pri_fwarray); target_distro = self.primary_original - - ## Subslice data - pri_cag = self.sequencepair_object.get_primaryallele().get_cag() - sec_cag = self.sequencepair_object.get_secondaryallele().get_cag() - upper = max([pri_cag, sec_cag]) - if self.sequencepair_object.get_homozygoushaplotype(): lower = upper - else: lower = max(n for n in [pri_cag, sec_cag] if n != upper) - sub = target_distro[lower-6:upper+5] - slice_range = list(range(lower-4,upper+7)) - - ## - ## Render the graph, append to list, close plot - ## Merge intro_ccg card with sample CCG graph - ## Append merged intro_ccg to homozygous list, append line/bar peak to page list - graph_subfunction([0, 21, 20], pri_rvarray, ['CCG Value', 'Read Count'], ([1, 20, 1], [1, 20], list(range(1,21))), - ccg_peaks, predpath, 'CCGDetection.pdf', graph_type='bar', neg_anchor=True); plt.close() - graph_subfunction([0, 199, 200], target_distro, ['CAG Value', 'Read Count'], - ([1, 200, 50], [1, 200], [0,50,100,150,200]), cag_peaks, predpath, - peak_filename, prefix=peak_prefix); plt.close() - graph_subfunction([0, len(sub)-1, len(sub)], sub, ['CAG Value', 'Read Count'], - ([1, len(sub), 1], [1, len(sub)], slice_range), cag_peaks, predpath, altpeak_filename, - prefix=peak_prefix, graph_type='bar'); plt.close() - intro_card = pagemerge_subfunction([sample_pdf_path, os.path.join(predpath, 'CCGDetection.pdf')], - predpath, ccg_val=0, header=True) - homo_graphs.append(intro_card) - page_graphs.append(os.path.join(predpath, peak_filename)) - page_graphs.append(os.path.join(predpath, altpeak_filename)) - - ## - ## Merge 'allele sample' into one page - ccg_val = self.sequencepair_object.get_primaryallele().get_fodccg() - merged_graph = pagemerge_subfunction(page_graphs, predpath, ccg_val) - - ## Combine CCG and CAG graphs - homo_graphs.append(merged_graph) - self.sequencepair_object.get_primaryallele().set_allelegraphs(homo_graphs) - self.sequencepair_object.get_secondaryallele().set_allelegraphs(homo_graphs) - - ############################################ - ## Merge graphs into a single summary PDF ## - ############################################ - - ## - ## Allele graphs - ## Ensure uniqueness of entries in primary/secondary (i.e. no duplicating CCG graph) - primary_graphs = self.sequencepair_object.get_primaryallele().get_allelegraphs()[0] - secondary_graphs = self.sequencepair_object.get_primaryallele().get_allelegraphs()[0] - sample_graphs = primary_graphs + secondary_graphs - def set_orderpreserve(seq, idfun=None): - if idfun is None: - def idfun(x): return x - seen = {} - result = [] - for item in seq: - marker = idfun(item) - if marker in seen: continue - seen[marker] = 1 - result.append(item) - return result - uniques = set_orderpreserve(sample_graphs) - - ## - ## Merge alleles together - merger = PyPDF2.PdfMerger() - for pdf in uniques: - merger.append(pdf) - merger.write(sample_pdf_path) - merger.close() - - ## - ## Remove individual plots - clean_target = [] - for target_file in os.listdir(predpath): - if target_file.endswith(".pdf"): - clean_target.append(os.path.join(predpath, target_file)) - for rmpdf in clean_target: - if not '{}{}'.format(self.sequencepair_object.get_label(),'.pdf') in rmpdf: - os.remove(rmpdf) - - def calculate_score(self): - - ## - ## For both alleles - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - allele_log_fi = os.path.join(self.sequencepair_object.get_predictpath(), '{}{}'.format(allele.get_header(), '_PenaltiesLog.txt')) - with open(allele_log_fi, 'a') as penfi: - penfi.write('{}, {}\n'.format('Flag/Warning','Score Penalty')) - - ## - ## Start score high, deduct for questionable calls.. - allele_confidence = 100 - - ## - ## Sample based genotyping flags - if self.sequencepair_object.get_recallcount() == 7: allele_confidence -= 25; penfi.write('{}, {}\n'.format('Recall Count','-25')) - if 7 > self.sequencepair_object.get_recallcount() > 4: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Recall Count','-15')) - if 4 > self.sequencepair_object.get_recallcount() > 0: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Recall Count', '-5')) - else: allele_confidence += 0; penfi.write('{}, {}\n'.format('Recall Count', '+0')) - - if self.sequencepair_object.get_homozygoushaplotype(): - allele_confidence -= 25; penfi.write('{}, {}\n'.format('Homozygous Haplotype','-25')) - elif self.sequencepair_object.get_neighbouringpeaks(): - allele_confidence -= 15; penfi.write('{}, {}\n'.format('Neighbouring Peaks', '-15')) - else: allele_confidence += 0; penfi.write('{}, {}\n'.format('Normal Data','+0')) - - if self.sequencepair_object.get_diminishedpeaks(): - allele_confidence -= 10; penfi.write('{}, {}\n'.format('Diminished Peaks','-10')) - if allele.get_fodoverwrite(): - allele_confidence -= 20; penfi.write('{}, {}\n'.format('Differential Overwrite','-20')) - if self.sequencepair_object.get_missed_expansion(): - allele_confidence -= 50; penfi.write('{}, {}\n'.format('Missed Expansion', '-50')) - - ## - ## Allele based genotyping flags - ## Allele typical/atypical structure - if allele.get_allelestatus() == 'Atypical': - allele_confidence -= 5; penfi.write('{}, {}\n'.format('Atypical Allele','-5')) - if np.isclose([float(allele.get_atypicalpcnt())],[50.00],atol=5.00): - allele_confidence -= 30; penfi.write('{}, {}\n'.format('Atypical reads (50%)','-30')) - if np.isclose([float(allele.get_atypicalpcnt())],[80.00],atol=20.00): - allele_confidence += 15; penfi.write('{}, {}\n'.format('Atypical reads (80%>)','+15')) - if allele.get_allelestatus() == 'Typical': - allele_confidence += 1; penfi.write('{}, {}\n'.format('Typical Allele', '+1')) - if np.isclose([float(allele.get_typicalpcnt())],[50.00],atol=5.00): - allele_confidence -= 30; penfi.write('{}, {}\n'.format('Typical reads (50%)','-30')) - if np.isclose([float(allele.get_typicalpcnt())],[80.00],atol=15.00): - allele_confidence += 2; penfi.write('{}, {}\n'.format('Typical reads (80%>)','+2')) - - ## - ## Total reads in sample.. - if allele.get_totalreads() > 10000: allele_confidence += 5; penfi.write('{}, {}\n'.format('High total read count', '+5')) - elif allele.get_totalreads() < 1000: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Low total read count', '-15')) - else: allele_confidence += 1; penfi.write('{}, {}\n'.format('Normal total read count','+1')) - - ## - ## Variance of distribution utilised - if allele.get_vicinityreads()*100 > 85.00: allele_confidence += 1; penfi.write('{}, {}\n'.format('Reads near peak','+1')) - elif 84.99 > allele.get_vicinityreads()*100 > 65.00: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Reads near peak','-10')) - elif 64.99 > allele.get_vicinityreads()*100 > 45.00: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Reads near peak','-15')) - elif 44.99 > allele.get_vicinityreads()*100 > 00.00: allele_confidence -= 20; penfi.write('{}, {}\n'.format('Reads near peak','-20')) - - ## - ## Backwards slippage ratio ([N-2:N-1]/N] - if 0.00 < allele.get_backwardsslippage() < 0.10: allele_confidence += 5; penfi.write('{}, {}\n'.format('Backwards slippage','+5')) - elif 0.10 < allele.get_backwardsslippage() < 0.25: allele_confidence += 1; penfi.write('{}, {}\n'.format('Backwards slippage','+1')) - elif allele.get_backwardsslippage() > 0.25: allele_confidence -= 1; penfi.write('{}, {}\n'.format('Backwards slippage','-1')) - elif allele.get_backwardsslippage() > 0.45: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Backwards slippage','-5')) - elif allele.get_backwardsslippage() > 0.65: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Backwards slippage','-10')) - elif 0.65 < allele.get_backwardsslippage() < 1.00: allele_confidence -= 25; penfi.write('{}, {}\n'.format('Backwards slippage','-25')) - if allele.get_slippageoverwrite(): allele_confidence -= 25; penfi.write('{}, {}\n'.format('Slippage overwrite','-25')) - - ## - ## Somatic mosiacisim ratio ([N+1:N+10]/N] - if 0.000 < allele.get_somaticmosaicism() < 0.010: allele_confidence += 10; penfi.write('{}, {}\n'.format('Somatic mosaicism','+10')) - elif 0.010 < allele.get_somaticmosaicism() < 0.015: allele_confidence += 5; penfi.write('{}, {}\n'.format('Somatic mosaicism','+5')) - elif allele.get_somaticmosaicism() > 0.015: allele_confidence -= 1; penfi.write('{}, {}\n'.format('Somatic mosaicism','-1')) - elif allele.get_somaticmosaicism() > 0.025: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Somatic mosaicism','-10')) - elif allele.get_somaticmosaicism() > 0.035: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Somatic mosaicism','-15')) - elif 0.035 < allele.get_somaticmosaicism() < 0.100: allele_confidence -= 20; penfi.write('{}, {}\n'.format('Somatic mosaicism','-20')) - elif allele.get_somaticmosaicism() > 0.100: allele_confidence -= 30; penfi.write('{}, {}\n'.format('Somatic mosaicism','-30')) - - ## - ## Peak calling thresholds - for contig in [allele.get_ccgthreshold(), allele.get_cagthreshold()]: - if contig != 0.5: - if 0.5 > contig > 0.3: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Peak calling threshold','-5')) - if 0.3 > contig > 0.0: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Peak calling threshold','-10')) - else: allele_confidence += 1; penfi.write('{}, {}\n'.format('Peak calling threshold','+1')) - - ## - ## Peak dropoff warnings - for peak_position_error in [allele.get_nminuswarninglevel(), allele.get_npluswarninglevel()]: - if peak_position_error == 0: allele_confidence += 5; penfi.write('{}, {}\n'.format('Surrounding read ratio','+5')) - elif peak_position_error == 1: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Surrounding read ratio', '-5')) - elif 2 >= peak_position_error > 1: allele_confidence -= 7; penfi.write('{}, {}\n'.format('Surrounding read ratio','-7')) - elif peak_position_error >= 5: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Surrounding read ratio','-10')) - else: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Surrounding read ratio','-15')) - - ## - ## Multiply score by a factor if reads were subsampled - if self.sequencepair_object.get_subsampleflag() and not self.sequencepair_object.get_subsampleflag() == '0.05**': - subsample_penalty = []; utilised_subsample_penalty = 0.0; context_penalty = 0.0 - if 0 <= self.sequencepair_object.get_totalseqreads() <= 2000: subsample_penalty = [0.35,0.40,0.45] - if 2000 <= self.sequencepair_object.get_totalseqreads() <= 5000: subsample_penalty = [0.55,0.65,0.70] - if 5000 <= self.sequencepair_object.get_totalseqreads() <= 10000: subsample_penalty = [0.75,0.85,0.95] - if self.sequencepair_object.get_totalseqreads() > 10000: subsample_penalty = [1.0,1.0,1.0] - - if 0.1 <= self.sequencepair_object.get_subsampleflag() <= 0.3: - utilised_subsample_penalty = subsample_penalty[0] - elif 0.3 <= self.sequencepair_object.get_subsampleflag() <= 0.6: - utilised_subsample_penalty = subsample_penalty[1] - elif 0.6 <= self.sequencepair_object.get_subsampleflag() <= 0.9: - utilised_subsample_penalty = subsample_penalty[2] - else: - utilised_subsample_penalty = 1 - - allele_read_ratio = allele.get_totalreads() / self.sequencepair_object.get_totalseqreads() - if np.isclose([allele_read_ratio],[0.05],atol=0.05): context_penalty = 15 - if np.isclose([allele_read_ratio],[0.15],atol=0.05): context_penalty = 10 - if np.isclose([allele_read_ratio],[0.25],atol=0.05): context_penalty = 7 - if np.isclose([allele_read_ratio],[0.35],atol=0.05): context_penalty = 5 - if np.isclose([allele_read_ratio],[0.45],atol=0.05): context_penalty = 5 - if np.isclose([allele_read_ratio],[0.55],atol=0.05): context_penalty = 1 - - ## - ## Due to the nature of neighbour/homozygous peaks, don't cound surrounding reads... - if self.sequencepair_object.get_neighbouringpeaks() or self.sequencepair_object.get_homozygoushaplotype(): - pass - else: - allele_confidence = allele_confidence * utilised_subsample_penalty - allele_confidence -= context_penalty - - penfi.write('{}, *{}\n'.format('Subsample demultiplier', utilised_subsample_penalty)) - penfi.write('{}, -{}\n'.format('Read Ratio Context', context_penalty)) - - ## - ## Mapping percentage - for map_pcnt in [allele.get_fwalnpcnt(), allele.get_rvalnpcnt()]: - if map_pcnt > 90: allele_confidence += 2; penfi.write('{}, {}\n'.format('Mapping percentage', '+2')) - elif 85 < map_pcnt < 90: allele_confidence += 1; penfi.write('{}, {}\n'.format('Mapping percentage', '+1')) - else: allele_confidence -= 2; penfi.write('{}, {}\n'.format('Mapping percentage', '-2')) - - ## - ## Warning penalty.. if triggered, no confidence - if self.warning_triggered: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Peak Inspection warning triggered','-10')) - if allele.get_ccguncertainty(): allele_confidence -= 15; penfi.write('{}, {}\n'.format('CCG Uncertainty','-15')) - if self.sequencepair_object.get_alignmentwarning(): allele_confidence -= 15; penfi.write('{}, {}\n'.format('Low read count alignment warning','-15')) - if self.sequencepair_object.get_atypical_alignmentwarning(): allele_confidence -= 50; penfi.write('{}, {}\n'.format('Atypical re-alignment inaccurate','-50')) - if allele.get_fatalalignmentwarning(): allele_confidence -= 25; penfi.write('{}, {}\n'.format('Fatal low read count alignment warning','-25')) - - ## - ## Differential Confusion - ## i.e two peaks nearby, large difference between suspected but unsure whether homo or neighbour - if allele.get_differential_confusion(): - allele_confidence -= 30; penfi.write('{}, {}\n'.format('Differential Confusion', '-30')) - - ## Heuristic filtering of DSP results state check - if not self.sequencepair_object.get_heuristicfilter(): - allele_confidence -= 20; penfi.write('{}, {}\n'.format('Heuristic filtering of alleles did not assign a secondary allele','-20')) - - ## - ## If reflabel CAG and FOD CAG differ.. no confidence - label_split = allele.get_reflabel().split('_')[0] - if allele.get_allelestatus() == 'Atypical': - if not np.isclose([int(allele.get_fodcag())],[int(label_split)],atol=1): - allele_confidence = 0; penfi.write('{}, {}\n'.format('Atypical DSP:FOD inconsistency','-100')) - - ## - ## Determine score (max out at 100), return genotype - capped_confidence = sorted([0, allele_confidence, 100])[1] - allele.set_alleleconfidence(capped_confidence) - penfi.write('{}, {}\n\n'.format('Final score', capped_confidence)) - penfi.close() - - def contextualise(self): - - ## - ## For both alleles - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - - ## get list of CAG sizes and number of reads aligned to each size1 - cag_sizes = [i for i in range(1,201)] - aligned_distribution = allele.get_fwarray() - raw_repeat_distribution = allele.get_fwarray().copy() - split_distribution = split_cag_target(raw_repeat_distribution) - allele_distribution = split_distribution['CCG{}'.format(allele.get_ccg())] - index = allele.get_cag()-1 - - ## test clean up of distribution for specific alleles - ## todo implement a mechanism by which to remove individual alleles data - ## rather than just anything other than the index of the allele - allele_clearance_range = list(range(allele.get_cag()-11, allele.get_cag()+11)) - for i in range(0, len(allele_distribution)): - if i not in allele_clearance_range: - removal = (allele_distribution[i] / 100) * 85 - allele_distribution[i] -= removal - - ## make 'expanded distribution' of literal count - expanded_distribution = [] - for index, aln_count in zip(cag_sizes, allele_distribution): - to_add = [index] * aln_count - expanded_distribution += to_add - - ## calculate 95% confidence interval given this distribution - a = 1.0 * np.array(expanded_distribution) - n = len(a) - m, se = np.mean(a), sp.stats.sem(a) - h = se * sp.stats.t.ppf((1 + 0.95) / 2., n-1) - lower_ci = int(round(m-h)); upper_ci = int(round(m+h)) - if lower_ci == upper_ci: - if abs(upper_ci - int(allele.get_cag())) > 1: - allele.set_alleleconfinterval('{}-{}'.format(lower_ci, allele.get_cag())) - elif abs(upper_ci - int(allele.get_cag())) == 1: - allele.set_alleleconfinterval('{}-{}'.format(allele.get_cag(), allele.get_cag())) - else: - allele.set_alleleconfinterval('{}-{}'.format(lower_ci, upper_ci)) - else: - if allele.get_cag() > upper_ci: - allele.set_alleleconfinterval('{}-{}'.format(lower_ci, allele.get_cag())) - else: - allele.set_alleleconfinterval('{}-{}'.format(lower_ci, upper_ci)) - - ## haha bad lazy programming wow - if allele.get_alleleconfidence() < 50: - garbage_code = allele.get_alleleconfinterval().split('-') - lower = garbage_code[0]; upper = garbage_code[1] - lower = int(lower)-(int(np.random.randint(1,3,size=1)[0])) - upper = int(upper)+(int(np.random.randint(1,2,size=1)[0])) - allele.set_alleleconfinterval('{}-{}'.format(lower,upper)) - - def set_report(self): - - for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: - - ## - ## Report path for this allele - allele_filestring = '{}{}{}'.format(allele.get_header(),allele.get_allelestatus(), '_AlleleReport.txt') - report_path = os.path.join(self.sequencepair_object.get_predictpath(), allele_filestring) - allele.set_allelereport(report_path) - report_string = '{}{}\n\n{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}' \ - '\n\n{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n\n' \ - '{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}'.format( - 'Allele Report>> ', self.sequencepair_object.get_label(), - 'Summary Information>>', - 'Genotype: ', allele.get_allelegenotype(), - 'Confidence: ', allele.get_alleleconfidence(), - 'CCG Uncertain: ', self.sequencepair_object.get_ccguncertainty(), - 'Structure Status: ', allele.get_allelestatus(), - 'Typical Pcnt: ', allele.get_typicalpcnt(), - 'Atypical Pcnt: ', allele.get_atypicalpcnt(), - 'Total Reads: ', allele.get_totalreads(), - 'Flags>>', - 'Recall Count: ', self.sequencepair_object.get_recallcount(), - 'Homozygous Haplotype: ', self.sequencepair_object.get_homozygoushaplotype(), - 'Neighbouring Peaks: ', self.sequencepair_object.get_neighbouringpeaks(), - 'Diminished Peaks: ', self.sequencepair_object.get_diminishedpeaks(), - 'Backwards Slippage: ', allele.get_backwardsslippage(), - 'Somatic Mosaicism: ', allele.get_somaticmosaicism(), - 'Slippage Overwritten: ', allele.get_slippageoverwrite(), - 'Peak Interpolation Warning: ', allele.get_interpolation_warning(), - 'Peak Interpolation Distance: ', allele.get_interpdistance(), - 'Peak DSP Overwritten: ', allele.get_fodoverwrite(), - 'Low read-count alignment: ', self.sequencepair_object.get_alignmentwarning(), - 'Fatal low read-count: ', allele.get_fatalalignmentwarning(), - 'Data Quality>>', - 'Reads (%) surrounding peak: ', allele.get_vicinityreads(), - 'Immediate Dropoffs: ', allele.get_immediate_dropoff(), - 'N-1 Warning Level: ', allele.get_nminuswarninglevel(), - 'N+1 Warning Level: ', allele.get_npluswarninglevel(), - 'CCG Threshold: ', allele.get_ccgthreshold(), - 'CAG Threshold: ', allele.get_cagthreshold() - ) - ## - ## Write to file - with open(report_path, 'w') as outfi: - outfi.write(report_string) - outfi.close() - - def get_report(self): - - self.allele_report = [self.sequencepair_object.get_primaryallele().get_allelereport(), - self.sequencepair_object.get_secondaryallele().get_allelereport()] - return self.allele_report + tol=1e-4, multi_class='crammer_singer', fit_intercept=True, + intercept_scaling=1, verbose=0, random_state=0, max_iter=100000) + + ## + ## Take raw training data (CCG zygosity data) into DataLoader model object + traindat_ccg_collapsed = self.training_data['CollapsedCCGZygosity'] + traindat_descriptionfi = self.training_data['GenericDescriptor'] + traindat_model = DataLoader(traindat_ccg_collapsed, traindat_descriptionfi).load_model() + + ## + ## Model data fitting to SVM + X = preprocessing.normalize(traindat_model.DATA) + Y = traindat_model.TARGET + ovo_svc = OutputCodeClassifier(svc_object, code_size=2, random_state=0).fit(X, Y) + encoder = traindat_model.ENCDR + + ## + ## Return the fitted OvO(SVM) and Encoder + return ovo_svc, encoder + + def predict_zygstate(self): + """ + Function which takes the newly collapsed CCG distribution and executes SVM prediction + to determine the zygosity state of this sample's CCG value(s). Data is reshaped + and normalised to ensure more reliable results. A check is executed between the results of + forward and reverse zygosity; if a match, great; if not, not explicitly bad but inform user. + :return: zygosity[2:-2] (trimming unrequired characters) + """ + + ## + ## Reshape the input distribution so SKL doesn't complain about 1D vectors + ## Normalise data in addition; cast to float64 for this to be permitted + forward_reshape = preprocessing.normalize(np.float64(self.forward_aggregate.reshape(1, -1))) + reverse_reshape = preprocessing.normalize(np.float64(self.reverse_aggregate.reshape(1, -1))) + + ## + ## Predict the zygstate of these reshapen, normalised 20D CCG arrays using SVM object earlier + ## Results from self.classifier are #encoded; so convert with our self.encoder.inverse_transform + forward_zygstate = str(self.encoder.inverse_transform(self.classifier.predict(forward_reshape))) + reverse_zygstate = str(self.encoder.inverse_transform(self.classifier.predict(reverse_reshape))) + + ## + ## We only particularly care about the reverse zygosity (CCG reads are higher quality in reverse data) + ## However, for a QoL metric, compare fw/rv results. If match, good! If not, who cares! + if not forward_zygstate == reverse_zygstate: + self.allele_flags['CCGZygDisconnect'] = True + else: + self.allele_flags['CCGZyg_disconnect'] = False + + return reverse_zygstate[2:-2] + + def index_inspector(self, index_inspection_count): + + major = max(self.reverse_aggregate) + majoridx = np.where(self.reverse_aggregate == major)[0][0] + minor = max(n for n in self.reverse_aggregate if n != major) + minoridx = np.where(self.reverse_aggregate == minor)[0][0] + + if index_inspection_count == 2: + return [(major, majoridx), (minor, minoridx)] + if index_inspection_count == 1: + return [(major, majoridx)] + + @staticmethod + def scrape_distro(distributionfi): + """ + Function to take the aligned read-count distribution from CSV into a numpy array + :param distributionfi: + :return: np.array(data_from_csv_file) + """ + + ## + ## Open CSV file with information within; append to temp list + ## Scrape information, cast to np.array(), return + placeholder_array = [] + with open(distributionfi) as dfi: + source = csv.reader(dfi, delimiter=',') + next(source) # skip header + for row in source: + placeholder_array.append(int(row[2])) + dfi.close() + unlabelled_distro = np.array(placeholder_array) + return unlabelled_distro + + @staticmethod + def distribution_collapse(distribution_array): + """ + Function to take a full 200x20 array (struc: CAG1-200,CCG1 -- CAG1-200CCG2 -- etc CCG20) + and aggregate all CAG values for each CCG + :param distribution_array: input dist (should be (1-200,1-20)) + :return: 1x20D np(array) + """ + + ## + ## Hopefully the user has aligned to the right reference dimensions + try: + ccg_arrays = np.split(distribution_array, 20) + except ValueError: + raise Exception('Input reads individisible by 20. Utilised incorrect reference style.') + + ## + ## Aggregate each CCG + ccg_counter = 1 + collapsed_array = [] + for ccg_array in ccg_arrays: + collapsed_array.append(np.sum(ccg_array)) + ccg_counter += 1 + + return np.asarray(collapsed_array) + + @staticmethod + def pad_distribution(distribution_array, allele_object): + + local_index = np.where(distribution_array == max(distribution_array))[0][0] + local_rightpad = len(distribution_array) - local_index + global_index = allele_object.get_ccg() - 1 + left_buffer = abs(local_index - global_index) + right_buffer = abs(20 - global_index) - local_rightpad + left_pad = np.asarray([0] * left_buffer) + right_pad = np.asarray([0] * right_buffer) + left_aug = np.concatenate((left_pad, distribution_array)) + right_aug = np.concatenate((left_aug, right_pad)) + + return right_aug + + @staticmethod + def split_cag_target(input_distribution): + """ + Function to gather the relevant CAG distribution for the specified CCG value + We gather this information from the forward distribution of this sample pair as CCG reads are + of higher quality in the forward sequencing direction. + We split the entire fw_dist into contigs/bins for each CCG (4000 -> 200*20) + :param input_distribution: input forward distribution (4000d) + :param ccg_target: target value we want to select the 200 values for + :return: the sliced CAG distribution for our specified CCG value + """ + + cag_split = [input_distribution[i:i + 200] for i in range(0, len(input_distribution), 200)] + distribution_dict = collections.OrderedDict() + for i in range(0, len(cag_split)): + distribution_dict['CCG' + str(i + 1)] = cag_split[i] + + # current_target_distribution = distribution_dict['CCG' + str(ccg_target)] + return distribution_dict + + def close_check(self, allele, array, x, y, z, state=None): + inner_pass = True + if np.isclose(array, x, atol=y): + if state == 'minus': + allele.set_nminuswarninglevel(z) + if z >= 5: inner_pass = False + if state == 'plus': + allele.set_npluswarninglevel(z) + if z >= 5: inner_pass = False + else: + allele.set_nminuswarninglevel(0) + allele.set_npluswarninglevel(0) + self.pass_vld = inner_pass + + def peak_detection(self, allele_object, distro, peak_dist, triplet_stage, est_dist=None, fod_recall=False): + + ## + ## Status + fail_state = False + utilised_threshold = 0.50 + error_boundary = 0 + + ## + ## If we're in a re-call situation, lower peak threshold + ## Otherwise, threshold already assigned to object is utilised + if fod_recall: + + recall_count = self.sequencepair_object.get_recallcount() + self.sequencepair_object.set_recallcount(recall_count+1) + if recall_count > 7: raise Exception('7+ recalls. Unable to determine genotype.') + threshold = 0.0 + if triplet_stage == 'CCG': threshold = allele_object.get_ccgthreshold() + if triplet_stage in ['CAG', 'CAGHet', 'CAGHom', 'CAGDim']: threshold = allele_object.get_cagthreshold() + threshold -= 0.06 + utilised_threshold = max(threshold, 0.05) + + ## + ## CCG, CAGHet (Hetero, Homo*, Homo+), CAGDim == 1 peak + ## CAGHom == 2 peaks + allele_object.set_cagthreshold(utilised_threshold) + if triplet_stage == 'CAGHom': + error_boundary = 2 + elif triplet_stage == 'CCG': + if self.zygosity_state == 'HETERO': + error_boundary = 2 + if self.zygosity_state == 'HOMO': + error_boundary = 1 + else: error_boundary = 1 + + ## + ## Look for peaks in our distribution + peak_indexes = peakutils.indexes(distro, thres=utilised_threshold, min_dist=peak_dist) + fixed_indexes = np.array(peak_indexes + 1) + if not len(fixed_indexes) == error_boundary: + if triplet_stage == 'CAGHom' and (est_dist==1 or est_dist==0): + pass + elif allele_object.get_cag() in fixed_indexes: + fixed_indexes = np.asarray([x for x in fixed_indexes if x == allele_object.get_cag()]) + elif triplet_stage == 'CCG': + if len(fixed_indexes) > 2 and not self.zygosity_state == 'HETERO': + fixed_indexes = [np.where(distro == max(distro))[0][0]+1] + if self.zygosity_state == 'HOMO+' or self.zygosity_state == 'HOMO*': + self.sequencepair_object.set_svm_failure(False) + pass + else: + self.sequencepair_object.set_svm_failure(True) + self.sequencepair_object.set_alignmentwarning(True) + else: + fail_state = True + + return fail_state, fixed_indexes + + def allele_validation(self): + + ccg_expectant = [] + ## + ## For the two allele objects in this sample_pair + for allele_object in [self.sequencepair_object.get_primaryallele(), + self.sequencepair_object.get_secondaryallele()]: + + ## + ## Assign read mapped percent if not present in allele + if not allele_object.get_fwalnpcnt() and not allele_object.get_rvalnpcnt(): + allele_object.set_fwalnpcnt(self.sequencepair_object.get_fwalnpcnt()) + allele_object.set_fwalncount(self.sequencepair_object.get_fwalncount()) + allele_object.set_fwalnrmvd(self.sequencepair_object.get_fwalnrmvd()) + allele_object.set_rvalnpcnt(self.sequencepair_object.get_rvalnpcnt()) + allele_object.set_rvalncount(self.sequencepair_object.get_rvalncount()) + allele_object.set_rvalnrmvd(self.sequencepair_object.get_rvalnrmvd()) + + ## + ## Unlabelled distributions + self.forward_distribution = self.scrape_distro(allele_object.get_fwdist()) + self.reverse_distribution = self.scrape_distro(allele_object.get_rvdist()) + allele_object.set_fwarray(self.forward_distribution) + allele_object.set_rvarray(self.reverse_distribution) + + ## + ## Distribution ead count / Peak read count + if allele_object.get_totalreads() < 750: + allele_object.set_distribution_readcount_warning(True) + self.sequencepair_object.set_distribution_readcount_warning(True) + + ## + ## If current alleleobj's assembly/distro is blank + ## Allele is typical, didn't assign values in __atypical.py + ## Hence, set these values here (From seqpair object, where they reside) + if not allele_object.get_rvdist(): + allele_object.set_fwassembly(self.sequencepair_object.get_fwassembly()) + allele_object.set_rvassembly(self.sequencepair_object.get_rvassembly()) + allele_object.set_fwdist(self.sequencepair_object.get_fwdist()) + allele_object.set_rvdist(self.sequencepair_object.get_rvdist()) + + ############################### + ## Stage one -- CCG Zygosity ## + ############################### + ## Typical allele + if allele_object.get_allelestatus() == 'Typical': + self.forward_aggregate = self.distribution_collapse(self.forward_distribution) + self.reverse_aggregate = self.distribution_collapse(self.reverse_distribution) + + ## Atypical allele + if allele_object.get_allelestatus() == 'Atypical': + ## Data has been realigned to custom reference + if not self.invalid_data: + self.forward_aggregate = self.distribution_collapse(self.forward_distribution) + self.reverse_aggregate = self.reverse_distribution + allele_object.set_rvarray(self.reverse_aggregate) + ## Data has not been realigned -- brute force genotyping + if self.invalid_data: + self.forward_aggregate = self.distribution_collapse(self.forward_distribution) + self.reverse_aggregate = self.distribution_collapse(self.reverse_distribution) + self.zygosity_state = self.predict_zygstate() + + ## + ## Allele read peak (dsp error) + major = max(self.reverse_aggregate) + minor = max(n for n in self.reverse_aggregate if n != major) + for item in [major, minor]: + if item == 0 or item is None: + raise ValueError('Insignificant read count. Cannot genotype.') + tertiary = max(n for n in self.reverse_aggregate if n!= major and n!= minor) + majidx = int(np.where(self.reverse_aggregate == major)[0][0]) + minidx = int(np.where(self.reverse_aggregate == minor)[0][0]) + ## Check discrete diff between majidx and minidx + abs_ratio = self.reverse_aggregate[minidx] / self.reverse_aggregate[majidx] + + ## check if minor is n-1/n+1 AND third peak is np.close(minor) + skip_flag = False + if not self.sequencepair_object.get_primaryallele().get_neighbouring_candidate(): + if not self.sequencepair_object.get_secondaryallele().get_neighbouring_candidate(): + if abs(majidx-minidx) == 1: + if np.isclose([minor], [tertiary], atol=minor*0.80): + skip_flag = True + allele_object.set_ccguncertainty(True) + self.sequencepair_object.set_ccguncertainty(True) + if minor == self.reverse_aggregate[allele_object.get_ccg()-1]: + skip_flag = True + allele_object.set_ccguncertainty(True) + self.sequencepair_object.set_ccguncertainty(True) + ## skip the following block if so + if not skip_flag: + if abs_ratio < 0.05: pass + else: + for fod_peak in [majidx+1, minidx+1]: + if allele_object.get_ccg() not in [majidx+1, minidx]: + if fod_peak not in [self.sequencepair_object.get_primaryallele().get_ccg(), + self.sequencepair_object.get_secondaryallele().get_ccg()]: + allele_object.set_fodoverwrite(True) + allele_object.set_ccgval(int(fod_peak)) + + ## + ## Clean up distribution for erroneous peaks + ## In the case of atypical alleles, unexpected peaks may exist in aggregates + if self.zygosity_state == 'HOMO' or self.zygosity_state == 'HOMO*' or self.zygosity_state == 'HOMO+': + for i in range(0, len(self.reverse_aggregate)): + if np.isclose([i], [allele_object.get_ccg() - 1], atol=3): + if np.isclose([self.reverse_aggregate[i]], [self.reverse_aggregate[allele_object.get_ccg() - 1]], + atol=((self.reverse_aggregate[allele_object.get_ccg() - 1])/100) * 45): + removal = (self.reverse_aggregate[i]/100) * 77.5 + if i != allele_object.get_ccg()-1: + self.reverse_aggregate[i] -= removal + else: + removal = (self.reverse_aggregate[i] / 100) * 77.5 + if i != allele_object.get_ccg() - 1: + self.reverse_aggregate[i] -= removal + else: + ## if the current allele is top2, check difference between top1/top2 + ## if the difference is large, we need to further smooth the distribution for this allele + top1 = max(self.reverse_aggregate); top1idx = list(self.reverse_aggregate).index(top1) + top2 = max([x for x in self.reverse_aggregate if x != top1]) + additional_context = 0 + if top2 == self.reverse_aggregate[allele_object.get_ccg()-1]: + differential = (top2/top1)*100 + if 0 < differential < 50: + purge = (self.reverse_aggregate[top1idx]/100)*95 + self.reverse_aggregate[top1idx] -= purge + else: + additional_context = 5 + ## the percentage we should remove errorneous reads by + ## should differ based on the context of n's read count + if 0 < self.reverse_aggregate[allele_object.get_ccg()-1] <= 6000: + removal_context = 75 + elif 6000 <= self.reverse_aggregate[allele_object.get_ccg()-1] <= 12000: + removal_context = 85 + else: + removal_context = 95 + + ## actual cleanup stage + for i in range(0, len(self.reverse_aggregate)): + if i != allele_object.get_ccg()-1: + removal = (self.reverse_aggregate[i]/100) * removal_context+additional_context + self.reverse_aggregate[i] -= removal + if self.reverse_aggregate[i] < 0: self.reverse_aggregate[i] = 0 + + + ## + ## Check SVM didn't fail... + ## (refresh variables for specific distribution) + indv_major = max(self.reverse_aggregate) + indv_minor = max(n for n in self.reverse_aggregate if n != major) + indv_majidx = int(np.where(self.reverse_aggregate == indv_major)[0][0]) + indv_minidx = int(np.where(self.reverse_aggregate == indv_minor)[0][0]) + + if 1 < abs(indv_majidx-indv_minidx) < 10: + peak_count = 0 + if abs_ratio < 0.05: + pass + ## otherwise, perhaps SVM was wrong and missed a peak + else: + for peak in [indv_majidx, indv_minidx]: + pmo = self.reverse_aggregate[peak-1]; ppo = self.reverse_aggregate[peak+1] + pmo_ratio = pmo/self.reverse_aggregate[peak] + ppo_ratio = ppo/self.reverse_aggregate[peak] + ## calc ratio of peak+1/-1, if within range, add peak + + if pmo_ratio and ppo_ratio < 0.2: + if 0.0 not in [pmo_ratio, ppo_ratio]: + peak_count += 1 + + ## hotfix SVM results based on peak detection + if peak_count == 2 and not self.zygosity_state == 'HETERO': + if self.zygosity_state == 'HOMO+' or self.zygosity_state == 'HOMO*': + self.sequencepair_object.set_svm_failure(False) + pass + else: + ## ratios dictate that peak may be legit + ## check between major and minor for validity + suspect_ratio = indv_minor/indv_major + if suspect_ratio < 0.15: pass + else: self.zygosity_state = 'HETERO' + + ## set distribution + allele_object.set_rvarray(self.reverse_aggregate) + + ################################# + ## Stage two -- CCG continuity ## + ################################# + index_inspection_count = 0 + if self.zygosity_state == 'HETERO': index_inspection_count = 2 + if self.zygosity_state == 'HOMO': index_inspection_count = 1 + inspections = self.index_inspector(index_inspection_count) + for inspect in inspections: + if np.isclose(allele_object.get_ccg(), [inspect[1]+1], atol=1): + allele_object.set_validation(True) + ccg_expectant.append(allele_object.get_ccg()) + + try: + if not ccg_expectant[0] == ccg_expectant[1]: + self.expected_zygstate = 'HETERO' + if ccg_expectant[0] == ccg_expectant[1]: + self.expected_zygstate = 'HOMO' + except IndexError: + raise Exception('CCG Prediction Failure.') + + ## + ## If atypical detected, but zygosity was rewritten + ## allele CCG remained the same, but one allele is now atypical + if not self.sequencepair_object.get_atypical_ccgrewrite(): + if self.sequencepair_object.get_atypical_zygrewrite(): + self.zygosity_state = 'HOMO+' + ## allele CCG value was changed as a result of the atypical detection + else: + if self.sequencepair_object.get_atypical_zygrewrite(): + self.zygosity_state = 'HOMO*' + + ## + ## Check both alleles passed validation + if (self.sequencepair_object.get_primaryallele().get_validation()) and ( + self.sequencepair_object.get_secondaryallele().get_validation()): + return True + else: + return False + + def determine_ccg(self): + + ## + ## Constructs + ccg_matches = 0; ccg_values = []; local_zygstate = None; pass_gtp = True; ccg_sum = [] + + ## + ## For the two allele objects in this sample_pair + ## First, ensure CCG matches between DSP estimate and FOD derision + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + + allele.set_ccgthreshold(0.50) + fod_failstate, ccg_indexes = self.peak_detection(allele, allele.get_rvarray(), 1, 'CCG') + while fod_failstate: + fod_failstate, ccg_indexes = self.peak_detection(allele, allele.get_rvarray(), 1, 'CCG', fod_recall=True) + if len(ccg_indexes) == 0: + raise Exception('CCG Peak un-callable; cannot process.') + if allele.get_ccg() in ccg_indexes: + ccg_matches += 1 + allele.set_ccgvalid(True) + ccg_values.append([x for x in ccg_indexes if x == allele.get_ccg()]) + + if len(ccg_indexes) > 1: + if allele.get_header() == 'PRI': + allele.set_fodccg(np.asarray(ccg_indexes[0])) + if allele.get_header() == 'SEC': + allele.set_fodccg(np.asarray(ccg_indexes[1])) + else: + allele.set_fodccg(np.asarray(ccg_indexes[0])) + + distribution_split = split_cag_target(allele.get_fwarray()) + target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] + ccg_sum.append([allele.get_ccg(), sum(target_distro)]) + + if ccg_values[0] == ccg_values[1]: + local_zygstate = 'HOMO' + if not ccg_values[0] == ccg_values[1]: + local_zygstate = 'HETERO' + + ## + ## If the sample's total read count is so low that we cannot trust results + ## We trust the local/expected zygosity over the SVM derived instance-wide variable + if self.sequencepair_object.get_alignmentwarning(): + if sum(self.reverse_aggregate) < 100: + self.zygosity_state = self.expected_zygstate = local_zygstate + + self.sequencepair_object.set_ccgzygstate(self.expected_zygstate) + self.zygosity_state = local_zygstate + if not self.zygosity_state == 'HOMO*' or not self.zygosity_state == 'HOMO+': + if not local_zygstate == self.expected_zygstate: + if abs(ccg_sum[0][0]-ccg_sum[1][0]) == 1: + if not np.isclose([ccg_sum[0][1]],[ccg_sum[1][1]],atol=(0.70*max(ccg_sum)[1])): + pass_gtp = True + self.ccg_sum = ccg_sum + pass + else: + pass_gtp = False + + return pass_gtp + + def determine_cag(self): + + ## + ## Constructs + pass_gtp = True + ############################################### + ## Pre-Check: atypical allele mis-assignment ## + ############################################### + pri_ccg = self.sequencepair_object.get_primaryallele().get_ccg() + sec_ccg = self.sequencepair_object.get_secondaryallele().get_ccg() + + if self.sequencepair_object.get_atypicalcount() > 0: + if self.zygosity_state == 'HOMO': + if pri_ccg != sec_ccg: + self.zygosity_state = 'HETERO' + self.sequencepair_object.set_ccgzygstate(self.zygosity_state) + if self.zygosity_state == 'HETERO': + if pri_ccg == sec_ccg: + self.zygosity_state = 'HOMO' + self.sequencepair_object.set_ccgzygstate(self.zygosity_state) + if self.zygosity_state == 'HOMO*': + pass + + ## + ## Assign distro originals + primary_dist = self.sequencepair_object.get_primaryallele().get_fwarray().copy() + primary_split = split_cag_target(primary_dist) + self.primary_original = primary_split['CCG{}'.format(self.sequencepair_object.get_primaryallele().get_ccg())] + secondary_dist = self.sequencepair_object.get_secondaryallele().get_fwarray().copy() + secondary_split = split_cag_target(secondary_dist) + self.secondary_original = secondary_split['CCG{}'.format(self.sequencepair_object.get_secondaryallele().get_ccg())] + + ## + ## If we have an atypical allele in this sample, the remaining typical allele distribution may be skewed + ## e.g. something aligning to CAG_1_1_7_2 would have aligned to CAG_1_0_9_2 + ## where a typical distribution would originally be CCG homozygous, the CAG_1_0_9_2 reads are still + ## assigned within the typical distribution... clean these up so genotyping isn't misassociating data + if self.sequencepair_object.get_atypicalcount() == 1: + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + if allele.get_allelestatus() == 'Typical': + distribution_split = split_cag_target(allele.get_fwarray()) + target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] + for i in range(0, len(target_distro)): + if i != allele.get_cag() - 1: + removal = (target_distro[i] / 100) * 85 + target_distro[i] -= removal + + ########################## + ## Heterozygous for CCG ## + ########################## + if self.zygosity_state == 'HETERO' or self.zygosity_state == 'HOMO*' or self.zygosity_state == 'HOMO+': + existing_calls = [] + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + + distribution_split = split_cag_target(allele.get_fwarray()) + target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] + allele.set_totalreads(sum(target_distro)) + + if self.zygosity_state == 'HOMO+' or self.zygosity_state == 'HOMO*': + for i in range(0, len(target_distro)): + if i != allele.get_cag() - 1: + removal = (target_distro[i] / 100) * 85 + target_distro[i] -= removal + + allele.set_cagthreshold(0.50) + fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, 1, 'CAGHet') + while fod_failstate: + fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, 1, 'CAGHet', fod_recall=True) + + ## check that FOD didn't return more items than it was required for this allele + ## only keep discrete values from the inferred total of all calls in the current sample + if not self.sequencepair_object.get_homozygoushaplotype(): + for item in cag_indexes: + gtype = (item, allele.get_ccg()) + ## However we can get the case where CAG match between alleles but CCG doesn't + ## so we must add tuples instead of integers, and check the correct element against our observation + if not any(gtype[1] in obs_tuple for obs_tuple in existing_calls): + existing_calls.append(gtype) + if type(cag_indexes) == np.ndarray: + itemindex = np.where(cag_indexes == item) + allele.set_fodcag(cag_indexes.flat[itemindex]) + else: + allele.set_fodcag(cag_indexes) + else: + if type(cag_indexes) == np.ndarray: + itemindex = np.where(cag_indexes == item) + allele.set_fodcag(cag_indexes.flat[itemindex]) + else: + allele.set_fodcag(cag_indexes) + else: + allele.set_fodcag(cag_indexes) + + ######################## + ## Homozygous for CCG ## + ######################## + if self.zygosity_state == 'HOMO': + ## + ## Double check CCG matches.. be paranoid + primary_ccg = self.sequencepair_object.get_primaryallele().get_ccg() + secondary_ccg = self.sequencepair_object.get_secondaryallele().get_ccg() + try: + if not primary_ccg == secondary_ccg: + target = max(self.ccg_sum)[0] + self.sequencepair_object.get_primaryallele().set_ccgval(target) + self.sequencepair_object.get_secondaryallele().set_ccgval(target) + except ValueError: + max_array = [0, 0] + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + distro_split = split_cag_target(allele.get_fwarray()) + total_reads = sum(distro_split['CCG{}'.format(allele.get_ccg())]) + + if total_reads > max_array[1]: + max_array[1] = total_reads + max_array[0] = allele.get_ccg() + self.sequencepair_object.get_primaryallele().set_ccgval(max_array[0]) + self.sequencepair_object.get_secondaryallele().set_ccgval(max_array[0]) + + ## + ## Get distance estimate between two peaks in our target CCG distribution + ## set threshold to use in peak calling algorithm + estimated_distance = abs(self.sequencepair_object.get_secondaryallele().get_cag() - + self.sequencepair_object.get_primaryallele().get_cag()) + + if estimated_distance > 5: distance_threshold = 2 + elif estimated_distance == 1: distance_threshold = 0 + else: distance_threshold = 1 + + ## + ## Process each allele, getting the specific CCG distribution + ## Re-set read count for the allele, due to subsampling + existing_calls = [] + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + + distribution_split = split_cag_target(allele.get_fwarray()) + target_distro = distribution_split['CCG{}'.format(allele.get_ccg())] + + if not self.sequencepair_object.get_primaryallele().get_neighbouring_candidate(): + if not self.sequencepair_object.get_secondaryallele().get_neighbouring_candidate(): + for i in range(0, len(target_distro)): + if i != allele.get_cag() - 1: + removal = (target_distro[i] / 100) * 85 + target_distro[i] -= removal + + allele.set_totalreads(sum(target_distro)) + allele.set_cagthreshold(0.50) + + fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, distance_threshold, 'CAGHom', est_dist=estimated_distance) + while fod_failstate: + fod_failstate, cag_indexes = self.peak_detection(allele, target_distro, distance_threshold, 'CAGHom', est_dist=estimated_distance, fod_recall=True) + + ## check that FOD didn't return more items than it was required for this allele + ## only keep discrete values from the inferred total of all calls in the current sample + if not self.sequencepair_object.get_homozygoushaplotype(): + for item in cag_indexes: + if not item in existing_calls: + existing_calls.append(item) + if type(cag_indexes) == np.ndarray: + itemindex = np.where(cag_indexes == item) + allele.set_fodcag(cag_indexes.flat[itemindex]) + else: + allele.set_fodcag(cag_indexes) + else: + if type(cag_indexes) == np.ndarray: + itemindex = np.where(cag_indexes == item) + allele.set_fodcag(cag_indexes.flat[itemindex]) + else: + allele.set_fodcag(cag_indexes) + else: + allele.set_fodcag(cag_indexes) + return pass_gtp + + def genotype_validation(self): + + ## + ## Constructs + pass_vld = True + primary_allele = self.sequencepair_object.get_primaryallele() + secondary_allele = self.sequencepair_object.get_secondaryallele() + pri_distro_split = split_cag_target(primary_allele.get_fwarray()) + sec_distro_split = split_cag_target(secondary_allele.get_fwarray()) + ccg_zygstate = self.zygosity_state + + ## + ## Primary Allele + primary_dsp_ccg = primary_allele.get_ccg(); primary_fod_ccg = primary_allele.get_fodccg() + primary_dsp_cag = primary_allele.get_cag(); primary_fod_cag = primary_allele.get_fodcag() + primary_peakreads = (split_cag_target(primary_allele.get_fwarray())['CCG{}'.format(primary_dsp_ccg)])[ + primary_dsp_cag-1] + primary_allele.set_peakreads(primary_peakreads) + + ## + ## Secondary Allele + secondary_dsp_ccg = secondary_allele.get_ccg(); secondary_fod_ccg = secondary_allele.get_fodccg() + secondary_dsp_cag = secondary_allele.get_cag(); secondary_fod_cag = secondary_allele.get_fodcag() + secondary_peakreads = (split_cag_target(secondary_allele.get_fwarray())['CCG{}'.format(secondary_dsp_ccg)])[ + secondary_dsp_cag - 1] + secondary_allele.set_peakreads(secondary_peakreads) + + ## + ## Double check fod peaks + def dimension_checker(input_list): + + ## data + fod = input_list[0]; dsp = input_list[1];allele = input_list[2] + + ## casting + if type(fod) is np.ndarray: + fod = input_list[0].tolist() + elif type(fod) is not list: + fod = [input_list[0]] + + ## validity between DSP/FOD + for i in range(0, len(fod)): + if np.isclose([fod[i]], [dsp], atol=1.0): + allele.set_fodcag(fod[i]) + + for item in [[primary_fod_cag, primary_dsp_cag, primary_allele], + [secondary_fod_cag, secondary_dsp_cag, secondary_allele]]: + dimension_checker(item) + primary_fod_cag = [primary_allele.get_fodcag()]; secondary_fod_cag = [secondary_allele.get_fodcag()] + + ## + ## Subfunctions + def read_comparison(val1, val2): + if np.isclose(val1, val2, atol=1): + return val2 + else: + return val1 + + def ensure_integrity(): + + ## + ## Ensure integrity + inner_pass = True + try: + if not primary_dsp_ccg == int(primary_fod_ccg): + if read_comparison(primary_dsp_ccg, int(primary_fod_ccg)) == primary_fod_ccg: + self.sequencepair_object.get_primaryallele().set_fodccg(primary_dsp_ccg) + inner_pass = True + else: + inner_pass = False + except TypeError: + self.sequencepair_object.get_primaryallele().set_fodccg(primary_dsp_ccg) + inner_pass = True + + try: + if not primary_dsp_cag == int(primary_fod_cag): + if read_comparison(primary_dsp_cag, int(primary_fod_cag)) == primary_fod_cag: + self.sequencepair_object.get_primaryallele().set_fodcag(primary_dsp_cag) + inner_pass = True + else: + inner_pass = False + except TypeError: + self.sequencepair_object.get_primaryallele().set_fodcag(primary_dsp_cag) + inner_pass = True + + try: + if not secondary_dsp_ccg == int(secondary_fod_ccg): + if read_comparison(secondary_dsp_ccg, int(secondary_fod_ccg)) == secondary_fod_ccg: + self.sequencepair_object.get_secondaryallele().set_fodccg(secondary_dsp_ccg) + inner_pass = True + else: + inner_pass = False + except TypeError: + self.sequencepair_object.get_secondaryallele().set_fodccg(secondary_dsp_ccg) + inner_pass = True + + try: + if not secondary_dsp_cag == int(secondary_fod_cag): + if read_comparison(secondary_dsp_cag, int(secondary_fod_cag)) == secondary_fod_cag: + self.sequencepair_object.get_secondaryallele().set_fodcag(secondary_dsp_cag) + inner_pass = True + else: + inner_pass = False + except TypeError: + self.sequencepair_object.get_secondaryallele().set_fodcag(secondary_dsp_cag) + + return inner_pass + + ## + ## Brute force zygosity + if not (primary_fod_ccg == secondary_fod_ccg) and (ccg_zygstate == 'HOMO' or ccg_zygstate == 'HOMO*' or ccg_zygstate == 'HOMO+'): + self.zygosity_state = 'HETERO'; ccg_zygstate = 'HETERO' + if not self.sequencepair_object.get_svm_failure(): + self.sequencepair_object.set_svm_failure(True) + if (primary_fod_ccg == secondary_fod_ccg) and ccg_zygstate == 'HETERO': + self.zygosity_state = 'HOMO'; ccg_zygstate = 'HOMO' + self.sequencepair_object.set_svm_failure(True) + + ## + ## Check for potential homozygous haplotype/neighbouring peak + if ccg_zygstate == 'HOMO' and np.isclose(primary_dsp_cag, secondary_dsp_cag, atol=1): + primary_target = pri_distro_split['CCG{}'.format(primary_allele.get_ccg())] + secondary_target = sec_distro_split['CCG{}'.format(secondary_allele.get_ccg())] + + primary_reads = primary_target[primary_allele.get_cag()-1] + secondary_reads = secondary_target[secondary_allele.get_cag()-1] + diff = abs(primary_reads-secondary_reads) + pcnt = (diff/max([primary_reads, secondary_reads])) + + ## If read count is so close (and distance is atol=1) + ## Neighbouring peak... + if 0.0 < pcnt < 0.20: + self.sequencepair_object.set_neighbouringpeaks(True) + pass_vld = ensure_integrity() + return pass_vld + if np.isclose([pcnt], [0.25], atol=0.05): + if 0.0 < pcnt <= 0.30: + self.sequencepair_object.set_neighbouringpeaks(True) + pass_vld = ensure_integrity() + return pass_vld + else: + self.sequencepair_object.set_homozygoushaplotype(True) + self.sequencepair_object.set_secondary_allele(self.sequencepair_object.get_primaryallele()) + ##no need to call ensure_integrity as secondary allele is a copy of primary object + return True + + ## fucky bug with homozygotes not filtering properly + ## leaving unassigned value from FOD + try: + primary_fod_cag.all(); secondary_fod_cag.all() + except AttributeError: + secondary_fod_cag = primary_fod_cag + + if primary_fod_cag == secondary_fod_cag: + + self.sequencepair_object.set_homozygoushaplotype(True) + self.sequencepair_object.set_secondary_allele(self.sequencepair_object.get_primaryallele()) + ##no need to call ensure_integrity as secondary allele is a copy of primary object + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + if allele.get_peakreads() < 250: + allele.set_fatalalignmentwarning(True) + self.sequencepair_object.set_fatalreadallele(False) + else: + allele.set_fatalalignmentwarning(False) + self.sequencepair_object.set_fatalreadallele(False) + ## Check if homozygous haplotype & that FW/RV distributions agree... + if self.sequencepair_object.get_homozygoushaplotype(): + inferred_fwarray = [] + ## infer CCG from fw dist (sum x200) + for contig, distribution in pri_distro_split.items(): + inferred_fwarray.append(sum(distribution)) + ## get values for 'peaks' + inferred_fwarray = np.asarray(inferred_fwarray) + fwidx = int(np.where(inferred_fwarray == max(inferred_fwarray))[0][0]) + rvidx = int(np.where(primary_allele.get_rvarray() == max(primary_allele.get_rvarray()))[0][0]) + ## compare against FOD + fwpeak = peakutils.indexes(inferred_fwarray, thres=0.15, min_dist=1) + rvpeak = peakutils.indexes(primary_allele.get_rvarray(), thres=0.15, min_dist=1) + ## suspected peak match, inspect for further noise + if fwidx == rvidx: + if len(rvpeak) > len(fwpeak): + self.sequencepair_object.set_ccguncertainty(True) + if len(fwpeak) > len(rvpeak): + self.sequencepair_object.set_ccguncertainty(True) + return pass_vld + + ## + ## Check for diminished peaks (incase DSP failure / read count is low) + ## Primary read info + primary_dist = split_cag_target(primary_allele.get_fwarray()) + primary_target = primary_dist['CCG{}'.format(primary_allele.get_ccg())] + primary_reads = primary_target[primary_allele.get_cag() - 1] + primary_total = sum(primary_target) + ## Secondary read info + secondary_dist = split_cag_target(secondary_allele.get_fwarray()) + secondary_target = secondary_dist['CCG{}'.format(secondary_allele.get_ccg())] + secondary_reads = secondary_target[secondary_allele.get_cag() - 1] + secondary_total = sum(secondary_target) + + ## Set specifics for zygstate + peak_total = sum([primary_reads, secondary_reads]); dist_total = 0 + if ccg_zygstate == 'HOMO': + dist_total = sum([primary_total]) + if ccg_zygstate == 'HOMO*' or ccg_zygstate == 'HOMO+' or ccg_zygstate == 'HETERO': + dist_total = sum([primary_total, secondary_total]) + + ## + ## In the case where the peak isn't 65% of all current_ccg distribution reads + ## check that there's no diminished peak (i.e. very small expanded peak) + ## Except when atypical; due to re-alignment occurring to a specific ref + ## diminished peaks won't occur + if not peak_total/dist_total >= 0.65: + if np.isclose([peak_total/dist_total], [0.65], atol=0.175): + pass + elif primary_fod_ccg == secondary_fod_ccg and primary_dsp_cag != secondary_dsp_cag: + primary_target = pri_distro_split['CCG{}'.format(primary_allele.get_ccg())] + split_target = primary_target[primary_allele.get_cag()+5:-1] + difference_buffer = len(primary_target)-len(split_target) + fod_failstate, cag_diminished = self.peak_detection(primary_allele, split_target, 1, 'CAGDim') + while fod_failstate: + fod_failstate, cag_diminished = self.peak_detection(primary_allele, split_target, 1, 'CAGDim', fod_recall=True) + if split_target[cag_diminished] > 100: + if not primary_allele.get_allelestatus()=='Atypical' and not secondary_allele.get_allelestatus()=='Atypical': + ## bypass integrity checks + secondary_allele.set_cagval(int(cag_diminished+difference_buffer-1)) + secondary_allele.set_fodcag(int(cag_diminished+difference_buffer-1)) + secondary_allele.set_fodoverwrite(True) + for peak in [primary_reads, secondary_reads]: + if peak < 750: + self.sequencepair_object.set_diminishedpeaks(True) + return pass_vld + + return pass_vld + + def inspect_peaks(self): + + primary_allele = self.sequencepair_object.get_primaryallele() + secondary_allele = self.sequencepair_object.get_secondaryallele() + for allele in [primary_allele, secondary_allele]: + + distribution_split = split_cag_target(allele.get_fwarray()) + target = distribution_split['CCG{}'.format(allele.get_ccg())] + linspace = np.linspace(0,199,200) + + ## + ## Here we set the peak reads for this allele (i.e. number of reads aligned to N) + ## In the case of WOEFUL atypical re-alignments, sometimes more than one 'peak' is found + ## Hence we detect for that, raise an exception as this allele is un-genotype-able + try: + if allele.get_peakreads() < 250: + allele.set_fatalalignmentwarning(True) + self.sequencepair_object.set_fatalreadallele(True) + except ValueError: + if not len(allele.get_peakreads()) == 1: + self.sequencepair_object.set_atypical_alignmentwarning(True) + raise Exception('Atypical re-alignment inaccuracy. Cannot genotype.') + ## + ## fucking weird interp bug filtering + ## Interp a gaussian to suspected peak + warnings.filterwarnings('error') + try: + peaks_interp = peakutils.interpolate(linspace, target, ind=[allele.get_fodcag() - 1]) + if np.isclose([peaks_interp], [allele.get_fodcag() - 1], rtol=0.5): + interp_distance = abs(peaks_interp - float(allele.get_fodcag()) - 1) + allele.set_interpdistance(interp_distance[0]) + else: + allele.raise_interpolation_warning(True) + except Warning: + allele.raise_interpolation_warning(True) + pass + + ## + ## Calculate % of reads located near peak + spread_reads = sum(target[allele.get_cag()-6:allele.get_cag()+5]) + spread_pcnt = (spread_reads/sum(target)) + allele.set_vicinityreads(spread_pcnt) + + ## + ## Calculate peak dropoff + nminus = target[allele.get_cag()-2]; n = target[allele.get_cag()-1]; nplus = target[allele.get_cag()] + nminus_overn = nminus/n; nplus_overn = nplus/n + dropoff_list = [nminus_overn, nplus_overn] + allele.set_immediate_dropoff(dropoff_list) + + ## + ## Sometimes, alignment parameters can result in invalid genotyping (i.e. 2 peaks when expecting 1) + ## Test for this, inform user.. + if self.zygosity_state == 'HETERO': + major = max(target) + majoridx = np.where(target == major)[0][0] + minor = max(n for n in target if n != major) + minoridx = np.where(target == minor)[0][0] + thresh = (major/100)*55 + + if abs(majoridx-minoridx) > 2: + if np.isclose([major],[minor], atol=thresh): + allele.set_unexpectedpeaks(True) + self.pass_vld = False + + ## + ## Slippage + ## Gather from N-2:N-1, sum and ratio:N + nmt = allele.get_cag() - 3; nmo = allele.get_cag() - 1 + slip_ratio = (sum(target[nmt:nmo])) / target[allele.get_cag() - 1] + allele.set_backwardsslippage(slip_ratio) + + rv_ratio = (target[allele.get_fodcag()-2]/target[allele.get_fodcag()-1]) + fw_ratio = (target[allele.get_fodcag()]/target[allele.get_fodcag()-1]) + if not self.sequencepair_object.get_homozygoushaplotype() and not self.sequencepair_object.get_neighbouringpeaks(): + if np.isclose([fw_ratio], [0.85], atol=0.075): + if rv_ratio > 0.65: + allele.set_fodcag(allele.get_fodcag()+1) + allele.set_slippageoverwrite(True) + if np.isclose([rv_ratio], [0.80], atol=0.150): + if fw_ratio > 0.65: + allele.set_fodcag(allele.get_fodcag()-1) + allele.set_slippageoverwrite(True) + ## + ## If we're not homozygous or neighbouring, 'normal' peaks.. + ## Check dropoffs are legitimate and 'clean' + if not self.sequencepair_object.get_homozygoushaplotype() and not self.sequencepair_object.get_neighbouringpeaks(): + self.close_check(allele, nminus_overn, [0.25], 0.02, 1, state='minus') ## inform user + self.close_check(allele, nplus_overn, [0.05], 0.02, 1, state='plus') ## inform user + self.close_check(allele, nminus_overn, [0.35], 0.04, 2, state='minus') ## warn user + self.close_check(allele, nplus_overn, [0.15], 0.03, 2, state='plus') ## warn user + self.close_check(allele, nminus_overn, [0.45], 0.05, 3, state='minus') ## severe warning + self.close_check(allele, nplus_overn, [0.27], 0.03, 3, state='plus') ## severe warning + self.close_check(allele, nminus_overn, [0.60], 0.05, 4, state='minus') ## extreme warning + self.close_check(allele, nplus_overn, [0.37], 0.05, 4, state='plus') ## extreme warning + self.close_check(allele, nminus_overn, [0.75], 0.05, 5, state='minus') ## failure + self.close_check(allele, nplus_overn, [0.65], 0.05, 5, state='plus') ## failure + if nminus_overn > 0.75: allele.set_nminuswarninglevel(6); self.pass_vld = False ## failure + if nplus_overn > 0.65: allele.set_npluswarninglevel(6); self.pass_vld = False ## failure + else: + allele.set_nminuswarninglevel(2) + allele.set_npluswarninglevel(2) + + ## + ## Somatic mosaicism + ## Gather from N+1:N+10, sum and ratio:N + npo = allele.get_cag(); npt = allele.get_cag()+10 + if allele.get_header() == 'PRI': dist = self.primary_original + if allele.get_header() == 'SEC': dist = self.secondary_original + somatic_ratio = (sum(dist[npo:npt]))/dist[allele.get_cag()-1] + allele.set_somaticmosaicism(somatic_ratio) + + ## + ## If we get here; alleles are valid + allele.set_ccgvalid(True) + allele.set_cagvalid(True) + allele.set_genotypestatus(True) + + novel_caacag = allele.get_reflabel().split('_')[1]; novel_ccgcca = allele.get_reflabel().split('_')[2] + allele.set_allelegenotype('{}_{}_{}_{}_{}'.format(allele.get_fodcag(), novel_caacag, + novel_ccgcca, allele.get_fodccg(), + allele.get_cct())) + + ## + ## Check DSP generated allele label vs FOD results + if int(allele.get_reflabel().split('_')[3]) != int(allele.get_fodccg()): + allele.set_referencelabel('{}_{}_{}_{}_{}'.format(allele.get_fodcag(), novel_caacag, + novel_ccgcca, allele.get_fodccg(), + allele.get_cct())) + allele.set_fodoverwrite(True) + if int(allele.get_reflabel().split('_')[0]) != int(allele.get_fodcag()): + allele.set_referencelabel('{}_{}_{}_{}_{}'.format(allele.get_fodcag(), novel_caacag, + novel_ccgcca, allele.get_fodccg(), + allele.get_cct())) + allele.set_fodoverwrite(True) + + ## + ## Homozygous from SVM, genotypes match, allele DSP differ, hzygous flag False + ## aka this sample has an large expanded allele with almost no reads + ## and the pipeline missed it + if self.zygosity_state != 'HETERO': + if not self.sequencepair_object.get_homozygoushaplotype(): + if allele.get_fodoverwrite(): + self.sequencepair_object.set_missed_expansion(True) + self.sequencepair_object.set_diminishedpeaks(True) + + ## + ## If failed, write intermediate data to report + if not self.pass_vld: + inspection_logfi = os.path.join(self.sequencepair_object.get_predictpath(), + '{}{}'.format(allele.get_header(), 'PeakInspectionLog.txt')) + inspection_str = '{} {}\n{}: {}\n{}: {}\n' \ + '{}: {}\n{}: {}\n{}: {}\n' \ + '{}: {}\n{}: {}\n{}: {}\n' \ + '{}: {}\n{}: {}\n'.format( + '>> Peak Inspection Failure','Intermediate results log', + 'Investigating CCG', allele.get_ccg(), + 'Interpolation warning', allele.get_interpolation_warning(), + 'Interpolation distance', allele.get_interpdistance(), + 'Reads (%) surrounding peak', allele.get_vicinityreads(), + 'Peak dropoff', dropoff_list, + 'NMinus ratio', nminus_overn, + 'NMinus warning', allele.get_nminuswarninglevel(), + 'NPlus ratio', nplus_overn, + 'NPlus warning', allele.get_npluswarninglevel(), + 'Unexpected Peaks', allele.get_unexpectedpeaks()) + with open(inspection_logfi,'w') as logfi: + logfi.write(inspection_str) + logfi.close() + + return self.pass_vld + + def n_align_dist(self): + + """ + Function to align alleles of the current sample to the same n-point as any previous alleles processed + in this run. Append the padded distributions to the same CSV file for in-depth somatic mosaicism + studies.. + :return: fuck all + """ + + ## + ## For each allele in the sample get the target CCG distribution + ## Pad it so that N (the determined genotype) is at the same index in the output file + ## output the padded distribution and close the file + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + if allele.get_header() == 'PRI': target = self.primary_original + if allele.get_header() == 'SEC': target = self.secondary_original + fix_target = ','.join(['%.5f' % num for num in target]) + + anchor = 203 + anchor_port = anchor - allele.get_cag() + anchor_starboard = anchor_port + 200 + left_buffer = '-,'*anchor_port + right_buffer = '-,'*(403-anchor_starboard) + padded_dist = left_buffer+fix_target+right_buffer[:-1] + + ### + ### distribution fixed but csv writing incorrect list still + sample_output = '{},{},{},{}\n'.format(self.sequencepair_object.get_label(), allele.get_header(), + allele.get_allelegenotype(), padded_dist) + + with open(self.padded_target, 'a') as distfi: distfi.write(sample_output) + distfi.close() + + def render_graphs(self): + + ## + ## Data for graph rendering (prevents frequent calls/messy code [[lol irony]]) + pri_fodccg = self.sequencepair_object.get_primaryallele().get_fodccg()-1 + sec_fodccg = self.sequencepair_object.get_secondaryallele().get_fodccg()-1 + pri_fodcag = self.sequencepair_object.get_primaryallele().get_fodcag()-1 + sec_fodcag = self.sequencepair_object.get_secondaryallele().get_fodcag()-1 + pri_rvarray = self.sequencepair_object.get_primaryallele().get_rvarray() + sec_rvarray = self.sequencepair_object.get_secondaryallele().get_rvarray() + pri_fwarray = self.sequencepair_object.get_primaryallele().get_fwarray() + sec_fwarray = self.sequencepair_object.get_secondaryallele().get_fwarray() + + predpath = self.sequencepair_object.get_predictpath() + + def graph_subfunction(x, y, axis_labels, xticks, peak_index, predict_path, file_handle, prefix='', graph_type=None, neg_anchor=False): + + #seaborn palette + sns.set(style='darkgrid') + + ## force fonts because matplotlib spam on some people's systems? + plt.rcParams['pdf.fonttype']=42 + plt.rcParams['ps.fonttype']=42 + mpll = log.getLogger('matplotlib'); mpll.setLevel(log.WARNING) + + + x = np.linspace(x[0],x[1],x[2]) + fig, ax = plt.subplots(figsize=(10, 6)); plt.title(prefix+self.sequencepair_object.get_label()) + plt.xlabel(axis_labels[0]); plt.ylabel(axis_labels[1]) + if graph_type == 'bar': + ## ticker forced to integers (instead of floats) + from matplotlib.ticker import FuncFormatter + ## format xtick labels correctly (CCG vs CAG distributions) + if neg_anchor: xtickslabel = xticks[2] + else: xtickslabel = [i-1 for i in xticks[2]] + ## plot bar data, remove x labels + sns.barplot(x=x,y=y); plt.xticks([]) + ## re-add x labels above respective bar plots + for p, dat in zip(ax.patches, xtickslabel): + ax.text(p.get_x() + p.get_width() / 2., p.get_height()+25, dat, ha="center", fontsize=9) + + ## ticker forced to integers (instead of floats) + plt.gca().xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x))) + else: + plt.xticks(np.arange(xticks[0][0], xticks[0][1], xticks[0][2]), xticks[2]) + plt.xlim(xticks[1][0], xticks[1][1]) + pplot(x,y,peak_index) + peak_index = [i+1 for i in peak_index] + plt.legend(['Genotype: {}'.format(peak_index)]) + warnings.simplefilter("ignore") ## ignore open file warning because they're closed after this subfunc + plt.savefig(os.path.join(predict_path, file_handle), format='pdf') + plt.close() + + def pagemerge_subfunction(graph_list, prediction_path, ccg_val, header=None, hplus=False): + + ## + ## Readers and pages + line_reader = PyPDF2.PdfReader(open(graph_list[0], 'rb')); line_page = line_reader.pages[0] + bar_reader = PyPDF2.PdfReader(open(graph_list[1], 'rb')); bar_page = bar_reader.pages[0] + + ## + ## Create new page (double width), append bar and line pages side-by-side (CHANGED MW) + # translated_page = PyPDF2.PageObject.create_blank_page(None, bar_page.mediabox.width, bar_page.mediabox.height) + # bar_transformation = PyPDF2.Transformation().translate(tx=720, ty=0) + # bar_page.add_transformation(bar_transformation) + # translated_page.merge_page(bar_page) + # translated_page.merge_page(line_page) + ## + ## Write to one PDF + if hplus: suffix = 'AtypicalHomozyg' + else: suffix = '' + if not header: output_path = os.path.join(prediction_path, 'CCG{}CAGDetection_{}.pdf'.format(ccg_val, suffix)) + else: output_path = os.path.join(prediction_path, 'IntroCCG.pdf') + writer = PyPDF2.PdfWriter() + writer.add_page(line_page) + writer.add_page(bar_page) + with open(output_path, 'wb') as f: + writer.write(f) + + ## + ## Return CAG plot path + return output_path + + ########################################## + ## SAMPLE CARD FOR GENOTYPE INFORMATION ## + ########################################## + sample_pdf_path = os.path.join(predpath, '{}{}'.format(self.sequencepair_object.get_label(),'.pdf')) + c = canvas.Canvas(sample_pdf_path, pagesize=(720,432)) + header_string = '{}{}'.format('Sample header: ', self.sequencepair_object.get_label()) + primary_string = '{}(CAG{}, CCG{}) ({}; {}; Confidence {}%)'.format('Primary: ', self.sequencepair_object.get_primaryallele().get_fodcag(), + self.sequencepair_object.get_primaryallele().get_fodccg(), + self.sequencepair_object.get_primaryallele().get_allelestatus(), + self.sequencepair_object.get_primaryallele().get_allelegenotype(), + int(self.sequencepair_object.get_primaryallele().get_alleleconfidence())) + secondary_string = '{}(CAG{}, CCG{}) ({}; {}; Confidence {}%)'.format('Secondary: ', self.sequencepair_object.get_secondaryallele().get_fodcag(), + self.sequencepair_object.get_secondaryallele().get_fodccg(), + self.sequencepair_object.get_secondaryallele().get_allelestatus(), + self.sequencepair_object.get_secondaryallele().get_allelegenotype(), + int(self.sequencepair_object.get_secondaryallele().get_alleleconfidence())) + + ########################################################## + ## Create canvas for sample 'intro card' ## + ## Set font colour depending on subsample/invalid/valid ## + ## invalid == atypical allele, no realignment ## + ## valid == atypical allele, realigned ## + ########################################################## + if self.sequencepair_object.get_subsampleflag(): + if self.sequencepair_object.get_subsampleflag() == '0.05**': + pass + elif self.sequencepair_object.get_automatic_DSPsubsample(): + pass + elif float(self.sequencepair_object.get_subsampleflag()) >= 0.5: + pass + else: + c.setFillColorRGB(75, 0, 130) + c.drawCentredString(360, 186, '!! Genotype derived from significantly subsampled data !!') + if self.invalid_data: + c.setFillColorRGB(255, 0, 0) + c.drawCentredString(360, 196, '!! Atypical alleles without re-alignment !!') + if not self.invalid_data: + c.setFillColorRGB(0, 0, 0) + c.drawCentredString(360, 256, header_string) + c.drawCentredString(360, 236, primary_string) + c.drawCentredString(360, 216, secondary_string) + c.save() + + ############################################### + ## CCG heterozygous example ## + ## i.e. CCG two peaks, one CAG dist per peak ## + ############################################### + if self.zygosity_state == 'HETERO' or self.zygosity_state == 'HOMO*' or self.zygosity_state == 'HOMO+': + + ## + ## Render CCG graph, append path to allele path list + ## Merge intro_ccg card with sample CCG graph + ## Append merged intro_ccg to heterozygous list + hetero_graphs = []; ccg_peaks = [int(pri_fodccg),int(sec_fodccg)] + concat = np.asarray([a + b for a, b in zip(pri_rvarray,sec_rvarray)]) + graph_subfunction([0, 21, 20], concat, ['CCG Value', 'Read Count'], ([1, 20, 1], [1, 20], list(range(1,21))), + ccg_peaks, predpath, 'CCGDetection.pdf', graph_type='bar', neg_anchor=True) + intro_card = pagemerge_subfunction([sample_pdf_path, os.path.join(predpath, 'CCGDetection.pdf')], + predpath, ccg_val=0, header=True) + hetero_graphs.append(intro_card) + plt.close() + + ## + ## For each CCG allele in this heterozygous sample + for allele in [self.sequencepair_object.get_primaryallele(),self.sequencepair_object.get_secondaryallele()]: + + ## + ## Data for this allele (peak detection graph) + temp_graphs = [] + + target_distro = [] + if allele.get_header() == 'PRI': + target_distro = self.primary_original + if allele.get_header() == 'SEC': + target_distro = self.secondary_original + + if self.zygosity_state == 'HOMO+': + for i in range(0, len(target_distro)): + if i != allele.get_cag() - 1: + removal = (target_distro[i] / 100) * 75 + target_distro[i] -= removal + + if allele.get_rewrittenccg() is not None: + peak_filename = 'CCG{}-CAGDetection_atypical_ccgdiff.pdf'.format(allele.get_fodccg()) + peak_prefix = '(CCG{}**) '.format(allele.get_fodccg()) + elif allele.get_unrewrittenccg() is not None: + peak_filename = 'CCG{}-CAGDetection_atypical_ccgsame.pdf'.format(allele.get_fodccg()) + peak_prefix = '(CCG{}++) '.format(allele.get_fodccg()) + else: + peak_filename = 'CCG{}-CAGDetection.pdf'.format(allele.get_fodccg()) + peak_prefix = '(CCG{}) '.format(allele.get_fodccg()) + peak_graph_path = os.path.join(predpath, peak_filename) + ## Render the graph, append to list, close plot + graph_subfunction([0, 199, 200], target_distro, ['CAG Value', 'Read Count'], + ([1, 200, 50], [1, 200], [0,50,100,150]), [np.int64(allele.get_fodcag() - 1)], + predpath, peak_filename, prefix=peak_prefix) + temp_graphs.append(peak_graph_path); plt.close() + + ## + ## Inspect the peak (subslice) + slice_range = list(range(allele.get_fodcag()-4, allele.get_fodcag()+7)) + if allele.get_rewrittenccg(): + slice_filename = 'CCG{}-Peak_atypical_ccgdiff.pdf'.format(allele.get_fodccg()) + slice_prefix = '(CCG{}**) '.format(allele.get_ccg()) + elif allele.get_unrewrittenccg(): + slice_filename = 'CCG{}-Peak_atypical_ccgsame.pdf'.format(allele.get_fodccg()) + slice_prefix = '(CCG{}++) '.format(allele.get_ccg()) + else: + slice_filename = 'CCG{}-Peak.pdf'.format(allele.get_fodccg()) + slice_prefix = '(CCG{}) ' .format(allele.get_ccg()) + sub = target_distro[np.int64(allele.get_fodcag()-6):np.int64(allele.get_fodcag()+5)] + ## Render the graph, append to list, close plot + graph_subfunction([0,10,11], sub, ['CAG Value', 'Read Count'], ([1,11,1], [1,11], slice_range), + [np.int64(allele.get_fodcag()-1)], predpath,slice_filename, prefix=slice_prefix, graph_type='bar') + temp_graphs.append(os.path.join(predpath,slice_filename)); plt.close() + + ## + ## Merge 'allele sample' into one page + ccg_val = allele.get_fodccg() + if (allele.get_unrewrittenccg() is not None) or (allele.get_rewrittenccg() is not None): hplus = True + else: hplus = False + merged_graph = pagemerge_subfunction(temp_graphs, predpath, ccg_val, hplus=hplus) + hetero_graphs.append(merged_graph) + + self.sequencepair_object.get_primaryallele().set_allelegraphs(hetero_graphs) + self.sequencepair_object.get_secondaryallele().set_allelegraphs(hetero_graphs) + + ############################################## + ## CCG homozygous example ## + ## i.e. CCG one peak, one CAG dist per peak ## + ############################################## + if self.zygosity_state == 'HOMO': + + ## + ##Data for homozygous graph(s) + homo_graphs = [] + page_graphs = [] + target_ccg = 'CCG{}'.format(self.sequencepair_object.get_primaryallele().get_ccg()) + ## Peak data + peak_filename = 'CCG{}-CAGDetection.pdf'.format(self.sequencepair_object.get_primaryallele().get_fodccg()) + peak_prefix = '(CCG{}) '.format(self.sequencepair_object.get_primaryallele().get_ccg()) + altpeak_filename = 'CCG{}-Peak.pdf'.format(self.sequencepair_object.get_primaryallele().get_fodccg()) + ccg_peaks = [int(pri_fodccg),int(sec_fodccg)]; cag_peaks = [int(pri_fodcag),int(sec_fodcag)] + distribution_split = split_cag_target(pri_fwarray); target_distro = self.primary_original + + ## Subslice data + pri_cag = self.sequencepair_object.get_primaryallele().get_cag() + sec_cag = self.sequencepair_object.get_secondaryallele().get_cag() + upper = max([pri_cag, sec_cag]) + if self.sequencepair_object.get_homozygoushaplotype(): lower = upper + else: lower = max(n for n in [pri_cag, sec_cag] if n != upper) + sub = target_distro[lower-6:upper+5] + slice_range = list(range(lower-4,upper+7)) + + ## + ## Render the graph, append to list, close plot + ## Merge intro_ccg card with sample CCG graph + ## Append merged intro_ccg to homozygous list, append line/bar peak to page list + graph_subfunction([0, 21, 20], pri_rvarray, ['CCG Value', 'Read Count'], ([1, 20, 1], [1, 20], list(range(1,21))), + ccg_peaks, predpath, 'CCGDetection.pdf', graph_type='bar', neg_anchor=True); plt.close() + graph_subfunction([0, 199, 200], target_distro, ['CAG Value', 'Read Count'], + ([1, 200, 50], [1, 200], [0,50,100,150]), cag_peaks, predpath, + peak_filename, prefix=peak_prefix); plt.close() + graph_subfunction([0, len(sub)-1, len(sub)], sub, ['CAG Value', 'Read Count'], + ([1, len(sub), 1], [1, len(sub)], slice_range), cag_peaks, predpath, altpeak_filename, + prefix=peak_prefix, graph_type='bar'); plt.close() + intro_card = pagemerge_subfunction([sample_pdf_path, os.path.join(predpath, 'CCGDetection.pdf')], + predpath, ccg_val=0, header=True) + homo_graphs.append(intro_card) + page_graphs.append(os.path.join(predpath, peak_filename)) + page_graphs.append(os.path.join(predpath, altpeak_filename)) + + ## + ## Merge 'allele sample' into one page + ccg_val = self.sequencepair_object.get_primaryallele().get_fodccg() + merged_graph = pagemerge_subfunction(page_graphs, predpath, ccg_val) + + ## Combine CCG and CAG graphs + homo_graphs.append(merged_graph) + self.sequencepair_object.get_primaryallele().set_allelegraphs(homo_graphs) + self.sequencepair_object.get_secondaryallele().set_allelegraphs(homo_graphs) + + ############################################ + ## Merge graphs into a single summary PDF ## + ############################################ + + ## + ## Allele graphs + ## Ensure uniqueness of entries in primary/secondary (i.e. no duplicating CCG graph) + primary_graphs = self.sequencepair_object.get_primaryallele().get_allelegraphs()[0] + secondary_graphs = self.sequencepair_object.get_primaryallele().get_allelegraphs()[0] + sample_graphs = primary_graphs + secondary_graphs + def set_orderpreserve(seq, idfun=None): + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + if marker in seen: continue + seen[marker] = 1 + result.append(item) + return result + uniques = set_orderpreserve(sample_graphs) + + ## + ## Merge alleles together + merger = PyPDF2.PdfMerger() + for pdf in uniques: + merger.append(pdf) + merger.write(sample_pdf_path) + merger.close() + + ## + ## Remove individual plots + clean_target = [] + for target_file in os.listdir(predpath): + if target_file.endswith(".pdf"): + clean_target.append(os.path.join(predpath, target_file)) + for rmpdf in clean_target: + if not '{}{}'.format(self.sequencepair_object.get_label(),'.pdf') in rmpdf: + os.remove(rmpdf) + + def calculate_score(self): + + ## + ## For both alleles + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + allele_log_fi = os.path.join(self.sequencepair_object.get_predictpath(), '{}{}'.format(allele.get_header(), '_PenaltiesLog.txt')) + with open(allele_log_fi, 'a') as penfi: + penfi.write('{}, {}\n'.format('Flag/Warning','Score Penalty')) + + ## + ## Start score high, deduct for questionable calls.. + allele_confidence = 100 + + ## + ## Sample based genotyping flags + if self.sequencepair_object.get_recallcount() == 7: allele_confidence -= 25; penfi.write('{}, {}\n'.format('Recall Count','-25')) + if 7 > self.sequencepair_object.get_recallcount() > 4: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Recall Count','-15')) + if 4 > self.sequencepair_object.get_recallcount() > 0: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Recall Count', '-5')) + else: allele_confidence += 0; penfi.write('{}, {}\n'.format('Recall Count', '+0')) + + if self.sequencepair_object.get_homozygoushaplotype(): + allele_confidence -= 25; penfi.write('{}, {}\n'.format('Homozygous Haplotype','-25')) + elif self.sequencepair_object.get_neighbouringpeaks(): + allele_confidence -= 15; penfi.write('{}, {}\n'.format('Neighbouring Peaks', '-15')) + else: allele_confidence += 0; penfi.write('{}, {}\n'.format('Normal Data','+0')) + + if self.sequencepair_object.get_diminishedpeaks(): + allele_confidence -= 10; penfi.write('{}, {}\n'.format('Diminished Peaks','-10')) + if allele.get_fodoverwrite(): + allele_confidence -= 20; penfi.write('{}, {}\n'.format('Differential Overwrite','-20')) + if self.sequencepair_object.get_missed_expansion(): + allele_confidence -= 50; penfi.write('{}, {}\n'.format('Missed Expansion', '-50')) + + ## + ## Allele based genotyping flags + ## Allele typical/atypical structure + if allele.get_allelestatus() == 'Atypical': + allele_confidence -= 5; penfi.write('{}, {}\n'.format('Atypical Allele','-5')) + if np.isclose([float(allele.get_atypicalpcnt())],[50.00],atol=5.00): + allele_confidence -= 30; penfi.write('{}, {}\n'.format('Atypical reads (50%)','-30')) + if np.isclose([float(allele.get_atypicalpcnt())],[80.00],atol=20.00): + allele_confidence += 15; penfi.write('{}, {}\n'.format('Atypical reads (80%>)','+15')) + if allele.get_allelestatus() == 'Typical': + allele_confidence += 1; penfi.write('{}, {}\n'.format('Typical Allele', '+1')) + if np.isclose([float(allele.get_typicalpcnt())],[50.00],atol=5.00): + allele_confidence -= 30; penfi.write('{}, {}\n'.format('Typical reads (50%)','-30')) + if np.isclose([float(allele.get_typicalpcnt())],[80.00],atol=15.00): + allele_confidence += 2; penfi.write('{}, {}\n'.format('Typical reads (80%>)','+2')) + + ## + ## Total reads in sample.. + if allele.get_totalreads() > 10000: allele_confidence += 5; penfi.write('{}, {}\n'.format('High total read count', '+5')) + elif allele.get_totalreads() < 1000: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Low total read count', '-15')) + else: allele_confidence += 1; penfi.write('{}, {}\n'.format('Normal total read count','+1')) + + ## + ## Variance of distribution utilised + if allele.get_vicinityreads()*100 > 85.00: allele_confidence += 1; penfi.write('{}, {}\n'.format('Reads near peak','+1')) + elif 84.99 > allele.get_vicinityreads()*100 > 65.00: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Reads near peak','-10')) + elif 64.99 > allele.get_vicinityreads()*100 > 45.00: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Reads near peak','-15')) + elif 44.99 > allele.get_vicinityreads()*100 > 00.00: allele_confidence -= 20; penfi.write('{}, {}\n'.format('Reads near peak','-20')) + + ## + ## Backwards slippage ratio ([N-2:N-1]/N] + if 0.00 < allele.get_backwardsslippage() < 0.10: allele_confidence += 5; penfi.write('{}, {}\n'.format('Backwards slippage','+5')) + elif 0.10 < allele.get_backwardsslippage() < 0.25: allele_confidence += 1; penfi.write('{}, {}\n'.format('Backwards slippage','+1')) + elif allele.get_backwardsslippage() > 0.25: allele_confidence -= 1; penfi.write('{}, {}\n'.format('Backwards slippage','-1')) + elif allele.get_backwardsslippage() > 0.45: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Backwards slippage','-5')) + elif allele.get_backwardsslippage() > 0.65: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Backwards slippage','-10')) + elif 0.65 < allele.get_backwardsslippage() < 1.00: allele_confidence -= 25; penfi.write('{}, {}\n'.format('Backwards slippage','-25')) + if allele.get_slippageoverwrite(): allele_confidence -= 25; penfi.write('{}, {}\n'.format('Slippage overwrite','-25')) + + ## + ## Somatic mosiacisim ratio ([N+1:N+10]/N] + if 0.000 < allele.get_somaticmosaicism() < 0.010: allele_confidence += 10; penfi.write('{}, {}\n'.format('Somatic mosaicism','+10')) + elif 0.010 < allele.get_somaticmosaicism() < 0.015: allele_confidence += 5; penfi.write('{}, {}\n'.format('Somatic mosaicism','+5')) + elif allele.get_somaticmosaicism() > 0.015: allele_confidence -= 1; penfi.write('{}, {}\n'.format('Somatic mosaicism','-1')) + elif allele.get_somaticmosaicism() > 0.025: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Somatic mosaicism','-10')) + elif allele.get_somaticmosaicism() > 0.035: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Somatic mosaicism','-15')) + elif 0.035 < allele.get_somaticmosaicism() < 0.100: allele_confidence -= 20; penfi.write('{}, {}\n'.format('Somatic mosaicism','-20')) + elif allele.get_somaticmosaicism() > 0.100: allele_confidence -= 30; penfi.write('{}, {}\n'.format('Somatic mosaicism','-30')) + + ## + ## Peak calling thresholds + for contig in [allele.get_ccgthreshold(), allele.get_cagthreshold()]: + if contig != 0.5: + if 0.5 > contig > 0.3: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Peak calling threshold','-5')) + if 0.3 > contig > 0.0: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Peak calling threshold','-10')) + else: allele_confidence += 1; penfi.write('{}, {}\n'.format('Peak calling threshold','+1')) + + ## + ## Peak dropoff warnings + for peak_position_error in [allele.get_nminuswarninglevel(), allele.get_npluswarninglevel()]: + if peak_position_error == 0: allele_confidence += 5; penfi.write('{}, {}\n'.format('Surrounding read ratio','+5')) + elif peak_position_error == 1: allele_confidence -= 5; penfi.write('{}, {}\n'.format('Surrounding read ratio', '-5')) + elif 2 >= peak_position_error > 1: allele_confidence -= 7; penfi.write('{}, {}\n'.format('Surrounding read ratio','-7')) + elif peak_position_error >= 5: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Surrounding read ratio','-10')) + else: allele_confidence -= 15; penfi.write('{}, {}\n'.format('Surrounding read ratio','-15')) + + ## + ## Multiply score by a factor if reads were subsampled + if self.sequencepair_object.get_subsampleflag() and not self.sequencepair_object.get_subsampleflag() == '0.05**': + subsample_penalty = []; utilised_subsample_penalty = 0.0; context_penalty = 0.0 + if 0 <= self.sequencepair_object.get_totalseqreads() <= 2000: subsample_penalty = [0.35,0.40,0.45] + if 2000 <= self.sequencepair_object.get_totalseqreads() <= 5000: subsample_penalty = [0.55,0.65,0.70] + if 5000 <= self.sequencepair_object.get_totalseqreads() <= 10000: subsample_penalty = [0.75,0.85,0.95] + if self.sequencepair_object.get_totalseqreads() > 10000: subsample_penalty = [1.0,1.0,1.0] + + if 0.1 <= self.sequencepair_object.get_subsampleflag() <= 0.3: + utilised_subsample_penalty = subsample_penalty[0] + elif 0.3 <= self.sequencepair_object.get_subsampleflag() <= 0.6: + utilised_subsample_penalty = subsample_penalty[1] + elif 0.6 <= self.sequencepair_object.get_subsampleflag() <= 0.9: + utilised_subsample_penalty = subsample_penalty[2] + else: + utilised_subsample_penalty = 1 + + allele_read_ratio = allele.get_totalreads() / self.sequencepair_object.get_totalseqreads() + if np.isclose([allele_read_ratio],[0.05],atol=0.05): context_penalty = 15 + if np.isclose([allele_read_ratio],[0.15],atol=0.05): context_penalty = 10 + if np.isclose([allele_read_ratio],[0.25],atol=0.05): context_penalty = 7 + if np.isclose([allele_read_ratio],[0.35],atol=0.05): context_penalty = 5 + if np.isclose([allele_read_ratio],[0.45],atol=0.05): context_penalty = 5 + if np.isclose([allele_read_ratio],[0.55],atol=0.05): context_penalty = 1 + + ## + ## Due to the nature of neighbour/homozygous peaks, don't cound surrounding reads... + if self.sequencepair_object.get_neighbouringpeaks() or self.sequencepair_object.get_homozygoushaplotype(): + pass + else: + allele_confidence = allele_confidence * utilised_subsample_penalty + allele_confidence -= context_penalty + + penfi.write('{}, *{}\n'.format('Subsample demultiplier', utilised_subsample_penalty)) + penfi.write('{}, -{}\n'.format('Read Ratio Context', context_penalty)) + + ## + ## Mapping percentage + for map_pcnt in [allele.get_fwalnpcnt(), allele.get_rvalnpcnt()]: + if map_pcnt > 90: allele_confidence += 2; penfi.write('{}, {}\n'.format('Mapping percentage', '+2')) + elif 85 < map_pcnt < 90: allele_confidence += 1; penfi.write('{}, {}\n'.format('Mapping percentage', '+1')) + else: allele_confidence -= 2; penfi.write('{}, {}\n'.format('Mapping percentage', '-2')) + + ## + ## Warning penalty.. if triggered, no confidence + if self.warning_triggered: allele_confidence -= 10; penfi.write('{}, {}\n'.format('Peak Inspection warning triggered','-10')) + if allele.get_ccguncertainty(): allele_confidence -= 15; penfi.write('{}, {}\n'.format('CCG Uncertainty','-15')) + if self.sequencepair_object.get_alignmentwarning(): allele_confidence -= 15; penfi.write('{}, {}\n'.format('Low read count alignment warning','-15')) + if self.sequencepair_object.get_atypical_alignmentwarning(): allele_confidence -= 50; penfi.write('{}, {}\n'.format('Atypical re-alignment inaccurate','-50')) + if allele.get_fatalalignmentwarning(): allele_confidence -= 25; penfi.write('{}, {}\n'.format('Fatal low read count alignment warning','-25')) + + ## + ## Differential Confusion + ## i.e two peaks nearby, large difference between suspected but unsure whether homo or neighbour + if allele.get_differential_confusion(): + allele_confidence -= 30; penfi.write('{}, {}\n'.format('Differential Confusion', '-30')) + + ## Heuristic filtering of DSP results state check + if not self.sequencepair_object.get_heuristicfilter(): + allele_confidence -= 20; penfi.write('{}, {}\n'.format('Heuristic filtering of alleles did not assign a secondary allele','-20')) + + ## + ## If reflabel CAG and FOD CAG differ.. no confidence + label_split = allele.get_reflabel().split('_')[0] + if allele.get_allelestatus() == 'Atypical': + if not np.isclose([int(allele.get_fodcag())],[int(label_split)],atol=1): + allele_confidence = 0; penfi.write('{}, {}\n'.format('Atypical DSP:FOD inconsistency','-100')) + + ## + ## Determine score (max out at 100), return genotype + capped_confidence = sorted([0, allele_confidence, 100])[1] + allele.set_alleleconfidence(capped_confidence) + penfi.write('{}, {}\n\n'.format('Final score', capped_confidence)) + penfi.close() + + def contextualise(self): + + ## + ## For both alleles + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + + ## get list of CAG sizes and number of reads aligned to each size1 + cag_sizes = [i for i in range(1,201)] + aligned_distribution = allele.get_fwarray() + raw_repeat_distribution = allele.get_fwarray().copy() + split_distribution = split_cag_target(raw_repeat_distribution) + allele_distribution = split_distribution['CCG{}'.format(allele.get_ccg())] + index = allele.get_cag()-1 + + ## test clean up of distribution for specific alleles + ## todo implement a mechanism by which to remove individual alleles data + ## rather than just anything other than the index of the allele + allele_clearance_range = list(range(allele.get_cag()-11, allele.get_cag()+11)) + for i in range(0, len(allele_distribution)): + if i not in allele_clearance_range: + removal = (allele_distribution[i] / 100) * 85 + allele_distribution[i] -= removal + + ## make 'expanded distribution' of literal count + expanded_distribution = [] + for index, aln_count in zip(cag_sizes, allele_distribution): + to_add = [index] * aln_count + expanded_distribution += to_add + + ## calculate 95% confidence interval given this distribution + a = 1.0 * np.array(expanded_distribution) + n = len(a) + m, se = np.mean(a), sp.stats.sem(a) + h = se * sp.stats.t.ppf((1 + 0.95) / 2., n-1) + lower_ci = int(round(m-h)); upper_ci = int(round(m+h)) + if lower_ci == upper_ci: + if abs(upper_ci - int(allele.get_cag())) > 1: + allele.set_alleleconfinterval('{}-{}'.format(lower_ci, allele.get_cag())) + elif abs(upper_ci - int(allele.get_cag())) == 1: + allele.set_alleleconfinterval('{}-{}'.format(allele.get_cag(), allele.get_cag())) + else: + allele.set_alleleconfinterval('{}-{}'.format(lower_ci, upper_ci)) + else: + if allele.get_cag() > upper_ci: + allele.set_alleleconfinterval('{}-{}'.format(lower_ci, allele.get_cag())) + else: + allele.set_alleleconfinterval('{}-{}'.format(lower_ci, upper_ci)) + + ## haha bad lazy programming wow + if allele.get_alleleconfidence() < 50: + garbage_code = allele.get_alleleconfinterval().split('-') + lower = garbage_code[0]; upper = garbage_code[1] + lower = int(lower)-(int(np.random.randint(1,3,size=1)[0])) + upper = int(upper)+(int(np.random.randint(1,2,size=1)[0])) + allele.set_alleleconfinterval('{}-{}'.format(lower,upper)) + + def set_report(self): + + for allele in [self.sequencepair_object.get_primaryallele(), self.sequencepair_object.get_secondaryallele()]: + + ## + ## Report path for this allele + allele_filestring = '{}{}{}'.format(allele.get_header(),allele.get_allelestatus(), '_AlleleReport.txt') + report_path = os.path.join(self.sequencepair_object.get_predictpath(), allele_filestring) + allele.set_allelereport(report_path) + report_string = '{}{}\n\n{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}' \ + '\n\n{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n\n' \ + '{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}\n{}{}'.format( + 'Allele Report>> ', self.sequencepair_object.get_label(), + 'Summary Information>>', + 'Genotype: ', allele.get_allelegenotype(), + 'Confidence: ', allele.get_alleleconfidence(), + 'CCG Uncertain: ', self.sequencepair_object.get_ccguncertainty(), + 'Structure Status: ', allele.get_allelestatus(), + 'Typical Pcnt: ', allele.get_typicalpcnt(), + 'Atypical Pcnt: ', allele.get_atypicalpcnt(), + 'Total Reads: ', allele.get_totalreads(), + 'Flags>>', + 'Recall Count: ', self.sequencepair_object.get_recallcount(), + 'Homozygous Haplotype: ', self.sequencepair_object.get_homozygoushaplotype(), + 'Neighbouring Peaks: ', self.sequencepair_object.get_neighbouringpeaks(), + 'Diminished Peaks: ', self.sequencepair_object.get_diminishedpeaks(), + 'Backwards Slippage: ', allele.get_backwardsslippage(), + 'Somatic Mosaicism: ', allele.get_somaticmosaicism(), + 'Slippage Overwritten: ', allele.get_slippageoverwrite(), + 'Peak Interpolation Warning: ', allele.get_interpolation_warning(), + 'Peak Interpolation Distance: ', allele.get_interpdistance(), + 'Peak DSP Overwritten: ', allele.get_fodoverwrite(), + 'Low read-count alignment: ', self.sequencepair_object.get_alignmentwarning(), + 'Fatal low read-count: ', allele.get_fatalalignmentwarning(), + 'Data Quality>>', + 'Reads (%) surrounding peak: ', allele.get_vicinityreads(), + 'Immediate Dropoffs: ', allele.get_immediate_dropoff(), + 'N-1 Warning Level: ', allele.get_nminuswarninglevel(), + 'N+1 Warning Level: ', allele.get_npluswarninglevel(), + 'CCG Threshold: ', allele.get_ccgthreshold(), + 'CAG Threshold: ', allele.get_cagthreshold() + ) + ## + ## Write to file + with open(report_path, 'w') as outfi: + outfi.write(report_string) + outfi.close() + + def get_report(self): + + self.allele_report = [self.sequencepair_object.get_primaryallele().get_allelereport(), + self.sequencepair_object.get_secondaryallele().get_allelereport()] + return self.allele_report From 78027262e59148a3098e53469006a3e5a0b09b8c Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 12:38:03 +0000 Subject: [PATCH 08/41] actions ci workflow --- .github/workflows/github_actions_test.yml | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/github_actions_test.yml diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml new file mode 100644 index 0000000..f8ea01c --- /dev/null +++ b/.github/workflows/github_actions_test.yml @@ -0,0 +1,36 @@ +name: Lint and tests +run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 +on: + push: + branches: ["master"] + pull_request: + branches: ["master"] +jobs: + build: + strategy: + fail-fast: false + matrix: + python-version: ["3.8"] + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + pip install . + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest From f457bf38918056a4f6ee83075b9549e2ba66b49e Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 12:41:17 +0000 Subject: [PATCH 09/41] correct install dir --- .github/workflows/github_actions_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index f8ea01c..5b79f11 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,7 +24,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flake8 pytest - pip install . + pip install src/ - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names From 18f0efbf34227dd5f0975a6c9dc8886fd4300d21 Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 12:53:55 +0000 Subject: [PATCH 10/41] downgrade setuptools --- src/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup.py b/src/setup.py index 822abcb..2a98053 100755 --- a/src/setup.py +++ b/src/setup.py @@ -90,7 +90,7 @@ 'cutadapt==1.18', 'pyvcf', 'fadapa', - 'setuptools' + 'setuptools<58.0.0' ], # These are the data files to be included in the package From d9d2d01226f9962464c33d032192c64de0890e39 Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 14:10:51 +0000 Subject: [PATCH 11/41] force downgrade of setuptools --- .github/workflows/github_actions_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 5b79f11..437802d 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -23,6 +23,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip + python -m pip install "setuptools<58.0.0" python -m pip install flake8 pytest pip install src/ - name: Lint with flake8 From b7e3583d2149fc8c7661741060214230ae44c317 Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 14:36:23 +0000 Subject: [PATCH 12/41] upgrade pyvcf --- src/setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/setup.py b/src/setup.py index 2a98053..300468f 100755 --- a/src/setup.py +++ b/src/setup.py @@ -88,9 +88,8 @@ 'generatr==1.0', 'batchadapt==1.2', # 1.0 was yanked 'cutadapt==1.18', - 'pyvcf', + 'PyVCF3', 'fadapa', - 'setuptools<58.0.0' ], # These are the data files to be included in the package From 368ffe87742c8c02ff4d7b23829d284faad97449 Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 14:50:32 +0000 Subject: [PATCH 13/41] fix linting --- .github/workflows/github_actions_test.yml | 4 ++-- src/ScaleHD/__backend.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 437802d..87cbfc7 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -29,9 +29,9 @@ jobs: - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 src/ --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=build # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-line-length=127 --statistics + flake8 src/ --count --exit-zero --max-line-length=127 --statistics --exclude=build - name: Test with pytest run: | pytest diff --git a/src/ScaleHD/__backend.py b/src/ScaleHD/__backend.py index bc69b9f..bbebe18 100755 --- a/src/ScaleHD/__backend.py +++ b/src/ScaleHD/__backend.py @@ -453,7 +453,7 @@ def sanitise_inputs(parsed_arguments): ## Jobname prefix validity check if parsed_arguments.jobname: for character in parsed_arguments.jobname: - if character is ' ' or character is '/': + if character == ' ' or character == '/': log.error('{}{}{}{}{}{}'.format(Colour.red,'shd__ ',Colour.end,'Specified Job Name has invalid characters: "', character, '"')) trigger = True From 2328e5f3f812cabb59394d0e480d8604a19613d5 Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 14:55:30 +0000 Subject: [PATCH 14/41] basic test for functionality of ci/cd --- test/test_square.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/test_square.py diff --git a/test/test_square.py b/test/test_square.py new file mode 100644 index 0000000..71c5d78 --- /dev/null +++ b/test/test_square.py @@ -0,0 +1,5 @@ +import math + +def test_sqrt(): + num = 25 + assert math.sqrt(num) == 5 From 0510131b1b471593672666db65725374973d3f5a Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 15:33:29 +0000 Subject: [PATCH 15/41] basic integration test to check that ScaleHD can be called from command line --- src/setup.py | 1 + test/test_integration.py | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 test/test_integration.py diff --git a/src/setup.py b/src/setup.py index 300468f..f839c3f 100755 --- a/src/setup.py +++ b/src/setup.py @@ -90,6 +90,7 @@ 'cutadapt==1.18', 'PyVCF3', 'fadapa', + 'pytest', ], # These are the data files to be included in the package diff --git a/test/test_integration.py b/test/test_integration.py new file mode 100644 index 0000000..02a5ee3 --- /dev/null +++ b/test/test_integration.py @@ -0,0 +1,8 @@ +import pytest +import subprocess + +@pytest.mark.integration +def test_can_call(): + subprocess.call([ + "ScaleHD" + ]) From 4d00781332eb7476b1452c064666832541bf3183 Mon Sep 17 00:00:00 2001 From: haessar Date: Sun, 24 Nov 2024 16:13:30 +0000 Subject: [PATCH 16/41] basic unit and integration tests as example --- pytest.ini | 4 ++++ test/integration/__init__.py | 0 test/{ => integration}/test_integration.py | 0 test/unit/__init__.py | 0 test/unit/test_align.py | 19 +++++++++++++++++++ 5 files changed, 23 insertions(+) create mode 100644 pytest.ini create mode 100644 test/integration/__init__.py rename test/{ => integration}/test_integration.py (100%) create mode 100644 test/unit/__init__.py create mode 100644 test/unit/test_align.py diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..0738f71 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration diff --git a/test/integration/__init__.py b/test/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_integration.py b/test/integration/test_integration.py similarity index 100% rename from test/test_integration.py rename to test/integration/test_integration.py diff --git a/test/unit/__init__.py b/test/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/test_align.py b/test/unit/test_align.py new file mode 100644 index 0000000..616c19d --- /dev/null +++ b/test/unit/test_align.py @@ -0,0 +1,19 @@ +import os.path +import shutil + +import pytest + +from ScaleHD.align.__alignment import ReferenceIndex + + +@pytest.fixture(scope='function') +def setUp_tearDown(request): + def tearDown(): + shutil.rmtree("4k-HD-INTER") + request.addfinalizer(tearDown) + + +def test_reference_index(setUp_tearDown): + reference_file = "refs/4k-HD-INTER.fa" + ri = ReferenceIndex(reference_file, target_output="") + assert os.path.basename(ri.get_index_path()) == os.path.basename(reference_file) From fd1862e77b80a5dd2daca3b918c7fa14eb7e859f Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 10:26:56 +0000 Subject: [PATCH 17/41] fix test_align and assert all index files exist --- test/unit/test_align.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/unit/test_align.py b/test/unit/test_align.py index 616c19d..9236d8e 100644 --- a/test/unit/test_align.py +++ b/test/unit/test_align.py @@ -1,3 +1,4 @@ +from glob import glob import os.path import shutil @@ -7,13 +8,18 @@ @pytest.fixture(scope='function') -def setUp_tearDown(request): +def setUp(request): + basename = "ref_sample" def tearDown(): - shutil.rmtree("4k-HD-INTER") + shutil.rmtree(basename) request.addfinalizer(tearDown) + return { + "basename": basename, + } -def test_reference_index(setUp_tearDown): - reference_file = "refs/4k-HD-INTER.fa" +def test_reference_index(setUp): + reference_file = "test/refs/{}.fa".format(setUp["basename"]) ri = ReferenceIndex(reference_file, target_output="") assert os.path.basename(ri.get_index_path()) == os.path.basename(reference_file) + assert len(glob(ri.get_index_path() + ".*")) == 5 From cf7041a1f5702c2e518ea8a7ea3e32c3b89f1129 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 10:52:52 +0000 Subject: [PATCH 18/41] test ref files --- test/refs/ref_sample.fa | 6 ++++++ test/refs/ref_sample.fas | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 test/refs/ref_sample.fa create mode 100644 test/refs/ref_sample.fas diff --git a/test/refs/ref_sample.fa b/test/refs/ref_sample.fa new file mode 100644 index 0000000..6237526 --- /dev/null +++ b/test/refs/ref_sample.fa @@ -0,0 +1,6 @@ +>1_1_1_1_2 +GCGACCCTGGAAAAGCTGATGAAGGCCTTCGAGTCCCTCAAGTCCTTCCAGCAACAGCCGCCACCGCCTCCTCAGCTTCCTCAGCCGCCGCCGCAGGCACAGCCGCTGCT +>2_1_1_1_2 +GCGACCCTGGAAAAGCTGATGAAGGCCTTCGAGTCCCTCAAGTCCTTCCAGCAGCAACAGCCGCCACCGCCTCCTCAGCTTCCTCAGCCGCCGCCGCAGGCACAGCCGCTGCT +>3_1_1_1_2 +GCGACCCTGGAAAAGCTGATGAAGGCCTTCGAGTCCCTCAAGTCCTTCCAGCAGCAGCAACAGCCGCCACCGCCTCCTCAGCTTCCTCAGCCGCCGCCGCAGGCACAGCCGCTGCT diff --git a/test/refs/ref_sample.fas b/test/refs/ref_sample.fas new file mode 100644 index 0000000..6237526 --- /dev/null +++ b/test/refs/ref_sample.fas @@ -0,0 +1,6 @@ +>1_1_1_1_2 +GCGACCCTGGAAAAGCTGATGAAGGCCTTCGAGTCCCTCAAGTCCTTCCAGCAACAGCCGCCACCGCCTCCTCAGCTTCCTCAGCCGCCGCCGCAGGCACAGCCGCTGCT +>2_1_1_1_2 +GCGACCCTGGAAAAGCTGATGAAGGCCTTCGAGTCCCTCAAGTCCTTCCAGCAGCAACAGCCGCCACCGCCTCCTCAGCTTCCTCAGCCGCCGCCGCAGGCACAGCCGCTGCT +>3_1_1_1_2 +GCGACCCTGGAAAAGCTGATGAAGGCCTTCGAGTCCCTCAAGTCCTTCCAGCAGCAGCAACAGCCGCCACCGCCTCCTCAGCTTCCTCAGCCGCCGCCGCAGGCACAGCCGCTGCT From 4b0588deb1a81170b1420ba6b437b445314e79d6 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 12:11:40 +0000 Subject: [PATCH 19/41] Install bwa dependancy --- .github/workflows/github_actions_test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 87cbfc7..cc62dfc 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -20,6 +20,13 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} + - name: Install bwa + uses: jaxxstorm/action-install-gh-release@v1.10.0 + with: + repo: lh3/bwa + tag: v0.7.17 + extension: "\\.bz2" + cache: enable - name: Install Python dependencies run: | python -m pip install --upgrade pip From 2288b6a0f92dae25c49bab5612bd014082464b22 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 12:21:45 +0000 Subject: [PATCH 20/41] try later version --- .github/workflows/github_actions_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index cc62dfc..c4a0fb8 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -21,7 +21,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install bwa - uses: jaxxstorm/action-install-gh-release@v1.10.0 + uses: jaxxstorm/action-install-gh-release@v1.13.0 with: repo: lh3/bwa tag: v0.7.17 From ed2a7be57a67e843c86cd22411d646d20fc0c6e9 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 12:23:25 +0000 Subject: [PATCH 21/41] try tweaking tag --- .github/workflows/github_actions_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index c4a0fb8..9ff23c9 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,7 +24,7 @@ jobs: uses: jaxxstorm/action-install-gh-release@v1.13.0 with: repo: lh3/bwa - tag: v0.7.17 + tag: 0.7.17 extension: "\\.bz2" cache: enable - name: Install Python dependencies From fac775e131887ce959ab9122c67d82ee0b4d2881 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:26:44 +0000 Subject: [PATCH 22/41] set explicit asset name --- .github/workflows/github_actions_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 9ff23c9..09a8408 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,8 +24,8 @@ jobs: uses: jaxxstorm/action-install-gh-release@v1.13.0 with: repo: lh3/bwa - tag: 0.7.17 - extension: "\\.bz2" + tag: v0.7.17 + asset-name: bwa-0.7.17.tar.bz2 cache: enable - name: Install Python dependencies run: | From 69571a83ab70325f6371c66749b23c1fe1c122d1 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:28:00 +0000 Subject: [PATCH 23/41] ...with ext --- .github/workflows/github_actions_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 09a8408..79bff80 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -26,6 +26,7 @@ jobs: repo: lh3/bwa tag: v0.7.17 asset-name: bwa-0.7.17.tar.bz2 + extension: "\\.bz2" cache: enable - name: Install Python dependencies run: | From 7134ea5ca751dd2b8dcc89b9243ae428aef7be48 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:29:23 +0000 Subject: [PATCH 24/41] swap asset name and tag --- .github/workflows/github_actions_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 79bff80..b9c0313 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,8 +24,8 @@ jobs: uses: jaxxstorm/action-install-gh-release@v1.13.0 with: repo: lh3/bwa - tag: v0.7.17 - asset-name: bwa-0.7.17.tar.bz2 + asset-name: v0.7.17 + tag: 0.7.17 extension: "\\.bz2" cache: enable - name: Install Python dependencies From 72d2d2f1c8402e37cb0778860ecf8126ddc37957 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:32:03 +0000 Subject: [PATCH 25/41] retry --- .github/workflows/github_actions_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index b9c0313..575ab85 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,8 +24,8 @@ jobs: uses: jaxxstorm/action-install-gh-release@v1.13.0 with: repo: lh3/bwa - asset-name: v0.7.17 - tag: 0.7.17 + tag: v0.7.17 + asset-name: bwa-0.7.17 extension: "\\.bz2" cache: enable - name: Install Python dependencies From 2d4f118ae101b6655d735c14c80d0f607ed1df11 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:39:52 +0000 Subject: [PATCH 26/41] try different action --- .github/workflows/github_actions_test.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 575ab85..d720019 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -21,13 +21,11 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install bwa - uses: jaxxstorm/action-install-gh-release@v1.13.0 + uses: sigoden/install-binary@v1 with: repo: lh3/bwa tag: v0.7.17 - asset-name: bwa-0.7.17 - extension: "\\.bz2" - cache: enable + name: bwa-0.7.17.tar.bz2 - name: Install Python dependencies run: | python -m pip install --upgrade pip From 48889c9d45d24e039960492fc918b58f583a0722 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:41:16 +0000 Subject: [PATCH 27/41] remove tag --- .github/workflows/github_actions_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index d720019..953bc9f 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,7 +24,7 @@ jobs: uses: sigoden/install-binary@v1 with: repo: lh3/bwa - tag: v0.7.17 + # tag: v0.7.17 name: bwa-0.7.17.tar.bz2 - name: Install Python dependencies run: | From b8d0315d7393eed0af3b144c2b32fee09caec7f9 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:42:57 +0000 Subject: [PATCH 28/41] remove name --- .github/workflows/github_actions_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 953bc9f..01d81f0 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -24,8 +24,8 @@ jobs: uses: sigoden/install-binary@v1 with: repo: lh3/bwa - # tag: v0.7.17 - name: bwa-0.7.17.tar.bz2 + tag: v0.7.17 + # name: bwa-0.7.17.tar.bz2 - name: Install Python dependencies run: | python -m pip install --upgrade pip From 0ab6e72c7d82e61541fc7e722b473eb3b6f02410 Mon Sep 17 00:00:00 2001 From: haessar Date: Mon, 25 Nov 2024 16:51:46 +0000 Subject: [PATCH 29/41] use another action --- .github/workflows/github_actions_test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 01d81f0..761e868 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -21,11 +21,11 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install bwa - uses: sigoden/install-binary@v1 + uses: giantswarm/install-binary-action@v3 with: - repo: lh3/bwa - tag: v0.7.17 - # name: bwa-0.7.17.tar.bz2 + binary: "bwa" + version: "0.7.17" + download_url: "https://deac-riga.dl.sourceforge.net/project/bio-${binary}/${binary}-${version}.tar.bz2" - name: Install Python dependencies run: | python -m pip install --upgrade pip From 51078f99240ba30598946b53e67e797dea321644 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 21:40:07 +0000 Subject: [PATCH 30/41] try with conda --- .github/workflows/github_actions_test.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 761e868..7d72161 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -17,15 +17,16 @@ jobs: - name: Check out repository code uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + # uses: actions/setup-python@v3 + # with: + # python-version: ${{ matrix.python-version }} + uses: conda-incubator/setup-miniconda@v3 with: + auto-update-conda: true python-version: ${{ matrix.python-version }} - name: Install bwa - uses: giantswarm/install-binary-action@v3 - with: - binary: "bwa" - version: "0.7.17" - download_url: "https://deac-riga.dl.sourceforge.net/project/bio-${binary}/${binary}-${version}.tar.bz2" + shell: bash -el {0} + run: conda install bioconda/label/cf201901::bwa - name: Install Python dependencies run: | python -m pip install --upgrade pip From 698330462c86baee0bed808df78e37e7a97ea8f0 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 21:50:15 +0000 Subject: [PATCH 31/41] specify shell --- .github/workflows/github_actions_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 7d72161..50ed757 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -28,6 +28,7 @@ jobs: shell: bash -el {0} run: conda install bioconda/label/cf201901::bwa - name: Install Python dependencies + shell: bash -el {0} run: | python -m pip install --upgrade pip python -m pip install "setuptools<58.0.0" From ad78d73b8b642de4d807f3c3acfa00d83ef78c57 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 21:53:16 +0000 Subject: [PATCH 32/41] set default shell --- .github/workflows/github_actions_test.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 50ed757..1b8c1e5 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -13,6 +13,9 @@ jobs: python-version: ["3.8"] os: [ubuntu-latest] runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash -el {0} steps: - name: Check out repository code uses: actions/checkout@v4 @@ -25,10 +28,10 @@ jobs: auto-update-conda: true python-version: ${{ matrix.python-version }} - name: Install bwa - shell: bash -el {0} + # shell: bash -el {0} run: conda install bioconda/label/cf201901::bwa - name: Install Python dependencies - shell: bash -el {0} + # shell: bash -el {0} run: | python -m pip install --upgrade pip python -m pip install "setuptools<58.0.0" From a48a7978badef679b2a1c9b0b2cd016d8b428874 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 22:02:16 +0000 Subject: [PATCH 33/41] tidy up workflow --- .github/workflows/github_actions_test.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index 1b8c1e5..aa78ae8 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -1,5 +1,4 @@ name: Lint and tests -run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 on: push: branches: ["master"] @@ -20,18 +19,13 @@ jobs: - name: Check out repository code uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - # uses: actions/setup-python@v3 - # with: - # python-version: ${{ matrix.python-version }} uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true python-version: ${{ matrix.python-version }} - name: Install bwa - # shell: bash -el {0} run: conda install bioconda/label/cf201901::bwa - name: Install Python dependencies - # shell: bash -el {0} run: | python -m pip install --upgrade pip python -m pip install "setuptools<58.0.0" From 8b50ef65cb256906e816d0c54838225684e98536 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 22:24:33 +0000 Subject: [PATCH 34/41] add cron job to run workflow once per month --- .github/workflows/github_actions_test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/github_actions_test.yml b/.github/workflows/github_actions_test.yml index aa78ae8..7523090 100644 --- a/.github/workflows/github_actions_test.yml +++ b/.github/workflows/github_actions_test.yml @@ -4,6 +4,9 @@ on: branches: ["master"] pull_request: branches: ["master"] + schedule: + # ...or run once a month + - cron: "0 0 1 * *" jobs: build: strategy: From 1b7c11dc03bd8485a9ccb2e4a42052ba203513f9 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 22:34:23 +0000 Subject: [PATCH 35/41] remove pyc from version control --- src/ScaleHD/__allelecontainer.pyc | Bin 43396 -> 0 bytes src/ScaleHD/__backend.pyc | Bin 29946 -> 0 bytes src/ScaleHD/__init__.pyc | Bin 361 -> 0 bytes src/ScaleHD/align/__alignment.pyc | Bin 12759 -> 0 bytes src/ScaleHD/align/__atypical.pyc | Bin 24384 -> 0 bytes src/ScaleHD/align/__init__.pyc | Bin 495 -> 0 bytes src/ScaleHD/genHTML/__generateHTML.pyc | Bin 27978 -> 0 bytes src/ScaleHD/genHTML/__init__.pyc | Bin 221 -> 0 bytes src/ScaleHD/predict/__init__.pyc | Bin 366 -> 0 bytes src/ScaleHD/predict/__prediction.pyc | Bin 53828 -> 0 bytes src/ScaleHD/predict/__snpcalling.pyc | Bin 6303 -> 0 bytes src/ScaleHD/seq_qc/__init__.pyc | Bin 274 -> 0 bytes src/ScaleHD/seq_qc/__quality_control.pyc | Bin 8848 -> 0 bytes src/ScaleHD/sherpa.pyc | Bin 25067 -> 0 bytes 14 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/ScaleHD/__allelecontainer.pyc delete mode 100644 src/ScaleHD/__backend.pyc delete mode 100644 src/ScaleHD/__init__.pyc delete mode 100644 src/ScaleHD/align/__alignment.pyc delete mode 100644 src/ScaleHD/align/__atypical.pyc delete mode 100644 src/ScaleHD/align/__init__.pyc delete mode 100644 src/ScaleHD/genHTML/__generateHTML.pyc delete mode 100644 src/ScaleHD/genHTML/__init__.pyc delete mode 100644 src/ScaleHD/predict/__init__.pyc delete mode 100644 src/ScaleHD/predict/__prediction.pyc delete mode 100644 src/ScaleHD/predict/__snpcalling.pyc delete mode 100644 src/ScaleHD/seq_qc/__init__.pyc delete mode 100644 src/ScaleHD/seq_qc/__quality_control.pyc delete mode 100644 src/ScaleHD/sherpa.pyc diff --git a/src/ScaleHD/__allelecontainer.pyc b/src/ScaleHD/__allelecontainer.pyc deleted file mode 100644 index 6a09ec6fdab54f9db777945beef7f370c4acbe44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43396 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 diff --git a/src/ScaleHD/align/__alignment.pyc b/src/ScaleHD/align/__alignment.pyc deleted file mode 100644 index 69b7e826a2539184a4c865bbb82d4c5614f2a96d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12759 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 diff --git a/src/ScaleHD/genHTML/__generateHTML.pyc b/src/ScaleHD/genHTML/__generateHTML.pyc deleted file mode 100644 index e1b0f2c1e3e0f0a5f0619be602586da02a6659a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27978 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 O`>| z0#YUjB;w=KQ}a@b5=&A&LVSJn3Mw=I1Azu3P^yF-NC0I)5`G%YFp>E9#GIV?_!2%0 z-9bPR1`SlASQ^L=PEO28^>ERL8m1o~pP83g5+4t=u7m?9ZWjE0=*{DO-!Ur=2D zuV`OhsnQ(+bC^tLTd|{m45v94eP|o!tnZPte`sZbV%Z?PI%k}DGk%m)K*fc*2eUI` zronpiag;^{<8mqnXNB9QXKAM1$Y;4yLG{6mtC(ed-Y<44V4Aawp-PQZ`WvP4ONbAE jd)PMbyX~y9WwsvoGM*vV;Gc%EJ38~MB2gWzlMVVmuAWme diff --git a/src/ScaleHD/predict/__prediction.pyc b/src/ScaleHD/predict/__prediction.pyc deleted file mode 100644 index f95b05276ea9f78c096483effef1a1dc12b8a6a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53828 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 (UL*3nUqn9Bn_U-f&fR8)a2dll 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 diff --git a/src/ScaleHD/seq_qc/__quality_control.pyc b/src/ScaleHD/seq_qc/__quality_control.pyc deleted file mode 100644 index da06d40730782790004e83db5dd76aaf921f7dc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8848 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 From 85e549d7274f784303a12fa7ab7af91b05e5fe63 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 22:34:47 +0000 Subject: [PATCH 36/41] ignore *.pyc --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9f2e848..2348c91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store __pycache__/ +*.pyc From 2fa9b9074db941b6cc539968930f3a5bad826093 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 23:12:02 +0000 Subject: [PATCH 37/41] add test for unknown file type --- test/integration/test_integration.py | 1 + test/unit/test_align.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/test/integration/test_integration.py b/test/integration/test_integration.py index 02a5ee3..4e671d0 100644 --- a/test/integration/test_integration.py +++ b/test/integration/test_integration.py @@ -1,6 +1,7 @@ import pytest import subprocess + @pytest.mark.integration def test_can_call(): subprocess.call([ diff --git a/test/unit/test_align.py b/test/unit/test_align.py index 9236d8e..03cf9db 100644 --- a/test/unit/test_align.py +++ b/test/unit/test_align.py @@ -1,4 +1,5 @@ from glob import glob +import logging import os.path import shutil @@ -23,3 +24,11 @@ def test_reference_index(setUp): ri = ReferenceIndex(reference_file, target_output="") assert os.path.basename(ri.get_index_path()) == os.path.basename(reference_file) assert len(glob(ri.get_index_path() + ".*")) == 5 + + +def test_unknown_file_ext(setUp, caplog): + # Requires .fasta or .fa, not .fas + reference_file = "test/refs/{}.fas".format(setUp["basename"]) + with caplog.at_level(logging.CRITICAL): + ReferenceIndex(reference_file, target_output="") + assert "CRITICAL" in caplog.text From 7aa4d569517e05f5b4bfb77c728468f880671f75 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 23:24:45 +0000 Subject: [PATCH 38/41] add to changelog --- src/CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/CHANGELOG.rst b/src/CHANGELOG.rst index e20929c..35c1f76 100644 --- a/src/CHANGELOG.rst +++ b/src/CHANGELOG.rst @@ -1,5 +1,12 @@ .. _sect_changelog: +Version 1.1.0 +------------- + +* Upgrade to Python 3.8 +* Initial wave of integration and unit tests using pytest +* Implement Continuous integration using GitHub Actions workflow. Build and run linting and tests to succeed. Triggered by PR/Push to master or monthly cron. + Version 1.0 ----------- From 20485f46c170288a0435d72c93ba264787c6430c Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 23:50:51 +0000 Subject: [PATCH 39/41] increment semver --- src/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup.py b/src/setup.py index f839c3f..87fe3ad 100755 --- a/src/setup.py +++ b/src/setup.py @@ -13,7 +13,7 @@ name='ScaleHD', # https://packaging.python.org/en/latest/single_source_version.html - version='1.0', + version='1.1.0', description='Automated DNA micro-satellite genotyping.', long_description=long_description, From 288402f342cdf485482c877a15196ba0b3dfe5b1 Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 23:58:36 +0000 Subject: [PATCH 40/41] remove redundant __version__ and __author__ tags. These only need to be defined once in the setup.py --- src/ScaleHD/__backend.py | 2 -- src/ScaleHD/__init__.py | 2 -- src/ScaleHD/__main__.py | 1 - src/ScaleHD/align/__alignment.py | 2 -- src/ScaleHD/genHTML/__generateHTML.py | 2 -- src/ScaleHD/predict/__prediction.py | 4 ---- src/ScaleHD/predict/__snpcalling.py | 2 -- src/ScaleHD/seq_qc/__quality_control.py | 2 -- src/ScaleHD/sherpa.py | 11 +++++------ 9 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/ScaleHD/__backend.py b/src/ScaleHD/__backend.py index bbebe18..cd5b325 100755 --- a/src/ScaleHD/__backend.py +++ b/src/ScaleHD/__backend.py @@ -1,6 +1,4 @@ #/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' ## ## Imports diff --git a/src/ScaleHD/__init__.py b/src/ScaleHD/__init__.py index c4878d0..9d58709 100755 --- a/src/ScaleHD/__init__.py +++ b/src/ScaleHD/__init__.py @@ -4,5 +4,3 @@ '__alleleface.py', '__init__.py', '__main__.py'] -__author__='alastair.maxwell@glasgow.ac.uk' -__version__='1.0' diff --git a/src/ScaleHD/__main__.py b/src/ScaleHD/__main__.py index b9dea3d..6fd84bc 100755 --- a/src/ScaleHD/__main__.py +++ b/src/ScaleHD/__main__.py @@ -1,3 +1,2 @@ -__version__ = '1.0' from .sherpa import main main() diff --git a/src/ScaleHD/align/__alignment.py b/src/ScaleHD/align/__alignment.py index 4366a92..416d069 100755 --- a/src/ScaleHD/align/__alignment.py +++ b/src/ScaleHD/align/__alignment.py @@ -1,6 +1,4 @@ #/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' ## ## Generals diff --git a/src/ScaleHD/genHTML/__generateHTML.py b/src/ScaleHD/genHTML/__generateHTML.py index 9da6a60..1e57290 100755 --- a/src/ScaleHD/genHTML/__generateHTML.py +++ b/src/ScaleHD/genHTML/__generateHTML.py @@ -1,6 +1,4 @@ #!/bin/bash/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' ## imports import os diff --git a/src/ScaleHD/predict/__prediction.py b/src/ScaleHD/predict/__prediction.py index 74447ba..efebc41 100755 --- a/src/ScaleHD/predict/__prediction.py +++ b/src/ScaleHD/predict/__prediction.py @@ -1,8 +1,4 @@ - - #/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' ## ## Generic imports diff --git a/src/ScaleHD/predict/__snpcalling.py b/src/ScaleHD/predict/__snpcalling.py index ff3f825..fe88763 100644 --- a/src/ScaleHD/predict/__snpcalling.py +++ b/src/ScaleHD/predict/__snpcalling.py @@ -1,6 +1,4 @@ #/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' import os import vcf diff --git a/src/ScaleHD/seq_qc/__quality_control.py b/src/ScaleHD/seq_qc/__quality_control.py index c8aa3bc..401b31b 100755 --- a/src/ScaleHD/seq_qc/__quality_control.py +++ b/src/ScaleHD/seq_qc/__quality_control.py @@ -1,6 +1,4 @@ #/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' ## ## Generals diff --git a/src/ScaleHD/sherpa.py b/src/ScaleHD/sherpa.py index 735f5ca..162614c 100755 --- a/src/ScaleHD/sherpa.py +++ b/src/ScaleHD/sherpa.py @@ -1,6 +1,4 @@ #/usr/bin/python -__version__ = '1.0' -__author__ = 'alastair.maxwell@glasgow.ac.uk' ## ## Python libraries @@ -17,6 +15,7 @@ from shutil import copyfile from reportlab.pdfgen import canvas from multiprocessing import cpu_count +from importlib.metadata import version ## ## Backend junk @@ -100,7 +99,7 @@ def __init__(self): pass if sys.version_info[2] < 6: current_user_version = '{}.{}.{}'.format(sys.version_info[0], sys.version_info[1], sys.version_info[2]) - log.error('{}{}{}{}{}.'.format(clr.red, 'shd__ ', clr.end, 'ScaleHD requires python3 3.7.6 or later!' + log.error('{}{}{}{}{}.'.format(clr.red, 'shd__ ', clr.end, 'ScaleHD requires python version 3.8 or later!' ' You are using: ', current_user_version)) sys.exit(2) @@ -476,10 +475,10 @@ def collate_graphs(self, sequencepair_object): ## ## Merge sample summary PDF with instance-wide PDF - merger = PyPDF2.PdfMerger() + merger = PyPDF2.PdfMerger() for filename in [self.instance_graphs, sample_pdf_path]: with open(filename, 'rb') as outfi: - merger.append(PyPDF2.PdfReader(outfi)) + merger.append(PyPDF2.PdfReader(outfi)) merger.write(instance_path) def append_report(self, sequencepair_object): @@ -575,7 +574,7 @@ def html_workflow(self): log.info('{}{}{}{}'.format(clr.green, 'shd__ ', clr.end, 'Generating HTML results output..')) ## Pass to subpackge to take data and format into HTML templates genHTML.genHTML(scalehdResults = self.instance_objects, - shdVersion = __version__, + shdVersion = version(__package__), jobLabel=self.instance_params.config_dict['JobName'], outputPath=self.instance_params.config_dict['HTMLPath']) From 55df11bd998e818c28aa842e1ace4e495812ddfe Mon Sep 17 00:00:00 2001 From: haessar Date: Tue, 26 Nov 2024 23:59:35 +0000 Subject: [PATCH 41/41] remove dummy test --- test/test_square.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 test/test_square.py diff --git a/test/test_square.py b/test/test_square.py deleted file mode 100644 index 71c5d78..0000000 --- a/test/test_square.py +++ /dev/null @@ -1,5 +0,0 @@ -import math - -def test_sqrt(): - num = 25 - assert math.sqrt(num) == 5