From 8528f9ce807ed2ee4db26a06113221ac465e7e5e Mon Sep 17 00:00:00 2001 From: tmsincomb Date: Tue, 2 Apr 2024 14:47:54 -0700 Subject: [PATCH 01/13] core package requirements --- .gitignore | 164 +++++++ .pre-commit-config.yaml | 20 + README.md | 0 README.pdf | Bin 0 -> 11284 bytes poetry.lock | 969 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 27 ++ 6 files changed, 1180 insertions(+) create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 README.md create mode 100644 README.pdf create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74f9128 --- /dev/null +++ b/.gitignore @@ -0,0 +1,164 @@ +.vscode/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +nb/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..d1dd0c1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-yaml + args: ["--unsafe"] + - id: end-of-file-fixer + - id: trailing-whitespace + exclude: docs/img/ + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + args: ["-l", "120"] + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort (python) + args: ["-m", "3", "--tc"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/README.pdf b/README.pdf new file mode 100644 index 0000000000000000000000000000000000000000..eff2baa79326409fbcb2e138d0d691504f2854bd GIT binary patch literal 11284 zcmaKybyV9;x3++B?LhJXQ|OAB*@ zxp~37U~X;*1i~xC4hFLz-^g*w&X)i85OHw;%*pa$3p{^s#lj2-0dPT;fm|xiu8w97 ze-GmOdyuTH0|L1-E?EcU9;9Iw&XzC$*Grg_HNpnS4Hgs^2f`7^lpFz`nIUoGc2Edj z=;1Zl{kbVbVG&LpXN1RJmE1*d`qIn={rvVz{dQNWU}DrW+YSjw`2x2`t(_A&^-mc{ z>jDp1X`!vNT9lBwwcn&2#?_bJj8sc~oG!_N&ulvM%?M&ztMz1lsTeudV-rt5YuqPy zs(`On9P)99lj6DUL2v!gK(eKUdTP7{DrN&5yZGkM#7vBaNt7^H2_?i2v;`?gqoS_Um_T3w!gjt`Tx1p zKXZHd`A>)bog4gNIydA_kTy}WwS)r=A4;HM^tVw2{xeL<3}NQrZ2ho|HL@cAv5Mf| z!~bC+2oS=}2Npyo{D0U?2qGZxUwzi@jq6D>oqSI>P@5&F@{1^(G(oQ~_}TDNCR<(} z5-*{eo3ZW$2iAB?falZb!gyZl5zQ0;?--Qfh1nw1kow9r58Wop9L>%10|;aUSbRK~ zGBGj18Qa+E2a0vV?eUQ%h)wx*zjt|mw-WGecxgp!dkH=&cKNN{qfn9(xe2UcS2^V6bwo%lyh-I zW$*L&1cTH>c7Sp*+q{-_4sU!(PWD`<+dS1;KhT{}LmSyoHocwhE^@?T$7-pnn>*vk zt%LRp)Ea3#=g2X#hwkn&p9}y6_8}OiXB`a)8Ynx!>J~#i!zcz^;?p}F7n@pyXFU(o ze^Iih{FRhZz;OS@v>y({^7+bnLQUaKd5f7x|F!bUA%ygg?!^S1DPX~R-Y{9qG4G9M z@Hg-2sVX+#^UHhJ<5=RW&umA347Fv-{4E%cS%MoygW?+Szg<`F#o-T%n+l;YR~o;p z*;bpXB{}(k9lYZw*HBqvV}lywj-WkJ!ue<;H%GxZMm)ncA857jV&=SOxWxf)$QNMvTuDqkPV^znzqn=AKc34fB zQcYm6jJu3m86xl^07Kdl8)Fy!kH(L1heB=d*}E?n=GqPt?kGM}wf)|T zhGC#K7go=Coa67PWT^MrM`ywav7RHME4HyuW12*NnCEg~pJ1Jv+V)j$C5{tT9KN1w zl_6n|I|#+hrG^;pBy))-#yDEgjBxJ!(Uzk+BC_9hTrqj$?tS*Io?h)H{w4HF!Zhy8 zfIq0AzdYgm&3PsNlza8Q(4xv>e@wui-?+GcLiQ*+J{_%8)J&n)tOV-;38!jsmGdbT z^Ei51rl_Cf$av&+?>z50us!4OCTI_dv7pJ#;%LOm=%b#Ku!uE{wFqd`;}A>nFM-!> zTBLBH(A9}Lc`dZew0v(F_*tLr-2UYXV=u2!b)lAj{(^OJ?c#o?>4)c*=S9=&o33?2 z4s;6f_M_wwi`5YnPm7CP!4oNa7Ij*0a2R!k-V<;?$#1wo3)YVD(NF&~6mMGJYi4iq zzNHMJFy;!uGs&m4X%vew`{)v3kSAOHfqLjsQuj}*pZ1x_{9^oN{;Ob4GB=}|OHw2T zg|7FYza#7x*bO=gp#jN}mt}OPr$^&c-7e6%kmF&|v#(ol8ynrqSG_*VI)d)HGI9@MYFSPH= zUW4{MJd%Vv7LGa6gD3;=7tX#j1^rB+;RukSFzj0uy5@X|606^r$HLil(C744ei^Vn zgMY@8%K&5<)5uj~2(^CHE=DnL_b1Tmhk*`z8Y>5JI%`~+-6Jast+m1C6#k3sKlTu; zQvys2j5-3p)$(X-f^3NC;Yzjt@~-{tS%vuily-6XuYLWRHXr@j3Fa)i{YL(e7*ai7 zS9jjEd(FJ8+b{oedChIgW=QBF(*CHNwRED&o65_o;y_PrmV(o0sa}mE) zqAm#LUVj9a3mc~L$3=aF&$tMqcFH$9$cH|8`k_xAzMtz{*laa6o1iZc<1%lB;1>EJ z;%5qM2(dBme*XnQihp2R`B{}rU}#>_jSa`mnE`z=05M(r%k-k`4?|Kmg!uA{<>X*d zdJ0utjB#7D_t@;KWzU+!fSn?&lD>oSmA2=F*n+?y^nry7G9`j%2L#N~W-FkmxNZ=7T*-^92X9=wn zS)n2ZO1#}3#|mE+H6T%@@2ie2-l_>1+KP9+RoAO2r^qDyp}gfnl_Z^13E&dTf|V{+I=S-Jm(7h}?u)XB zBS-{G!!*yc3t9t1#cRV*L?;d=$|6@*d6seQS1b_b%ZiQjF2`cr@|02|zQL5F8VAU;Vs`HN0&Kk%-Ofw8qOa9WF>grDQvu zyR%WM4{%vUBtitif+$W!zG}&jLT4A3MPf7Wx3OEl)0hF;+!`2v-35@DT|J2apAyQX ziFpN!t@7B*&RySgb#1q?bb&$R_b5egW7@@1B^K!Yj|0gUO}f2B4F3dtf9=G6`{&d! zhBn9RG;vvglNs2$k=49{W)G?LqrQmM(Mt`B)dP1{_|Y#4)Dtwg{jRVntr&4`Fqq-A zJv_3oQQl6tr=-mK*?oNUagpWw0{wxP-?j9p;o^AZzO&<@7rB!P*0jjjHEI*4L*ETe zKm*3T_h{RBv9nV!_2OfHCk98165DOHC$$Dl?4ZZP=8ilb(Qkzm;bj?IJo{rnf zQyW;6xA-P+mTM2Q4b^^{$QsQtyH#pg|MNwA)ML~lxmk1K<5o>nn0ITcKFBevctKbs z<-EeUea1YipZ1c&aHieLZROD%ho=j>JH7C@ijApf@MXKDaII^D$B%}U_K5b-brr0H zQK{mrh1Q?-)5Qd1B9N9@znN94eY!K*!GyjKWzJ&8glCQhG$N#lDf1eQ%+A5jzBw6w z%FBA+=>L7qV5P3I0S9d2ew9-Hsq;{|2hFgnh>kAG88e{&@aHd4oK8BB2+j)N=AcMP zbIr~eHT|w9Kkm#`W-hf%B6bA@NDS^;TyOmKaUP*!0oC zAFmTq`vQJfdr~lAdZfdY|!;AuP(=ZuM01R86 z(BW=b<=~5TQH|EPW7!cfIZ^jhzkdhJ-_0n#AhC)1sh~UC(?3!}YNck6f z3^S2QOk`hYSA(nJ+<6~92B*KRgO-S9Vn9bewG_TAQL6&5Ac%KZgUd+9SjTjUnD7J3 zHgPJXZOEaC!v~XOJ2J^*yihJ;B4|(;Ju_ZAbV^XrEB_gB9frBiAnBMGzLB26?(Ov2 z$06sX>zS^GQZ9eg?Ypf=AT0k> z@#kF5+3GrVtT*rkc<$Ad9kv;Y-+g5sdbbI_{b$6s-C^+j@9CV0aa3zZG+T zvD+0t7Goj~7EAK{)ixrbEJ`WTd+)sB?cl!76eAH}U3Ihf(^08bH^A>u&c+=#;B1~^ zLaZvF;4oW)TJ<}Tf8h0<8n&R2Sjdp@U)=BXt2vD+)-kTVuhW!U5OEf@ zTVyv%Z97X^mneN)rOy*ctb21*tLk?)Lw*?};lG!6p?hALnzM3Mar3!$NbbJ(dMWBu zuDc+>b7O+eG5(r+*ZQtjVqbI_#q;=N&LjPL!l zNcL6L&T*{!O7ne=KV{o){jGrC*4wZHvraLD@qVwvA)yJ?%H<+YE9WQ2jMhCV0h5!S zp(_6i*M6ZYt;4(3Co%rp<5it!#Y)-ta~tQa{@&#s0sF-Rkp3#CJc+|SiVr>}`j@jK z*8wN1sDBg*fV+dR89IZ@`q!h865@MzV-3oeAPS1n z-_v(*S#nzZhL&k`vO5WB?s~XVGL3GACSrYW%JLff#XI%|D86?VpH5i_fR+VMiB2&b zW>ZtMT#wj!oK?lQxHO*Qajhw&Nz2Y*W@*aOWyh^zUsGp?tD2w3Ri&J3 zvsx_eEk~)Q6nT@)mWVZgB~nuNnBEm?MSYJu7^m^!75b?*8KEad#ll)p2|+NG!TN<1 ztS@baEDHO6P-hPa*$P9p%xJFdh`$^v&RV>#>hhklDNKpBB~X36up2AVHm#O@-eVX2 z`S*dWASG{Ygnp*Z?|HHUAD74xto~YZs!OF7y|UUG1vbTT@23#;{Givu>+k_P)`{HS z_)x5z=?{K&+^R5_82hID@t1tG%tn}p&zpO(_0OA^P#Jb{2<6&ki_rU)==CZEL7&5c_q(nM+K&v#(_sXgpt;l{LUo(1)`n}6RHw{Uz zrZxkZR=4hkmr3A^k!viSgN8$#o;(#?|SwY(4S0gv_!3KVpjI{x9_4G$@k|Q7+eUG`Rm17N{|Lt*Syqw zxxFD6%c7Vq9Z{H!PUC{nvl|CxlX1ppY4>vyO_+SC6Gul;+e9qw32_Bgv1@ zXA%$6A9p?NE_l$GK#bNp_>D9(p<8>D08AP6=;u^$g<6e~)G~-uuGgQ{Nsn z>ATnx28*seH*H>bj@@V-D&*6W7qOwdHfE93tDlWgn?)t}5V+JV`* zP@Rw6*IGqi6bv|*)$)se23IONaDt#F0Kqew5t(4qo`EKET~H>EqQS7HE)A+EO;y}0 zh!h5S{5ds_(X#!pXQD!(RxbYKL`749sWk4V`0A6BIC9U862Gn%Qm~ZH zJJ@J+(fDMw5GPKGMi-L^IS-bUt`*ARZj`lrJ7iB}UDzGJwDB%d;m*uR|CY=vB+d@Q zSKNH>Eg2c(DbK8FyMsG*QpzqQjLLUn{jz$o1&{E|LW;>F+2pIpJeMQD5DbF3j3Z` zIMIQ!>%2p)bd~wO2Rzazqi_l7W`H#fO3M>EX)VhVa7_;6GekJVzvQ9L6b4}=s=G3>YAC+WDX&pc<@-sp)~p@oP7zqMNBbrI?I!(I}!9v@QQ!gfaVjOygn}{F4eX`l~Nc~>cc;(DZzxsQ#m@M&=t9^c2;tcFW$89Z_+mz8%E| zJ?35MmjKqf+Cf>d@gCmcC3=bjHeo%LD&5jF_Dno(;_nJFh=y$DC;%y7Qn8lds9Yy1 zjy&E>kB^eMAp(%bs}NM85Y~bR5a0R@gF8h4;W$j8~#6@eT&Z6TA8h zfj)G3YYJ{H^jfdyg@$D8{zR-P=vY5P1UZJQ_i0zUDzsC-{_3qoF$a7n#aUwgr>qZ9 zYA27&qt5x`oYF1?loBbd8eyz%JunzNOX&gLP3xA49a5pq;aq3jJp1Z{|NboDZK%O8 zYhn`PwN@5xr0+Wq*gWg2Cpm1#_!adOTtZ%6fGvgh*#t2^ma5r;&s&HADoJGe8k34- zTlJLhfBaMl%HcO{?}d{uP$j@*sa3t)7>r)Vw{^karf;}L$+mr{>C?*W#V45|PCswP z=PT6-BpX(TCVZomT&9EzEkko@%lZd+>GOgVN@>}s9hz!XndT!HyWb^0YWa{5k^ZCd zD`P+Se7Gr*Ah?@ZY+HvV5Mya1Su3<+{pow{q_oM>9;5FzJr!2@+I(QzKCcgX80_%| zFG|bC^?KGRXtO!A%UpzvV&haKFN$1Pcg$Tv#^vM{2%e#BRv#R%9+%}}+S7}baJS4? z?FNgAZG8;EtI=~){_I<-r6)EZdxVqq6iy%Do4-@(spLJpIW9CPGdu4pty=00p)SU# zNa!-#elarBY(w7ol7x!V!{=SxmpF*3&`4eK?yK}4X#;`peB=Wh47Aoe6sMUJ#^sxK zwr!1+l>YFRlFz-O8-<)F&{vMVt$8V#uQAQmo$ezV|E@?oWCB1faZrl6%*-u}>XWR$ zFcNLA$XvWWV8Adag#IZmEg6pa-c6Z1@2emwehY3Pz4b?n#Lf1N@RX5ez^lrG^Px;^ z+$Q7A=WJ?(NjE~tT9k3IuQ58mjgvO3*BX$K*J`j&Kwsg|bn})z1t8je^ z76g6310Cd1(~{mSX#uo zu&RK3xAwa}G`Ez55F&xT53k{GjgP>1*eh)Ee>1-?%w6Ikxc(szPtoIkLeNtpk?WXNpw@r3& z(f)Pw$m4Z~bXQ%tQ`$!(OGP{yvmN44c-PT6**vDM>vmr|YN`@gT7%(uIbp(OVi!Qb zldQ(?ONvm;d|o*3ryVq)To(_ngsV6Urf{M&o#!7=n@K4W>Gn-%P~T9*s`A?Mf^E zOtX8zNXyVm-ggP@UGpzZg`-b_tU`_#LNR@V(!-x{04?q%@q}(r`JmQ7k zxuat*JHWpElbQ9)#Cyf~h`xU0#rU$W&uMTKtxUq!bJA5Y>bF%pF@3QYRknMt%a(lU zk7-S`l{b8`8*b^AQ`p&H#(WdP*XxPz>kHR?tI_syhiC08ZN@7o+g41&mMxzdP2v}Q zuuhbeQ#=-S3w=D-V+_igQDCm<7A7Tmot6XTuG};g#W;)XkJXe#m^7`b2r{Q^+jz-cePDxS(P zn~=^_-?Kr%L=I)hCimgcTFVO`L>Z&ZD?aTX3Dxd0Kg$IbTh|@pV_?1E@qJRw%Pobi zWqSIMkyGVpLW5O4JZeDPB`3JrwOS zMO4(p8A=UfCIQ&^X2E6H&6|a3lk?9@HR8&;WZCqzTm#c0bbk2OPOXxzqY3e|UCk#@ zm(dt=ib<-(eo1`w(ib`)^t)lo{wZ z<6{QpQ3^%9*B<%BK6!mpylx*uLD%`Z%?rx;Hd6|)3O2=vh+8*+eFr%Yt|X_ZqpLmU zW!qscB%9T8YC4vCjcUx)_)>vU_jRoyoj%K*W8-E z#$0ekr9=1D$vroRWAFXekJH@FPf~i=L?t7aO$HnqGBjc*=}FiUbgE7+mMz?`7nw6J zng${jU`C{ckylO;q!Vb)Pm2d8X;~^P-U@V{d*4H1Gk=R-p@`R;4_vO# zMG7|VVP({C_0;4oFVE3{-_cgIw!0nLE)!q$sih9IR6c60q`O`}2=Bddyd`|R!-E($ z_rD+Od8|e{)}5kKa?km~1GP4nOK*5c`bi_OX}F%$2=}+a=W7tZyolL5rO)wim{8@= zRA^dIT-8g~0#kaiH--(EDl#9rwgR_pHswpAgK6gn&kcpQtH0%4S5wsBIQ+~ezgp{w-dLIPGNIGY?3F3M#SaI#0&X-JEuS0$C&JWG zdOkHAcYy4>As*{+E?G&+tTpRIVI zghJn3c}+Ttdf3pKWqE(#^()z@#6Q#YYP*Jg`v>~dC-0+H<~y&oev#BS;|D$7m&*WI zc{7|eObBn;6xF2_FjtMlXXK>`e>y#l`wAB^dE=5aHj`2 z?XODvWO{kgDXDVa+2PiKFIeT6br`&nnz0S?_;xNK2j#ABxi8-Mb1N}VwG)8O;mdvp zz3;Ycz8BBeUUgr2^uY~*4L21aa64a7^sl{td@^{K;nCP*QfP4D6#gk;PvFt%_dE74 z?^@iD|9H<q>EV!bv zPk}^DTXC$uIFus{1Z06DejpJWo@!q?^`)1eV?rb|ltsXn?^70%qwJpQq4MRtPqX6! z))vVc7~7PJnU&FXn4g9p9&L?)$QN}GGD@3(MIB8Hw2qzTN0CnC(|&jbg8ZpW(<4&_MHU*u7ITpJ z^%|ToTiSPwEJtKVsdH1JtSUtIH<*3hDMYvw51I z`@C}r>QPH@t#a>@jjGM(PziJ2;v3lzWubav{R$*nh;R=?P^hK;iDr-w8+ZD={UsZa zcRKzD*p5{(y;MvwVyOQl=7X? z9XRPLsI_@d3gq6NDxKlE7|V8|-Pj{1@Hx#uoM8cJu9(y)yfJQZ%&&#L$&Yg0I?V0v zbmmFTTI{J&$ePliIQKT9xSKvTrPxCM^>!1-Y}?fMGgCDtlLHJu{wsL^AJ3tl|hLFl=={?cQQ zfYgx2u{Tt>kJ1{d--taXG_C5z{Y^V0^yZU(BkFgjc>>Qc7}jc5O4ge&j@Hi$B^cf- zSFj;UyqU#vBKRJ|9>Zr3J!h;N^1ZA#op!fut3rFTC@c2uG)IILCRjH~=L(-C>h7?Q zoXbb^I2TjPJ42%HMjA8XM{X+VzoeW8pd5RK9{*3^O6YG~=pRth0~RF>gIl=Tx*!?4 ze_&E7W{yY{OUu>~1_x<4JDNEmWd}2BBzpwll7w3zu_vGaACh}|hm$n60%90?5w~;pFFrfRUmC5@xcskaV(kfB}(F56eUU;;%s5 zU>;5+x&#FAatm^Db93|a10e!nFegNaj|a_V@&L0+B*AnPFrNjJf`fe-Z41Kssc zS5?V0{%`C_)r8S#|1Tm!ycahN2;1`=7_&oG${q?hsO_f;znkpAg$#r zry#9t=7Pk{ERlKJBE0^RHND`-+!dUxoFDiy4Vbkp5_0nbK9@w&fiNUWsOoA7bG3D{ z20s5+7*f=9b8&HiIX;vR`~afqJ`iYz0^Ho35Pkun2M7%OP*)=$L=ee{2?%fs@*oR@ z6#2klBV-Mvb^nU{SK|K#hy1^|FK!WWi^qOr z^-osk6zyyr3IwBW{9x8;Pz-+BLZ9Y@wzJW851b7V3?9?K0WYPd&}h7_@Ve7{r_aKP zPFntTJI|j+h50Ero%sXu_fR&lJZ7auGP4~hk0rkf)XIMwb-8N68}5F*PRO}Kss&}O z7Oa_!imCFJjFWLc09_g_jF$h&_`Spg)oc=dPP_x)u^4P?8t zoVA^7A4pFib7e-!C9PA0H1N&J=o@mH;o*9gBL LfSFlFRrdb@>jkC0 literal 0 HcmV?d00001 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..1d5fa74 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,969 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "fastapi" +version = "0.110.1" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.110.1-py3-none-any.whl", hash = "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc"}, + {file = "fastapi-0.110.1.tar.gz", hash = "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "filelock" +version = "3.13.3" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, + {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "identify" +version = "2.5.35" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "3.7.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, + {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pydantic" +version = "2.6.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.16.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.16.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.2.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.29" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, + {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, + {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[[package]] +name = "uvicorn" +version = "0.29.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, + {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "virtualenv" +version = "20.25.1" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "984ae526cd54554a0eea20909da100b028b3c43e0d07c9058d9c2ba7c50ec11f" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a6cba5f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[tool.poetry] +name = "quantdb" +version = "0.1.0" +description = "Quantitative DB" +authors = ["tmsincomb "] +license = "MIT" +readme = "README.md" +packages = [{ include = "quantdb" }] + +[tool.poetry.dependencies] +python = "^3.12" +sqlalchemy = "^2.0.29" +pydantic = "^2.6.4" +fastapi = "^0.110.1" +uvicorn = {extras = ["standard"], version = "^0.29.0"} +pydantic-settings = "^2.2.1" + +[tool.poetry.scripts] +quantdb-fastapi = "uvicorn quantdb.main:app --reload" + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pre-commit = "^3.7.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 29bb91034f6181334a4bc20d96701a449137ee06 Mon Sep 17 00:00:00 2001 From: tmsincomb Date: Tue, 2 Apr 2024 14:48:26 -0700 Subject: [PATCH 02/13] dummy fastpi service --- quantdb/__init__.py | 1 + quantdb/config.py | 10 +++++++ quantdb/main.py | 52 +++++++++++++++++++++++++++++++++++ quantdb/mysql_app/__init__.py | 5 ++++ quantdb/mysql_app/database.py | 17 ++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 quantdb/config.py create mode 100644 quantdb/main.py create mode 100644 quantdb/mysql_app/__init__.py create mode 100644 quantdb/mysql_app/database.py diff --git a/quantdb/__init__.py b/quantdb/__init__.py index e69de29..9d48db4 100644 --- a/quantdb/__init__.py +++ b/quantdb/__init__.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/quantdb/config.py b/quantdb/config.py new file mode 100644 index 0000000..ad690e0 --- /dev/null +++ b/quantdb/config.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Optional + +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + pass diff --git a/quantdb/main.py b/quantdb/main.py new file mode 100644 index 0000000..a6b1d18 --- /dev/null +++ b/quantdb/main.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from functools import lru_cache +from typing import Any, Generator + +from fastapi import Depends, FastAPI +from sqlalchemy import text +from sqlalchemy.orm import Session # type: ignore + +from quantdb.config import Settings + +from quantdb import mysql_app + +app = FastAPI() + + +@lru_cache() +def get_settings(): + """ + Get settings from mongodb - alternative for test and production credientials + + Returns + ------- + Settings + pydantic BaseSettings model with MongoDB credentials + """ + return Settings() + + +def get_mysql_db() -> Generator[Session, Any, None]: + db = mysql_app.database.SessionLocal() + try: + yield db + finally: + db.close() + + +@app.get( + "/example", + status_code=200, +) +def get_example( + mysql_db: Session = Depends(get_mysql_db), # type: ignore +): + query = text( + """ + select * from limit 10 + """ + ) + result = mysql_db.execute(query) + data = result.mappings().all() + return data diff --git a/quantdb/mysql_app/__init__.py b/quantdb/mysql_app/__init__.py new file mode 100644 index 0000000..4064ab4 --- /dev/null +++ b/quantdb/mysql_app/__init__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from . import database + +__all__ = ["database"] diff --git a/quantdb/mysql_app/database.py b/quantdb/mysql_app/database.py new file mode 100644 index 0000000..411d706 --- /dev/null +++ b/quantdb/mysql_app/database.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from typing import Any + +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +from quantdb.config import Settings + +# TODO: get new setting env setup first +# SQLALCHEMY_DATABASE_URL = Settings().MYSQL_URL + +# engine = create_engine(SQLALCHEMY_DATABASE_URL) +# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Base: Any = declarative_base() From ccb189fba98f00affab0f5210099578d859ba456 Mon Sep 17 00:00:00 2001 From: tmsincomb Date: Tue, 2 Apr 2024 14:48:56 -0700 Subject: [PATCH 03/13] mysql reformat to black --- sql/postgres.sql | 1 - sql/tables.sql | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/postgres.sql b/sql/postgres.sql index 1a0727c..4af6647 100644 --- a/sql/postgres.sql +++ b/sql/postgres.sql @@ -35,4 +35,3 @@ CREATE DATABASE :database -- quantdb LC_COLLATE = 'en_US.UTF-8' -- this was a gentoo locale issue check ${LANG} LC_CTYPE = 'en_US.UTF-8' CONNECTION LIMIT = -1; - diff --git a/sql/tables.sql b/sql/tables.sql index c68293d..86691e5 100644 --- a/sql/tables.sql +++ b/sql/tables.sql @@ -130,7 +130,7 @@ CREATE TYPE prov_type AS ENUM ( ); create table provenance( --- +-- id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, type prov_type, -- this control the interpretation of the field source field_name text, From ed5404642e110f71e0bcad696fbd3f4b9785bc1a Mon Sep 17 00:00:00 2001 From: tmsincomb Date: Wed, 3 Apr 2024 12:52:57 -0700 Subject: [PATCH 04/13] native pyproject --- poetry.lock | 969 ------------------------------------------------- pyproject.toml | 37 +- 2 files changed, 17 insertions(+), 989 deletions(-) delete mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 1d5fa74..0000000 --- a/poetry.lock +++ /dev/null @@ -1,969 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.6.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, -] - -[[package]] -name = "anyio" -version = "4.3.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.8" -files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "distlib" -version = "0.3.8" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - -[[package]] -name = "fastapi" -version = "0.110.1" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fastapi-0.110.1-py3-none-any.whl", hash = "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc"}, - {file = "fastapi-0.110.1.tar.gz", hash = "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.38.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "filelock" -version = "3.13.3" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, - {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "greenlet" -version = "3.0.3" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "httptools" -version = "0.6.1" -description = "A collection of framework independent HTTP protocol utils." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, - {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, - {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, - {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, - {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, - {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, - {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] - -[[package]] -name = "identify" -version = "2.5.35" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.6" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "packaging" -version = "24.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, -] - -[[package]] -name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - -[[package]] -name = "pluggy" -version = "1.4.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "3.7.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, - {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "pydantic" -version = "2.6.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, - {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.16.3" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.16.3" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, - {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, - {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, - {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, - {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, - {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, - {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, - {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, - {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, - {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, - {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, - {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, - {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, - {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, - {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, - {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, - {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, - {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, - {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, - {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, - {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, - {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, - {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, - {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, - {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, - {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, - {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, - {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, - {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, - {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pydantic-settings" -version = "2.2.1" -description = "Settings management using Pydantic" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, - {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, -] - -[package.dependencies] -pydantic = ">=2.3.0" -python-dotenv = ">=0.21.0" - -[package.extras] -toml = ["tomli (>=2.0.1)"] -yaml = ["pyyaml (>=6.0.1)"] - -[[package]] -name = "pytest" -version = "8.1.1" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.4,<2.0" - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "python-dotenv" -version = "1.0.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, - {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "setuptools" -version = "69.2.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "sqlalchemy" -version = "2.0.29" -description = "Database Abstraction Library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, - {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, - {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, - {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, - {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, - {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, - {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, - {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, - {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - -[[package]] -name = "starlette" -version = "0.37.2" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.8" -files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] - -[[package]] -name = "typing-extensions" -version = "4.10.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, -] - -[[package]] -name = "uvicorn" -version = "0.29.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvloop" -version = "0.19.0" -description = "Fast implementation of asyncio event loop on top of libuv" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, - {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, -] - -[package.extras] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] - -[[package]] -name = "virtualenv" -version = "20.25.1" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "watchfiles" -version = "0.21.0" -description = "Simple, modern and high performance file watching and code reload in python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.12" -content-hash = "984ae526cd54554a0eea20909da100b028b3c43e0d07c9058d9c2ba7c50ec11f" diff --git a/pyproject.toml b/pyproject.toml index a6cba5f..3f32edf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,27 +1,24 @@ -[tool.poetry] +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] name = "quantdb" version = "0.1.0" description = "Quantitative DB" -authors = ["tmsincomb "] -license = "MIT" readme = "README.md" packages = [{ include = "quantdb" }] +dependencies = [ + "sqlalchemy ~= 2.0.29", + "pydantic ~= 2.6.4", + "fastapi ~= 0.110.1", + "uvicorn ~= 0.29.0", + "pydantic-settings ~= 2.2.1", +] +requires-python = ">=3.12" -[tool.poetry.dependencies] -python = "^3.12" -sqlalchemy = "^2.0.29" -pydantic = "^2.6.4" -fastapi = "^0.110.1" -uvicorn = {extras = ["standard"], version = "^0.29.0"} -pydantic-settings = "^2.2.1" - -[tool.poetry.scripts] -quantdb-fastapi = "uvicorn quantdb.main:app --reload" +# [project.scripts] +# quantdb-fastapi = "uvicorn quantdb.main:app --reload" -[tool.poetry.group.dev.dependencies] -pytest = "^8.1.1" -pre-commit = "^3.7.0" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +[project.optional-dependencies] +dev = ["pytest ~= 8.1.1", "pre-commit ~= 3.7.0"] From 11c9264ba7d8609f81c70c8d25ed3c5d5526df3a Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 7 May 2024 21:11:12 +0000 Subject: [PATCH 05/13] move fastapi to router.py --- pyproject.toml | 2 +- quantdb/config.py | 13 ---------- quantdb/main.py | 60 +++-------------------------------------------- quantdb/router.py | 52 ++++++++++++++++++++++++++++++++++++++++ sql/postgres.sql | 16 ------------- 5 files changed, 56 insertions(+), 87 deletions(-) create mode 100644 quantdb/router.py diff --git a/pyproject.toml b/pyproject.toml index 3f32edf..601e297 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "uvicorn ~= 0.29.0", "pydantic-settings ~= 2.2.1", ] -requires-python = ">=3.12" +requires-python = ">=3.10" # [project.scripts] # quantdb-fastapi = "uvicorn quantdb.main:app --reload" diff --git a/quantdb/config.py b/quantdb/config.py index 135c0bb..aa2a831 100644 --- a/quantdb/config.py +++ b/quantdb/config.py @@ -1,15 +1,2 @@ -<<<<<<< HEAD -from __future__ import annotations - -from pathlib import Path -from typing import Optional - -from pydantic_settings import BaseSettings, SettingsConfigDict - - -class Settings(BaseSettings): - pass -======= import orthauth as oa auth = oa.configure_here('auth-config.py', __name__) ->>>>>>> master diff --git a/quantdb/main.py b/quantdb/main.py index 32bf274..ad52ff0 100644 --- a/quantdb/main.py +++ b/quantdb/main.py @@ -1,66 +1,12 @@ -<<<<<<< HEAD -from __future__ import annotations - -from functools import lru_cache -from typing import Any, Generator - -from fastapi import Depends, FastAPI -from sqlalchemy import text -from sqlalchemy.orm import Session # type: ignore - -from quantdb.config import Settings - -from quantdb import mysql_app - -app = FastAPI() - - -@lru_cache() -def get_settings(): - """ - Get settings from mongodb - alternative for test and production credientials - - Returns - ------- - Settings - pydantic BaseSettings model with MongoDB credentials - """ - return Settings() - - -def get_mysql_db() -> Generator[Session, Any, None]: - db = mysql_app.database.SessionLocal() - try: - yield db - finally: - db.close() - - -@app.get( - "/example", - status_code=200, -) -def get_example( - mysql_db: Session = Depends(get_mysql_db), # type: ignore -): - query = text( - """ - select * from limit 10 - """ - ) - result = mysql_db.execute(query) - data = result.mappings().all() - return data -======= from fastapi import FastAPI from fastapi.middleware.wsgi import WSGIMiddleware from quantdb.api_server import app as flask_app app = FastAPI() -app.mount('/', WSGIMiddleware(flask_app)) +app.mount("/", WSGIMiddleware(flask_app)) if __name__ == "__main__": import uvicorn - uvicorn.run("quantdb.main:app", host='127.0.0.1', port=8990, reload=True) ->>>>>>> master + + uvicorn.run("quantdb.main:app", host="127.0.0.1", port=8990, reload=True) diff --git a/quantdb/router.py b/quantdb/router.py new file mode 100644 index 0000000..a6b1d18 --- /dev/null +++ b/quantdb/router.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from functools import lru_cache +from typing import Any, Generator + +from fastapi import Depends, FastAPI +from sqlalchemy import text +from sqlalchemy.orm import Session # type: ignore + +from quantdb.config import Settings + +from quantdb import mysql_app + +app = FastAPI() + + +@lru_cache() +def get_settings(): + """ + Get settings from mongodb - alternative for test and production credientials + + Returns + ------- + Settings + pydantic BaseSettings model with MongoDB credentials + """ + return Settings() + + +def get_mysql_db() -> Generator[Session, Any, None]: + db = mysql_app.database.SessionLocal() + try: + yield db + finally: + db.close() + + +@app.get( + "/example", + status_code=200, +) +def get_example( + mysql_db: Session = Depends(get_mysql_db), # type: ignore +): + query = text( + """ + select * from limit 10 + """ + ) + result = mysql_db.execute(query) + data = result.mappings().all() + return data diff --git a/sql/postgres.sql b/sql/postgres.sql index f4c9d35..dbbe6ce 100644 --- a/sql/postgres.sql +++ b/sql/postgres.sql @@ -36,19 +36,3 @@ ALTER ROLE "quantdb-test-user" SET search_path = quantdb, public; ALTER ROLE "quantdb-admin" SET search_path = quantdb, public; ALTER ROLE "quantdb-user" SET search_path = quantdb, public; -<<<<<<< HEAD --- postgres postgres - -DROP DATABASE IF EXISTS :database; - --- postgres postgres - -CREATE DATABASE :database -- quantdb - WITH OWNER = 'quantdb-admin' - ENCODING = 'UTF8' - TABLESPACE = pg_default - LC_COLLATE = 'en_US.UTF-8' -- this was a gentoo locale issue check ${LANG} - LC_CTYPE = 'en_US.UTF-8' - CONNECTION LIMIT = -1; -======= ->>>>>>> master From 76b8072dd12318fe6ed3b361614940a3ee92339a Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Thu, 9 May 2024 17:53:42 +0000 Subject: [PATCH 06/13] flask dep --- pyproject.toml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 601e297..2c0e355 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,18 +3,21 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "quantdb" -version = "0.1.0" -description = "Quantitative DB" -readme = "README.md" -packages = [{ include = "quantdb" }] dependencies = [ "sqlalchemy ~= 2.0.29", "pydantic ~= 2.6.4", "fastapi ~= 0.110.1", "uvicorn ~= 0.29.0", "pydantic-settings ~= 2.2.1", + "flask ~= 3.0.3", + "Flask-SQLAlchemy ~= 3.1.1", + ] +name = "quantdb" +version = "0.1.0" +description = "Quantitative DB" +readme = "README.md" +packages = [{ include = "quantdb" }] requires-python = ">=3.10" # [project.scripts] From d09bbff4f9a80a1745e0d57f99dbf4cb9f18a5b9 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 21 May 2024 19:43:36 +0000 Subject: [PATCH 07/13] changes for server --- pyproject.toml | 4 +- quantdb/api.py | 899 +++++++++++++++++++++++++--------------------- quantdb/router.py | 171 +++++++++ 3 files changed, 663 insertions(+), 411 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2c0e355..24def26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,9 @@ dependencies = [ "pydantic-settings ~= 2.2.1", "flask ~= 3.0.3", "Flask-SQLAlchemy ~= 3.1.1", - + "orthauth ~= 0.0.18", + "cython", + "psycopg-binary", ] name = "quantdb" version = "0.1.0" diff --git a/quantdb/api.py b/quantdb/api.py index 952688f..b911f84 100644 --- a/quantdb/api.py +++ b/quantdb/api.py @@ -10,7 +10,7 @@ from quantdb import exceptions as exc -log = log.getChild('api') +log = log.getChild("api") class JEncode(json.JSONEncoder): @@ -27,31 +27,40 @@ def default(self, obj): url_sql_where = ( # TODO arity spec here - # dupes overwrite params but that is ok, this way we get the correct table alias for both cases - ('object', 'object', 'cv.object = any(:object)', 'cat'), # XXX should not use this outside values/ unless we left outer due to intersect ? - ('object', 'object', 'qv.object = any(:object)', 'quant'), # XXX should not use this outside values/ unless we left outer due to intersect ? - - ('desc-inst', 'desc_inst', 'idin.label = any(:desc_inst)', 'both'), - ('dataset', 'dataset', 'im.dataset = :dataset', 'both'), - ('inst', 'inst', 'im.id_formal = any(:inst)', 'both'), - ('subject', 'subject', 'im.id_sub = any(:subject)', 'both'), - ('sample', 'sample', 'im.id_sam = any(:sample)', 'both'), - - ('desc-cat', 'desc_cat', 'cd.label = any(:desc_cat)', 'cat'), - - ('value-cat', 'value_cat', 'ct.label = any(:value_cat)', 'cat'), - ('value-cat-open', 'value_cat_open', 'cv.value_open = any(:value_cat_open)', 'cat'), - - ('unit', 'unit', 'u.label = any(:unit)', 'quant'), - ('aspect', 'aspect', 'ain.label = any(:aspect)', 'quant'), - ('agg-type', 'agg_type', 'qd.aggregation_type = :agg_type', 'quant'), + ( + "object", + "object", + "cv.object = any(:object)", + "cat", + ), # XXX should not use this outside values/ unless we left outer due to intersect ? + ( + "object", + "object", + "qv.object = any(:object)", + "quant", + ), # XXX should not use this outside values/ unless we left outer due to intersect ? + ("desc-inst", "desc_inst", "idin.label = any(:desc_inst)", "both"), + ("dataset", "dataset", "im.dataset = :dataset", "both"), + ("inst", "inst", "im.id_formal = any(:inst)", "both"), + ("subject", "subject", "im.id_sub = any(:subject)", "both"), + ("sample", "sample", "im.id_sam = any(:sample)", "both"), + ("desc-cat", "desc_cat", "cd.label = any(:desc_cat)", "cat"), + ("value-cat", "value_cat", "ct.label = any(:value_cat)", "cat"), + ("value-cat-open", "value_cat_open", "cv.value_open = any(:value_cat_open)", "cat"), + ("unit", "unit", "u.label = any(:unit)", "quant"), + ("aspect", "aspect", "ain.label = any(:aspect)", "quant"), + ("agg-type", "agg_type", "qd.aggregation_type = :agg_type", "quant"), # TODO shape - - ('value-quant', 'value_quant', 'qv.value = :value_quant', 'quant'), - ('value-quant-margin', 'value_quant_margin', 'qv.value <= :value_quant + :value_quant_margin AND qv.value >= :value_quant - :value_quant_margin', 'quant'), - ('value-quant-min', 'value_quant_min', 'qv.value >= :value_quant_min', 'quant'), - ('value-quant-max', 'value_quant_max', 'qv.value <= :value_quant_min', 'quant'), + ("value-quant", "value_quant", "qv.value = :value_quant", "quant"), + ( + "value-quant-margin", + "value_quant_margin", + "qv.value <= :value_quant + :value_quant_margin AND qv.value >= :value_quant - :value_quant_margin", + "quant", + ), + ("value-quant-min", "value_quant_min", "qv.value >= :value_quant_min", "quant"), + ("value-quant-max", "value_quant_max", "qv.value <= :value_quant_min", "quant"), ) @@ -62,140 +71,126 @@ def get_where(kwargs): for u, s, w, t in url_sql_where: if u in kwargs and kwargs[u]: params[s] = kwargs[u] - if t == 'cat': + if t == "cat": _where_cat.append(w) - elif t == 'quant': + elif t == "quant": _where_quant.append(w) - elif t == 'both': + elif t == "both": _where_cat.append(w) _where_quant.append(w) else: - raise ValueError('wat') + raise ValueError("wat") - where_cat = ' AND '.join(_where_cat) - where_quant = ' AND '.join(_where_quant) - log.log(9, f'\nwhere-quant\n{where_quant}\nwhere-quant') + where_cat = " AND ".join(_where_cat) + where_quant = " AND ".join(_where_quant) + log.log(9, f"\nwhere-quant\n{where_quant}\nwhere-quant") return where_cat, where_quant, params def main_query(endpoint, kwargs): ep_select = { #'instances': 'im.dataset, im.id_formal, im.id_sam, im.id_sub, id.label', - 'values/inst': ( - 'im.dataset, ' - 'im.id_formal AS inst, ' - 'im.id_sam AS sample, ' - 'im.id_sub AS subject, ' - 'id.label AS desc_inst' + "values/inst": ( + "im.dataset, " + "im.id_formal AS inst, " + "im.id_sam AS sample, " + "im.id_sub AS subject, " + "id.label AS desc_inst" ), - 'objects': ( # TODO probably some path metadata file type, etc. too - 'im.dataset, ' - 'o.id, ' - 'o.id_type, ' - 'oi.updated_transitive' + "objects": ( # TODO probably some path metadata file type, etc. too + "im.dataset, " "o.id, " "o.id_type, " "oi.updated_transitive" ), - 'values/cat': ( - 'im.dataset, ' - 'im.id_formal AS inst, ' - 'id.label AS desc_inst, ' - 'cdid.label AS domain, ' - 'cd.range, ' - 'cd.label AS desc_cat, ' - 'cv.value_open, ' - 'ct.label AS value_controlled' # TODO and where did it come from TODO iri + "values/cat": ( + "im.dataset, " + "im.id_formal AS inst, " + "id.label AS desc_inst, " + "cdid.label AS domain, " + "cd.range, " + "cd.label AS desc_cat, " + "cv.value_open, " + "ct.label AS value_controlled" # TODO and where did it come from TODO iri ), # TODO will want/need to return the shape of the value for these as well since that will be needed to correctly interpret the contents of the value field in the future - 'values/quant': ( - 'im.dataset, ' - 'im.id_formal AS inst, ' - 'id.label AS desc_inst, ' - 'qd.aggregation_type AS agg_type, ' - 'a.label AS aspect, ' - 'u.label AS unit, qv.value' # TODO and where did it come from + "values/quant": ( + "im.dataset, " + "im.id_formal AS inst, " + "id.label AS desc_inst, " + "qd.aggregation_type AS agg_type, " + "a.label AS aspect, " + "u.label AS unit, qv.value" # TODO and where did it come from ), - 'values/cat-quant': ( + "values/cat-quant": ( ( "'value-cat' AS type, " - 'im.dataset, ' - 'im.id_formal AS inst, ' - 'id.label AS desc_inst, ' - 'cdid.label AS domain, ' - 'cd.range, ' - 'NULL::quant_agg_type as agg_type, ' # have to annoate the nulls because distinct causes type inference to fail ??? - 'cd.label AS pred_or_asp, ' - 'cv.value_open AS vo_or_unit, ' - 'ct.label AS value_controlled, ' - 'NULL::numeric AS value') - , ( + "im.dataset, " + "im.id_formal AS inst, " + "id.label AS desc_inst, " + "cdid.label AS domain, " + "cd.range, " + "NULL::quant_agg_type as agg_type, " # have to annoate the nulls because distinct causes type inference to fail ??? + "cd.label AS pred_or_asp, " + "cv.value_open AS vo_or_unit, " + "ct.label AS value_controlled, " + "NULL::numeric AS value" + ), + ( "'value-quant' AS type, im.dataset, " - 'im.id_formal AS inst, id.label AS desc_inst, ' - 'NULL AS domain, ' - 'NULL::cat_range_type AS range, ' - 'qd.aggregation_type AS agg_type, ' - 'a.label AS aspect, ' - 'u.label AS unit, ' - 'NULL AS vc, qv.value' - )), - 'desc/inst': ( - 'id.iri, ' - 'id.label, ' - 'idpar.label as subclassof ' + "im.id_formal AS inst, id.label AS desc_inst, " + "NULL AS domain, " + "NULL::cat_range_type AS range, " + "qd.aggregation_type AS agg_type, " + "a.label AS aspect, " + "u.label AS unit, " + "NULL AS vc, qv.value" + ), ), - 'desc/cat': ( - 'cd.label, ' - 'cdid.label AS domain, ' - 'cd.range, ' - 'cd.description ' - ), - 'desc/quant': ( - 'qd.label, ' - 'id.label AS domain, ' - 'qd.shape, ' - 'qd.aggregation_type as agg_type, ' - 'a.label AS aspect, ' - 'u.label AS unit, ' - 'qd.description ' - ), - 'terms': ( - 'ct.iri, ' - 'ct.label ' - ), - 'units': ( - 'u.iri, ' - 'u.label ' - ), - 'aspects': ( - 'a.iri, ' - 'a.label, ' - 'aspar.label as subclassof ' + "desc/inst": ("id.iri, " "id.label, " "idpar.label as subclassof "), + "desc/cat": ("cd.label, " "cdid.label AS domain, " "cd.range, " "cd.description "), + "desc/quant": ( + "qd.label, " + "id.label AS domain, " + "qd.shape, " + "qd.aggregation_type as agg_type, " + "a.label AS aspect, " + "u.label AS unit, " + "qd.description " ), + "terms": ("ct.iri, " "ct.label "), + "units": ("u.iri, " "u.label "), + "aspects": ("a.iri, " "a.label, " "aspar.label as subclassof "), }[endpoint] # FIXME move extra and select out and pass then in in as arguments ? or retain control here? - def gkw(k): return k in kwargs and kwargs[k] + def gkw(k): + return k in kwargs and kwargs[k] class sn: # select needs - objects = endpoint == 'objects' - desc_inst = endpoint not in ('objects', 'terms', 'units', 'aspects',) - desc_cat = endpoint in ('values/cat', 'values/cat-quant', 'desc/cat') - value_cat = endpoint in ('values/cat', 'values/cat-quant', 'terms') - aspect = endpoint in ('values/quant', 'values/cat-quant', 'desc/quant', 'aspects') - unit = endpoint in ('values/quant', 'values/cat-quant', 'desc/quant', 'units') - agg_type = endpoint in ('values/quant', 'values/cat-quant') - desc_quant = (aspect or unit or agg_type) - parent_aspect = endpoint == 'aspects' - parent_desc_inst = endpoint == 'desc/inst' + objects = endpoint == "objects" + desc_inst = endpoint not in ( + "objects", + "terms", + "units", + "aspects", + ) + desc_cat = endpoint in ("values/cat", "values/cat-quant", "desc/cat") + value_cat = endpoint in ("values/cat", "values/cat-quant", "terms") + aspect = endpoint in ("values/quant", "values/cat-quant", "desc/quant", "aspects") + unit = endpoint in ("values/quant", "values/cat-quant", "desc/quant", "units") + agg_type = endpoint in ("values/quant", "values/cat-quant") + desc_quant = aspect or unit or agg_type + parent_aspect = endpoint == "aspects" + parent_desc_inst = endpoint == "desc/inst" class kw: # keywords - prov = gkw('prov') - source_only = gkw('source-only') - desc_inst = gkw('desc-inst') - desc_cat = gkw('desc-cat') - value_cat = gkw('value-cat') - aspect = gkw('aspect') - unit = gkw('unit') - agg_type = gkw('agg-type') - desc_quant = (aspect or unit or agg_type) + prov = gkw("prov") + source_only = gkw("source-only") + desc_inst = gkw("desc-inst") + desc_cat = gkw("desc-cat") + value_cat = gkw("value-cat") + aspect = gkw("aspect") + unit = gkw("unit") + agg_type = gkw("agg-type") + desc_quant = aspect or unit or agg_type q_par_desc_inst = """ JOIN descriptors_inst AS idstart ON idstart.id = {join_to}.desc_inst @@ -235,7 +230,8 @@ class kw: # keywords adc.addr_type as prov_value_addr_type, adc.addr_field as prov_value_addr_field, adc.value_type as prov_value_type -""" + (""", +""" + ( + """, NULL::address_type as prov_unit_addr_type, NULL as prov_unit_addr_field, NULL::field_value_type as prov_unit_type, @@ -243,7 +239,10 @@ class kw: # keywords NULL::address_type as prov_aspect_addr_type, NULL as prov_aspect_addr_field, NULL::field_value_type as prov_aspect_type -""" if sn.unit or endpoint == 'values/inst' else '') +""" + if sn.unit or endpoint == "values/inst" + else "" + ) s_prov_q = """ adq.addr_type as prov_value_addr_type, @@ -277,132 +276,211 @@ class kw: # keywords LEFT OUTER JOIN addresses AS ada ON ada.id = odq.addr_aspect """ - maybe_distinct = 'DISTINCT ' if ( - endpoint.startswith('desc/') or - endpoint in ('terms', 'units', 'aspects') or - (sn.objects or kw.prov) and not kw.source_only) else '' + maybe_distinct = ( + "DISTINCT " + if ( + endpoint.startswith("desc/") + or endpoint in ("terms", "units", "aspects") + or (sn.objects or kw.prov) + and not kw.source_only + ) + else "" + ) ep_select_cat, ep_select_quant = ep_select if isinstance(ep_select, tuple) else (ep_select, ep_select) - select_cat = f'SELECT {maybe_distinct}{ep_select_cat}' + ( - (s_prov_objs + s_prov_i + ((',\n' + s_prov_c) if endpoint != 'values/inst' else '')) if kw.prov else '') - select_quant = f'SELECT {maybe_distinct}{ep_select_quant}' + ( - (s_prov_objs + s_prov_i + ((',\n' + s_prov_q) if endpoint != 'values/inst' else '')) if kw.prov else '') + select_cat = f"SELECT {maybe_distinct}{ep_select_cat}" + ( + (s_prov_objs + s_prov_i + ((",\n" + s_prov_c) if endpoint != "values/inst" else "")) if kw.prov else "" + ) + select_quant = f"SELECT {maybe_distinct}{ep_select_quant}" + ( + (s_prov_objs + s_prov_i + ((",\n" + s_prov_q) if endpoint != "values/inst" else "")) if kw.prov else "" + ) _where_cat, _where_quant, params = get_where(kwargs) - where_cat = f'WHERE {_where_cat}' if _where_cat else '' - where_quant = f'WHERE {_where_quant}' if _where_quant else '' + where_cat = f"WHERE {_where_cat}" if _where_cat else "" + where_quant = f"WHERE {_where_quant}" if _where_quant else "" # FIXME even trying to be smart here about which joins to pull just papers over the underlying perf issue # shaves about 140ms off but the underlying issue remains - q_cat = '\n'.join(( - 'FROM values_cat AS cv', - '\n'.join(( - 'JOIN descriptors_inst AS idin', - 'CROSS JOIN LATERAL get_child_closed_desc_inst(idin.id) AS idc ON cv.desc_inst = idc.child -- FIXME', - )) if kw.desc_inst else '', - (q_par_desc_inst.format(join_to='cv') if sn.parent_desc_inst else - 'JOIN descriptors_inst AS id ON cv.desc_inst = id.id' - ) if sn.desc_inst or kw.desc_inst else '', # FIXME handle parents case - 'JOIN values_inst AS im ON cv.instance = im.id', - '\n'.join(( - 'JOIN descriptors_cat AS cd ON cv.desc_cat = cd.id', - 'LEFT OUTER JOIN descriptors_inst AS cdid ON cd.domain = cdid.id -- XXX TODO mismach', - )) if sn.desc_cat or kw.desc_cat else '', - 'LEFT OUTER JOIN controlled_terms AS ct ON cv.value_controlled = ct.id' if sn.value_cat or kw.value_cat else '', - (('\n' - 'JOIN objects AS o ON cv.object = o.id\n' - 'LEFT OUTER JOIN objects_internal AS oi\n' - 'ON oi.id = o.id\n') - if kw.source_only else - ('\n' # have to use LEFT OUTER because object might have only one of cat or quant - 'LEFT OUTER JOIN values_quant AS qv ON qv.instance = im.id\n' - 'JOIN objects AS o ON cv.object = o.id OR qv.object = o.id\n' - 'LEFT OUTER JOIN objects_internal AS oi\n' - 'ON oi.id = o.id\n') - ) if sn.objects or kw.prov else '', - (q_prov_i + q_prov_c) if kw.prov else '', - )) - - q_quant = '\n'.join(( - 'FROM values_quant AS qv', - '\n'.join(( - 'JOIN descriptors_inst AS idin', - 'CROSS JOIN LATERAL get_child_closed_desc_inst(idin.id) AS idc ON qv.desc_inst = idc.child -- FIXME', - )) if kw.desc_inst else '', - (q_par_desc_inst.format(join_to='qv') if sn.parent_desc_inst else - 'JOIN descriptors_inst AS id ON qv.desc_inst = id.id' - ) if sn.desc_inst or kw.desc_inst else '', # FIXME handle parents case - 'JOIN values_inst AS im ON qv.instance = im.id', - 'JOIN descriptors_quant AS qd ON qv.desc_quant = qd.id' if ( - sn.desc_quant or kw.desc_quant) else '', - '\n'.join(( - 'JOIN aspects AS ain', - 'CROSS JOIN LATERAL get_child_closed_aspect(ain.id) AS ac ON qd.aspect = ac.child', - 'JOIN aspects AS a ON ac.child = a.id', - )) if kw.aspect else ( - (q_par_aspect if sn.parent_aspect else - 'JOIN aspects AS a ON qd.aspect = a.id' - ) if sn.aspect else ''), # FIXME handle parents case - 'LEFT OUTER JOIN units AS u ON qd.unit = u.id' if sn.unit or kw.unit else '', - (('\n' - 'JOIN objects AS o ON qv.object = o.id\n' - 'LEFT OUTER JOIN objects_internal AS oi ON oi.id = o.id\n') - if kw.source_only else - ('\n' # have to use LEFT OUTER because object might have only one of cat or quant - 'LEFT OUTER JOIN values_cat AS cv ON cv.instance = im.id\n' - 'JOIN objects AS o ON qv.object = o.id OR cv.object = o.id\n' - 'LEFT OUTER JOIN objects_internal AS oi ON oi.id = o.id\n') - ) if sn.objects or kw.prov else '', - (q_prov_i + q_prov_q) if kw.prov else '', - )) - - sw_cat = f'{select_cat}\n{q_cat}\n{where_cat}' # XXX yes this can be malformed in some cases - sw_quant = f'{select_quant}\n{q_quant}\n{where_quant}' # XXX yes this can be malformed in some cases - if endpoint in ('values/cat', 'terms', 'desc/cat'): + q_cat = "\n".join( + ( + "FROM values_cat AS cv", + ( + "\n".join( + ( + "JOIN descriptors_inst AS idin", + "CROSS JOIN LATERAL get_child_closed_desc_inst(idin.id) AS idc ON cv.desc_inst = idc.child -- FIXME", + ) + ) + if kw.desc_inst + else "" + ), + ( + ( + q_par_desc_inst.format(join_to="cv") + if sn.parent_desc_inst + else "JOIN descriptors_inst AS id ON cv.desc_inst = id.id" + ) + if sn.desc_inst or kw.desc_inst + else "" + ), # FIXME handle parents case + "JOIN values_inst AS im ON cv.instance = im.id", + ( + "\n".join( + ( + "JOIN descriptors_cat AS cd ON cv.desc_cat = cd.id", + "LEFT OUTER JOIN descriptors_inst AS cdid ON cd.domain = cdid.id -- XXX TODO mismach", + ) + ) + if sn.desc_cat or kw.desc_cat + else "" + ), + ( + "LEFT OUTER JOIN controlled_terms AS ct ON cv.value_controlled = ct.id" + if sn.value_cat or kw.value_cat + else "" + ), + ( + ( + ( + "\n" + "JOIN objects AS o ON cv.object = o.id\n" + "LEFT OUTER JOIN objects_internal AS oi\n" + "ON oi.id = o.id\n" + ) + if kw.source_only + else ( + "\n" # have to use LEFT OUTER because object might have only one of cat or quant + "LEFT OUTER JOIN values_quant AS qv ON qv.instance = im.id\n" + "JOIN objects AS o ON cv.object = o.id OR qv.object = o.id\n" + "LEFT OUTER JOIN objects_internal AS oi\n" + "ON oi.id = o.id\n" + ) + ) + if sn.objects or kw.prov + else "" + ), + (q_prov_i + q_prov_c) if kw.prov else "", + ) + ) + + q_quant = "\n".join( + ( + "FROM values_quant AS qv", + ( + "\n".join( + ( + "JOIN descriptors_inst AS idin", + "CROSS JOIN LATERAL get_child_closed_desc_inst(idin.id) AS idc ON qv.desc_inst = idc.child -- FIXME", + ) + ) + if kw.desc_inst + else "" + ), + ( + ( + q_par_desc_inst.format(join_to="qv") + if sn.parent_desc_inst + else "JOIN descriptors_inst AS id ON qv.desc_inst = id.id" + ) + if sn.desc_inst or kw.desc_inst + else "" + ), # FIXME handle parents case + "JOIN values_inst AS im ON qv.instance = im.id", + "JOIN descriptors_quant AS qd ON qv.desc_quant = qd.id" if (sn.desc_quant or kw.desc_quant) else "", + ( + "\n".join( + ( + "JOIN aspects AS ain", + "CROSS JOIN LATERAL get_child_closed_aspect(ain.id) AS ac ON qd.aspect = ac.child", + "JOIN aspects AS a ON ac.child = a.id", + ) + ) + if kw.aspect + else ( + (q_par_aspect if sn.parent_aspect else "JOIN aspects AS a ON qd.aspect = a.id") if sn.aspect else "" + ) + ), # FIXME handle parents case + "LEFT OUTER JOIN units AS u ON qd.unit = u.id" if sn.unit or kw.unit else "", + ( + ( + ( + "\n" + "JOIN objects AS o ON qv.object = o.id\n" + "LEFT OUTER JOIN objects_internal AS oi ON oi.id = o.id\n" + ) + if kw.source_only + else ( + "\n" # have to use LEFT OUTER because object might have only one of cat or quant + "LEFT OUTER JOIN values_cat AS cv ON cv.instance = im.id\n" + "JOIN objects AS o ON qv.object = o.id OR cv.object = o.id\n" + "LEFT OUTER JOIN objects_internal AS oi ON oi.id = o.id\n" + ) + ) + if sn.objects or kw.prov + else "" + ), + (q_prov_i + q_prov_q) if kw.prov else "", + ) + ) + + sw_cat = f"{select_cat}\n{q_cat}\n{where_cat}" # XXX yes this can be malformed in some cases + sw_quant = f"{select_quant}\n{q_quant}\n{where_quant}" # XXX yes this can be malformed in some cases + if endpoint in ("values/cat", "terms", "desc/cat"): query = sw_cat - elif endpoint in ('values/quant', 'units', 'aspects', 'desc/quant'): # FIXME TODO make it possible to cross query terms, units, aspects + elif endpoint in ( + "values/quant", + "units", + "aspects", + "desc/quant", + ): # FIXME TODO make it possible to cross query terms, units, aspects query = sw_quant else: - operator = 'UNION' if 'union-cat-quant' in kwargs and kwargs['union-cat-quant'] else 'INTERSECT' - query = f'{sw_cat}\n{operator}\n{sw_quant}' + operator = "UNION" if "union-cat-quant" in kwargs and kwargs["union-cat-quant"] else "INTERSECT" + query = f"{sw_cat}\n{operator}\n{sw_quant}" - log.log(9, '\n' + query) + log.log(9, "\n" + query) return query, params def to_json(record_type, res, prov=False): rows = list(res) if rows: - if record_type == 'object': - result = [{k: v for k, v in r._asdict().items() if k != 'id'} - # do not leak internal ids because the might change and are not meaningful - if r.id_type == 'quantdb' else - {k: v for k, v in r._asdict().items() if k != 'updated_transitive'} - for r in rows] - elif record_type is None and 'type' in rows[0]._fields: - rem_cat = 'value', 'agg_type' + if record_type == "object": + result = [ + ( + {k: v for k, v in r._asdict().items() if k != "id"} + # do not leak internal ids because the might change and are not meaningful + if r.id_type == "quantdb" + else {k: v for k, v in r._asdict().items() if k != "updated_transitive"} + ) + for r in rows + ] + elif record_type is None and "type" in rows[0]._fields: + rem_cat = "value", "agg_type" + def type_fields_cat(k): - if k == 'pred_or_asp': - return 'desc_cat' - elif k == 'vo_or_unit': - return 'value_open' + if k == "pred_or_asp": + return "desc_cat" + elif k == "vo_or_unit": + return "value_open" else: return k - rem_quant = 'domain', 'range', 'value_controlled' + rem_quant = "domain", "range", "value_controlled" + def type_fields_quant(k): - if k == 'pred_or_asp': - return 'aspect' - elif k == 'vo_or_unit': - return 'unit' + if k == "pred_or_asp": + return "aspect" + elif k == "vo_or_unit": + return "unit" else: return k def prow(r): - if r.type == 'value-cat': + if r.type == "value-cat": rem, type_fields = rem_cat, type_fields_cat - elif r.type == 'value-quant': + elif r.type == "value-quant": rem, type_fields = rem_quant, type_fields_quant else: - raise NotImplementedError(f'wat {r.type}') + raise NotImplementedError(f"wat {r.type}") return {type_fields(k): v for k, v in r._asdict().items() if k not in rem} @@ -412,35 +490,40 @@ def prow(r): for r in result: if record_type is not None: - r['type'] = record_type + r["type"] = record_type - for cull_none in ('subclassof',): + for cull_none in ("subclassof",): if cull_none in r and r[cull_none] is None: r.pop(cull_none) if prov: + def pop_prefix(d, prefix): - usc = prefix.count('_') - return {k.split('_', 1 + usc)[-1]:v for k in list(d) if k.startswith(prefix + '_') and (v := d.pop(k)) is not None} + usc = prefix.count("_") + return { + k.split("_", 1 + usc)[-1]: v + for k in list(d) + if k.startswith(prefix + "_") and (v := d.pop(k)) is not None + } for r in result: - provs = pop_prefix(r, 'prov') - if 'source_id_type' in provs and provs['source_id_type'] == 'quantdb': - provs.pop('source_id', None) # don't leak internal ids + provs = pop_prefix(r, "prov") + if "source_id_type" in provs and provs["source_id_type"] == "quantdb": + provs.pop("source_id", None) # don't leak internal ids else: - provs.pop('source_updated_transitive', None) # always None in this case + provs.pop("source_updated_transitive", None) # always None in this case - for prefix in ('desc_inst', 'inst', 'value', 'value', 'source'): + for prefix in ("desc_inst", "inst", "value", "value", "source"): d = pop_prefix(provs, prefix) if d: - d['type'] = 'address' if prefix != 'source' else 'object' + d["type"] = "address" if prefix != "source" else "object" provs[prefix] = d - provs['type'] = 'prov' - r['prov'] = provs + provs["type"] = "prov" + r["prov"] = provs out = result - #breakpoint() + # breakpoint() else: out = [] @@ -455,60 +538,51 @@ def wrap_out(endpoint, kwargs, out): parameters = {k: v for k, v in kwargs.items() if v} n_records = len(out) blob = { - 'type': 'quantdb-query-result', - 'endpoint': endpoint, - 'parameters': parameters, - 'records': n_records, - 'result': out, + "type": "quantdb-query-result", + "endpoint": endpoint, + "parameters": parameters, + "records": n_records, + "result": out, } return blob args_default = { - 'object': [], - 'updated-transitive': None, # TODO needed to query for some internal - + "object": [], + "updated-transitive": None, # TODO needed to query for some internal ## inst - 'desc-inst': [], # aka class - + "desc-inst": [], # aka class # value-inst - 'dataset': None, - 'inst': [], - 'subject': [], - 'sample': [], - 'include-equivalent': False, - + "dataset": None, + "inst": [], + "subject": [], + "sample": [], + "include-equivalent": False, ## cat - 'desc-cat': [], # aka predicate - - 'value-cat': [], - 'value-cat-open': [], - + "desc-cat": [], # aka predicate + "value-cat": [], + "value-cat-open": [], ## quant # desc-quant - 'unit': [], - 'aspect': [], - 'agg-type': None, + "unit": [], + "aspect": [], + "agg-type": None, # TODO shape - - 'value-quant': None, - 'value-quant-margin': None, - 'value-quant-min': None, - 'value-quant-max': None, - - 'limit': 100, + "value-quant": None, + "value-quant-margin": None, + "value-quant-min": None, + "value-quant-max": None, + "limit": 100, #'operator': 'INTERSECT', # XXX ... - 'union-cat-quant': False, # by default we intersect but sometimes you want the union instead e.g. if object is passed - 'source-only': False, - 'include-unused': False, - 'prov': False, - + "union-cat-quant": False, # by default we intersect but sometimes you want the union instead e.g. if object is passed + "source-only": False, + "include-unused": False, + "prov": False, #'cat-value': [], #'class': [], #'predicate': None, #'object': None, #'filter': [], - #'quant-value': None, #'quant-margin': None, #'quant-min': None, @@ -520,62 +594,64 @@ def getArgs(request, endpoint, dev=False): default = copy.deepcopy(args_default) if dev: - default['return-query'] = False + default["return-query"] = False # modify defaults by endpoint - if endpoint != 'objects': - default.pop('source-only') + if endpoint != "objects": + default.pop("source-only") - if not (endpoint.startswith('desc/') or endpoint in ('terms', 'units', 'aspects')): - default.pop('include-unused') + if not (endpoint.startswith("desc/") or endpoint in ("terms", "units", "aspects")): + default.pop("include-unused") else: # prevent filtering on the thing we are trying to query - if endpoint == 'terms': - default.pop('value-cat') - elif endpoint == 'units': - default.pop('unit') - elif endpoint == 'aspects': - default.pop('aspect') - elif endpoint == 'desc/inst': - default.pop('desc-inst') - elif endpoint == 'desc/cat': - default.pop('desc-cat') - - if not endpoint.startswith('values/'): - default.pop('prov') - elif endpoint == 'values/cat': - [default.pop(k) for k in list(default) if k.startswith('value-quant') or k in ('unit', 'aspect', 'agg-type')] - elif endpoint == 'values/quant': - [default.pop(k) for k in list(default) if k in ('desc-cat', 'value-cat', 'value-cat-open')] + if endpoint == "terms": + default.pop("value-cat") + elif endpoint == "units": + default.pop("unit") + elif endpoint == "aspects": + default.pop("aspect") + elif endpoint == "desc/inst": + default.pop("desc-inst") + elif endpoint == "desc/cat": + default.pop("desc-cat") + + if not endpoint.startswith("values/"): + default.pop("prov") + elif endpoint == "values/cat": + [default.pop(k) for k in list(default) if k.startswith("value-quant") or k in ("unit", "aspect", "agg-type")] + elif endpoint == "values/quant": + [default.pop(k) for k in list(default) if k in ("desc-cat", "value-cat", "value-cat-open")] extras = set(request.args) - set(default) if extras: # FIXME raise this as a 401, TODO need error types for this - nl = '\n' - raise exc.UnknownArg(f'unknown args: {nl.join(extras)}') + nl = "\n" + raise exc.UnknownArg(f"unknown args: {nl.join(extras)}") def convert(k, d): if k in request.args: # arity is determined here - if k in ('dataset', 'include-equivalent', 'union-cat-quant', 'include-unused') or k.startswith('value-quant-'): + if k in ("dataset", "include-equivalent", "union-cat-quant", "include-unused") or k.startswith( + "value-quant-" + ): v = request.args[k] - if k in ('dataset',): + if k in ("dataset",): v = uuid.UUID(v) else: v = request.args.getlist(k) - if k in ('object',): + if k in ("object",): v = [uuid.UUID(_) for _ in v] # caste to uuid to simplify sqlalchemy type mapping else: return d - if k in ('include-equivalent', 'union-cat-quant', 'include-unused'): - if v.lower() == 'true': + if k in ("include-equivalent", "union-cat-quant", "include-unused"): + if v.lower() == "true": return True - elif v.lower() == 'false': + elif v.lower() == "false": return False else: raise TypeError(f'Expected a bool, got "{v}" instead.') - elif k.startswith('value-quant') or k in ('limit',): + elif k.startswith("value-quant") or k in ("limit",): try: return float(v) except ValueError as e: @@ -583,34 +659,34 @@ def convert(k, d): else: return v - out = {k:convert(k, v) for k, v in default.items()} + out = {k: convert(k, v) for k, v in default.items()} return out -def make_app(db=None, name='quantdb-api-server', dev=False): +def make_app(db=None, name="quantdb-api-server", dev=False): app = Flask(name) - kwargs = {k:auth.get(f'db-{k}') # TODO integrate with cli options - for k in ('user', 'host', 'port', 'database')} - kwargs['dbuser'] = kwargs.pop('user') - app.config['SQLALCHEMY_DATABASE_URI'] = dbUri(**kwargs) # use os.environ.update - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + kwargs = {k: auth.get(f"db-{k}") for k in ("user", "host", "port", "database")} # TODO integrate with cli options + kwargs["dbuser"] = kwargs.pop("user") + app.config["SQLALCHEMY_DATABASE_URI"] = dbUri(**kwargs) # use os.environ.update + app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db.init_app(app) session = db.session - bp = '/api/1/' + bp = "/api/1/" def default_flow(endpoint, record_type, query_fun, json_fun, alt_query_fun=None): try: kwargs = getArgs(request, endpoint, dev=dev) except exc.UnknownArg as e: - return json.dumps({'error': e.args[0], 'http_response_status': 422}), 422 + return json.dumps({"error": e.args[0], "http_response_status": 422}), 422 except Exception as e: breakpoint() raise e - def gkw(k): return k in kwargs and kwargs[k] + def gkw(k): + return k in kwargs and kwargs[k] - if gkw('include-unused'): + if gkw("include-unused"): query_fun = alt_query_fun # FIXME record_type is actually determined entirely in query_fun right now @@ -620,7 +696,7 @@ def gkw(k): return k in kwargs and kwargs[k] breakpoint() raise e - if gkw('return-query'): + if gkw("return-query"): return query try: @@ -630,137 +706,140 @@ def gkw(k): return k in kwargs and kwargs[k] raise e try: - out = json_fun(record_type, res, prov=('prov' in kwargs and kwargs['prov'])) - resp = json.dumps(wrap_out(endpoint, kwargs, out), cls=JEncode), 200, {'Content-Type': 'application/json'} + out = json_fun(record_type, res, prov=("prov" in kwargs and kwargs["prov"])) + resp = json.dumps(wrap_out(endpoint, kwargs, out), cls=JEncode), 200, {"Content-Type": "application/json"} except Exception as e: breakpoint() raise e return resp - @app.route(f'{bp}/objects') + @app.route(f"{bp}/test") + def route_test_check(): + "objects with derived values that match all criteria" + return "testing-api" + + @app.route(f"{bp}/objects") def route_1_objects(): "objects with derived values that match all criteria" - return default_flow('objects', 'object', main_query, to_json) + return default_flow("objects", "object", main_query, to_json) - @app.route(f'{bp}/desc/inst') - @app.route(f'{bp}/descriptors/inst') - @app.route(f'{bp}/classes') + @app.route(f"{bp}/desc/inst") + @app.route(f"{bp}/descriptors/inst") + @app.route(f"{bp}/classes") def route_1_desc_inst(): def query(endpoint, kwargs): - return ('SELECT ' - - 'id.iri, ' - 'id.label, ' - 'idpar.label as subclassof' - + return ( + ( + "SELECT " + "id.iri, " + "id.label, " + "idpar.label as subclassof" """ FROM descriptors_inst AS id LEFT OUTER JOIN class_parent AS ip ON ip.id = id.id LEFT OUTER JOIN descriptors_inst AS idpar ON idpar.id = ip.parent -"""), {} +""" + ), + {}, + ) - return default_flow('desc/inst', 'desc-inst', main_query, to_json, alt_query_fun=query) + return default_flow("desc/inst", "desc-inst", main_query, to_json, alt_query_fun=query) - @app.route(f'{bp}/desc/cat') - @app.route(f'{bp}/descriptors/cat') - @app.route(f'{bp}/predicates') + @app.route(f"{bp}/desc/cat") + @app.route(f"{bp}/descriptors/cat") + @app.route(f"{bp}/predicates") def route_1_desc_cat(): def query(endpoint, kwargs): - return ('select ' - - 'cd.label, ' - 'cdid.label AS domain, ' - 'cd.range, ' - 'cd.description ' - - 'from descriptors_cat as cd ' - 'left outer join descriptors_inst as cdid on cdid.id = cd.domain' - ), {} - - return default_flow('desc/cat', 'desc-cat', main_query, to_json, alt_query_fun=query) # TODO likely need different args e.g. to filter by desc_inst - - @app.route(f'{bp}/desc/quant') - @app.route(f'{bp}/descriptors/quant') + return ( + "select " + "cd.label, " + "cdid.label AS domain, " + "cd.range, " + "cd.description " + "from descriptors_cat as cd " + "left outer join descriptors_inst as cdid on cdid.id = cd.domain" + ), {} + + return default_flow( + "desc/cat", "desc-cat", main_query, to_json, alt_query_fun=query + ) # TODO likely need different args e.g. to filter by desc_inst + + @app.route(f"{bp}/desc/quant") + @app.route(f"{bp}/descriptors/quant") def route_1_desc_quant(): def query(endpoint, kwargs): - return ('select ' - - 'qd.label, ' - 'id.label AS domain, ' - 'qd.shape, ' - 'qd.aggregation_type as agg_type, ' - 'a.label AS aspect, ' - 'u.label AS unit, ' - 'qd.description ' - - 'from descriptors_quant as qd ' - 'left outer join descriptors_inst as id on id.id = qd.domain ' - 'left outer join units as u on u.id = qd.unit ' - 'join aspects as a on a.id = qd.aspect' - ), {} - - return default_flow('desc/quant', 'desc-quant', main_query, to_json, alt_query_fun=query) # TODO likely need different args e.g. to filter by desc_inst - - @app.route(f'{bp}/values/inst') - @app.route(f'{bp}/instances') + return ( + "select " + "qd.label, " + "id.label AS domain, " + "qd.shape, " + "qd.aggregation_type as agg_type, " + "a.label AS aspect, " + "u.label AS unit, " + "qd.description " + "from descriptors_quant as qd " + "left outer join descriptors_inst as id on id.id = qd.domain " + "left outer join units as u on u.id = qd.unit " + "join aspects as a on a.id = qd.aspect" + ), {} + + return default_flow( + "desc/quant", "desc-quant", main_query, to_json, alt_query_fun=query + ) # TODO likely need different args e.g. to filter by desc_inst + + @app.route(f"{bp}/values/inst") + @app.route(f"{bp}/instances") def route_1_val_inst(): "instances associated with values that match all critiera" - return default_flow('values/inst', 'instance', main_query, to_json) + return default_flow("values/inst", "instance", main_query, to_json) - @app.route(f'{bp}/values') - @app.route(f'{bp}/values/cat-quant') + @app.route(f"{bp}/values") + @app.route(f"{bp}/values/cat-quant") def route_1_val_cat_quant(): - return default_flow('values/cat-quant', None, main_query, to_json) + return default_flow("values/cat-quant", None, main_query, to_json) - @app.route(f'{bp}/values/cat') + @app.route(f"{bp}/values/cat") def route_1_val_cat(): - return default_flow('values/cat', 'value-cat', main_query, to_json) + return default_flow("values/cat", "value-cat", main_query, to_json) - @app.route(f'{bp}/values/quant') + @app.route(f"{bp}/values/quant") def route_1_val_quant(): - return default_flow('values/quant', 'value-quant', main_query, to_json) + return default_flow("values/quant", "value-quant", main_query, to_json) - @app.route(f'{bp}/terms') - @app.route(f'{bp}/controlled-terms') + @app.route(f"{bp}/terms") + @app.route(f"{bp}/controlled-terms") def route_1_cterms(): def query(endpoint, kwargs): - return ('select ' - - 'ct.iri, ' - 'ct.label ' + return ("select " "ct.iri, " "ct.label " "from controlled_terms as ct"), {} - 'from controlled_terms as ct'), {} + return default_flow("terms", "term", main_query, to_json, alt_query_fun=query) - return default_flow('terms', 'term', main_query, to_json, alt_query_fun=query) - - @app.route(f'{bp}/units') + @app.route(f"{bp}/units") def route_1_units(): def query(endpoint, kwargs): - return ('select ' - - 'u.iri, ' - 'u.label ' - - 'from units as u'), {} + return ("select " "u.iri, " "u.label " "from units as u"), {} - return default_flow('units', 'unit', main_query, to_json, alt_query_fun=query) + return default_flow("units", "unit", main_query, to_json, alt_query_fun=query) - @app.route(f'{bp}/aspects') + @app.route(f"{bp}/aspects") def route_1_aspects(): def query(endpoint, kwargs): - return ('SELECT ' - - 'a.iri, ' - 'a.label, ' - 'aspar.label AS subclassof ' - + return ( + ( + "SELECT " + "a.iri, " + "a.label, " + "aspar.label AS subclassof " """ FROM aspects AS a LEFT OUTER JOIN aspect_parent AS ap ON ap.id = a.id LEFT OUTER JOIN aspects AS aspar ON aspar.id = ap.parent -"""), {} +""" + ), + {}, + ) - return default_flow('aspects', 'aspect', main_query, to_json, alt_query_fun=query) + return default_flow("aspects", "aspect", main_query, to_json, alt_query_fun=query) return app diff --git a/quantdb/router.py b/quantdb/router.py index a6b1d18..df75984 100644 --- a/quantdb/router.py +++ b/quantdb/router.py @@ -50,3 +50,174 @@ def get_example( result = mysql_db.execute(query) data = result.mappings().all() return data + + def default_flow(endpoint, record_type, query_fun, json_fun, alt_query_fun=None): + try: + kwargs = getArgs(request, endpoint, dev=dev) + except exc.UnknownArg as e: + return json.dumps({"error": e.args[0], "http_response_status": 422}), 422 + except Exception as e: + breakpoint() + raise e + + def gkw(k): + return k in kwargs and kwargs[k] + + if gkw("include-unused"): + query_fun = alt_query_fun + + # FIXME record_type is actually determined entirely in query_fun right now + try: + query, params = query_fun(endpoint, kwargs) + except Exception as e: + breakpoint() + raise e + + if gkw("return-query"): + return query + + try: + res = session.execute(sql_text(query), params) + except Exception as e: + breakpoint() + raise e + + try: + out = json_fun(record_type, res, prov=("prov" in kwargs and kwargs["prov"])) + resp = json.dumps(wrap_out(endpoint, kwargs, out), cls=JEncode), 200, {"Content-Type": "application/json"} + except Exception as e: + breakpoint() + raise e + + return resp + + +# @app.route(f"{bp}/test") +# def route_test_check(): +# "objects with derived values that match all criteria" +# return "testing-api" + +# @app.route(f"{bp}/objects") +# def route_1_objects(): +# "objects with derived values that match all criteria" +# return default_flow("objects", "object", main_query, to_json) + +# @app.route(f"{bp}/desc/inst") +# @app.route(f"{bp}/descriptors/inst") +# @app.route(f"{bp}/classes") +# def route_1_desc_inst(): +# def query(endpoint, kwargs): +# return ( +# ( +# "SELECT " +# "id.iri, " +# "id.label, " +# "idpar.label as subclassof" +# """ +# FROM descriptors_inst AS id +# LEFT OUTER JOIN class_parent AS ip ON ip.id = id.id +# LEFT OUTER JOIN descriptors_inst AS idpar ON idpar.id = ip.parent +# """ +# ), +# {}, +# ) + +# return default_flow("desc/inst", "desc-inst", main_query, to_json, alt_query_fun=query) + +# @app.route(f"{bp}/desc/cat") +# @app.route(f"{bp}/descriptors/cat") +# @app.route(f"{bp}/predicates") +# def route_1_desc_cat(): +# def query(endpoint, kwargs): +# return ( +# "select " +# "cd.label, " +# "cdid.label AS domain, " +# "cd.range, " +# "cd.description " +# "from descriptors_cat as cd " +# "left outer join descriptors_inst as cdid on cdid.id = cd.domain" +# ), {} + +# return default_flow( +# "desc/cat", "desc-cat", main_query, to_json, alt_query_fun=query +# ) # TODO likely need different args e.g. to filter by desc_inst + +# @app.route(f"{bp}/desc/quant") +# @app.route(f"{bp}/descriptors/quant") +# def route_1_desc_quant(): +# def query(endpoint, kwargs): +# return ( +# "select " +# "qd.label, " +# "id.label AS domain, " +# "qd.shape, " +# "qd.aggregation_type as agg_type, " +# "a.label AS aspect, " +# "u.label AS unit, " +# "qd.description " +# "from descriptors_quant as qd " +# "left outer join descriptors_inst as id on id.id = qd.domain " +# "left outer join units as u on u.id = qd.unit " +# "join aspects as a on a.id = qd.aspect" +# ), {} + +# return default_flow( +# "desc/quant", "desc-quant", main_query, to_json, alt_query_fun=query +# ) # TODO likely need different args e.g. to filter by desc_inst + +# @app.route(f"{bp}/values/inst") +# @app.route(f"{bp}/instances") +# def route_1_val_inst(): +# "instances associated with values that match all critiera" +# return default_flow("values/inst", "instance", main_query, to_json) + +# @app.route(f"{bp}/values") +# @app.route(f"{bp}/values/cat-quant") +# def route_1_val_cat_quant(): +# return default_flow("values/cat-quant", None, main_query, to_json) + +# @app.route(f"{bp}/values/cat") +# def route_1_val_cat(): +# return default_flow("values/cat", "value-cat", main_query, to_json) + +# @app.route(f"{bp}/values/quant") +# def route_1_val_quant(): +# return default_flow("values/quant", "value-quant", main_query, to_json) + +# @app.route(f"{bp}/terms") +# @app.route(f"{bp}/controlled-terms") +# def route_1_cterms(): +# def query(endpoint, kwargs): +# return ("select " "ct.iri, " "ct.label " "from controlled_terms as ct"), {} + +# return default_flow("terms", "term", main_query, to_json, alt_query_fun=query) + +# @app.route(f"{bp}/units") +# def route_1_units(): +# def query(endpoint, kwargs): +# return ("select " "u.iri, " "u.label " "from units as u"), {} + +# return default_flow("units", "unit", main_query, to_json, alt_query_fun=query) + +# @app.route(f"{bp}/aspects") +# def route_1_aspects(): +# def query(endpoint, kwargs): +# return ( +# ( +# "SELECT " +# "a.iri, " +# "a.label, " +# "aspar.label AS subclassof " +# """ +# FROM aspects AS a +# LEFT OUTER JOIN aspect_parent AS ap ON ap.id = a.id +# LEFT OUTER JOIN aspects AS aspar ON aspar.id = ap.parent +# """ +# ), +# {}, +# ) + +# return default_flow("aspects", "aspect", main_query, to_json, alt_query_fun=query) + +# return app From 2987252b583077772b213146464e7f7dc5bead0a Mon Sep 17 00:00:00 2001 From: tmsincomb Date: Tue, 21 May 2024 13:35:42 -0700 Subject: [PATCH 08/13] pre-commit pass --- .gitignore | 2 +- README.pdf | Bin 11284 -> 11273 bytes pyproject.toml | 2 +- quantdb/api.py | 11 +- quantdb/api_server.py | 9 +- quantdb/auth-config.py | 60 ++-- quantdb/config.py | 3 +- quantdb/exceptions.py | 4 +- quantdb/ingest.py | 617 ++++++++++++++++++++++++----------------- quantdb/main.py | 1 + quantdb/router.py | 228 +-------------- quantdb/utils.py | 33 +-- sql/postgres.sql | 1 - test/test_api.py | 158 +++++------ 14 files changed, 501 insertions(+), 628 deletions(-) diff --git a/.gitignore b/.gitignore index 74f9128..2d87f8c 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -nb/ \ No newline at end of file +nb/ diff --git a/README.pdf b/README.pdf index eff2baa79326409fbcb2e138d0d691504f2854bd..0431664500efbcab3a942e6e80100ec14a0195da 100644 GIT binary patch delta 195 zcmbOd(HXJfi2d}') + # return int(f'{sam_ind}{seg_ind}{rest:0>2d}') return comps @@ -186,12 +188,12 @@ def pps(path_structure): p1 = sam_1, subject # child, parent to match db convention wasDerivedFrom p2 = segment, sam_1 return { - 'parents': (p1, p2), - 'subject': subject, - 'sample': segment, - 'modality': modality, + "parents": (p1, p2), + "subject": subject, + "sample": segment, + "modality": modality, # note that because we do not convert to a single value we cannot include raw_anat_index in the qdb but that's ok - 'raw_anat_index_v1': anat_index(segment), + "raw_anat_index_v1": anat_index(segment), } else: raise NotImplementedError(path_structure) @@ -199,124 +201,155 @@ def pps(path_structure): def ext(j): out = {} - out['dataset'] = j['dataset_id'] - out['object'] = j['remote_id'] - out['file_id'] = j['file_id'] if 'file_id' in j else int(j['uri_api'].rsplit('/')[-1]) # XXX old pathmeta schema that didn't include file id - ps = pathlib.Path(j['dataset_relative_path']).parts - [p for p in ps if p.startswith('sub-') or p.startswith('sam-')] + out["dataset"] = j["dataset_id"] + out["object"] = j["remote_id"] + out["file_id"] = ( + j["file_id"] if "file_id" in j else int(j["uri_api"].rsplit("/")[-1]) + ) # XXX old pathmeta schema that didn't include file id + ps = pathlib.Path(j["dataset_relative_path"]).parts + [p for p in ps if p.startswith("sub-") or p.startswith("sam-")] out.update(pps(ps)) return out class Queries: - def __init__(self, session): self.session = session def address_from_fadd_type_fadd(self, fadd_type, fadd): # FIXME multi etc. - res = [i for i, in self.session.execute(sql_text("select * from address_from_fadd_type_fadd(:fadd_type, :fadd)"), dict(fadd_type=fadd_type, fadd=fadd))] + res = [ + i + for i, in self.session.execute( + sql_text("select * from address_from_fadd_type_fadd(:fadd_type, :fadd)"), + dict(fadd_type=fadd_type, fadd=fadd), + ) + ] if res: return res[0] - def desc_inst_from_label(self, label): # FIXME multi etc. - res = [i for i, in self.session.execute(sql_text("select * from desc_inst_from_label(:label)"), dict(label=label))] + res = [ + i for i, in self.session.execute(sql_text("select * from desc_inst_from_label(:label)"), dict(label=label)) + ] if res: return res[0] - def desc_quant_from_label(self, label): # FIXME multi etc. - res = [i for i, in self.session.execute(sql_text("select * from desc_quant_from_label(:label)"), dict(label=label))] + res = [ + i for i, in self.session.execute(sql_text("select * from desc_quant_from_label(:label)"), dict(label=label)) + ] if res: return res[0] - def desc_cat_from_label_domain_label(self, label, domain_label): # FIXME multi etc. - res = [i for i, in self.session.execute(sql_text("select * from desc_cat_from_label_domain_label(:label, :domain_label)"), - dict(label=label, domain_label=domain_label))] + res = [ + i + for i, in self.session.execute( + sql_text("select * from desc_cat_from_label_domain_label(:label, :domain_label)"), + dict(label=label, domain_label=domain_label), + ) + ] if res: return res[0] - def cterm_from_label(self, label): # FIXME multi etc. res = [i for i, in self.session.execute(sql_text("select * from cterm_from_label(:label)"), dict(label=label))] if res: return res[0] - def insts_from_dataset_ids(self, dataset, ids): - return list(self.session.execute(sql_text("select * from insts_from_dataset_ids(:dataset, :ids)"), dict(dataset=dataset, ids=ids))) + return list( + self.session.execute( + sql_text("select * from insts_from_dataset_ids(:dataset, :ids)"), dict(dataset=dataset, ids=ids) + ) + ) class InternalIds: def __init__(self, queries): q = queries self._q = queries - - self.addr_suid = q.address_from_fadd_type_fadd('tabular-header', 'id_sub') - self.addr_said = q.address_from_fadd_type_fadd('tabular-header', 'id_sam') - self.addr_spec = q.address_from_fadd_type_fadd('tabular-header', 'species') - self.addr_saty = q.address_from_fadd_type_fadd('tabular-header', 'sample_type') - self.addr_tmod = q.address_from_fadd_type_fadd('tabular-header', 'modality') - #addr_trai = address_from_fadd_type_fadd('tabular-header', 'raw_anat_index') - #addr_tnai = address_from_fadd_type_fadd('tabular-header', 'norm_anat_index') - #addr_context = address_from_fadd_type_fadd('context', '#/path-metadata/{index of match remote_id}/dataset_relative_path') # XXX this doesn't do what we want, I think what we really would want in these contexts are objects_internal that reference the file system state for a given updated snapshot, that is the real "object" that corresponds to the path-metadata.json that we are working from + self.addr_suid = q.address_from_fadd_type_fadd("tabular-header", "id_sub") + self.addr_said = q.address_from_fadd_type_fadd("tabular-header", "id_sam") + self.addr_spec = q.address_from_fadd_type_fadd("tabular-header", "species") + self.addr_saty = q.address_from_fadd_type_fadd("tabular-header", "sample_type") - #addr_jpmod = address_from_fadd_type_fadd('json-path-with-types', '#/#int/modality') - #addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/#int/anat_index') - #addr_jpnai = address_from_fadd_type_fadd('json-path-with-types', '#/#int/norm_anat_index') + self.addr_tmod = q.address_from_fadd_type_fadd("tabular-header", "modality") + # addr_trai = address_from_fadd_type_fadd('tabular-header', 'raw_anat_index') + # addr_tnai = address_from_fadd_type_fadd('tabular-header', 'norm_anat_index') + # addr_context = address_from_fadd_type_fadd('context', '#/path-metadata/{index of match remote_id}/dataset_relative_path') # XXX this doesn't do what we want, I think what we really would want in these contexts are objects_internal that reference the file system state for a given updated snapshot, that is the real "object" that corresponds to the path-metadata.json that we are working from - self.addr_jpdrp = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path') + # addr_jpmod = address_from_fadd_type_fadd('json-path-with-types', '#/#int/modality') + # addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/#int/anat_index') + # addr_jpnai = address_from_fadd_type_fadd('json-path-with-types', '#/#int/norm_anat_index') - # XXX these are more accurate if opaque - self.addr_jpmod = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-modality') - #addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-raw-anat-index') - self.addr_jpnai = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1') - self.addr_jpnain = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1-min') - self.addr_jpnaix = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1-max') - self.addr_jpsuid = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-subject-id') - self.addr_jpsaid = q.address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-sample-id') + self.addr_jpdrp = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path" + ) - self.addr_jpspec = q.address_from_fadd_type_fadd('json-path-with-types', '#/local/tom-made-it-up/species') - self.addr_jpsaty = q.address_from_fadd_type_fadd('json-path-with-types', '#/local/tom-made-it-up/sample_type') + # XXX these are more accurate if opaque + self.addr_jpmod = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-modality" + ) + # addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-raw-anat-index') + self.addr_jpnai = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1" + ) + self.addr_jpnain = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1-min" + ) + self.addr_jpnaix = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1-max" + ) + self.addr_jpsuid = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-subject-id" + ) + self.addr_jpsaid = q.address_from_fadd_type_fadd( + "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-sample-id" + ) + + self.addr_jpspec = q.address_from_fadd_type_fadd("json-path-with-types", "#/local/tom-made-it-up/species") + self.addr_jpsaty = q.address_from_fadd_type_fadd("json-path-with-types", "#/local/tom-made-it-up/sample_type") # future version when we actually have the metadata files - #addr_jpmod = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/manifest/#int/modality') - #addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/raw_anat_index') - #addr_jpnai = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/norm_anat_index') - #addr_jpsuid = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/subjects/#int/id_sub') - #addr_jpsaid = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/id_sam') - - self.addr_const_null = q.address_from_fadd_type_fadd('constant', None) - - #qd_rai = desc_quant_from_label('reva ft sample anatomical location distance index raw') - self.qd_nai = q.desc_quant_from_label('reva ft sample anatomical location distance index normalized v1') - self.qd_nain = q.desc_quant_from_label('reva ft sample anatomical location distance index normalized v1 min') - self.qd_naix = q.desc_quant_from_label('reva ft sample anatomical location distance index normalized v1 max') - - self.cd_mod = q.desc_cat_from_label_domain_label('hasDataAboutItModality', None) - self.cd_bot = q.desc_cat_from_label_domain_label('bottom', None) # we just need something we can reference that points to null so we can have a refernce to all the objects - - self.id_human = q.desc_inst_from_label('human') - self.id_nerve = q.desc_inst_from_label('nerve') - self.id_nerve_volume = q.desc_inst_from_label('nerve-volume') + # addr_jpmod = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/manifest/#int/modality') + # addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/raw_anat_index') + # addr_jpnai = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/norm_anat_index') + # addr_jpsuid = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/subjects/#int/id_sub') + # addr_jpsaid = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/id_sam') + + self.addr_const_null = q.address_from_fadd_type_fadd("constant", None) + + # qd_rai = desc_quant_from_label('reva ft sample anatomical location distance index raw') + self.qd_nai = q.desc_quant_from_label("reva ft sample anatomical location distance index normalized v1") + self.qd_nain = q.desc_quant_from_label("reva ft sample anatomical location distance index normalized v1 min") + self.qd_naix = q.desc_quant_from_label("reva ft sample anatomical location distance index normalized v1 max") + + self.cd_mod = q.desc_cat_from_label_domain_label("hasDataAboutItModality", None) + self.cd_bot = q.desc_cat_from_label_domain_label( + "bottom", None + ) # we just need something we can reference that points to null so we can have a refernce to all the objects + + self.id_human = q.desc_inst_from_label("human") + self.id_nerve = q.desc_inst_from_label("nerve") + self.id_nerve_volume = q.desc_inst_from_label("nerve-volume") self.luid = { - 'human': self.id_human, - 'nerve': self.id_nerve, - 'nerve-volume': self.id_nerve_volume, + "human": self.id_human, + "nerve": self.id_nerve, + "nerve-volume": self.id_nerve_volume, } - self.ct_mod = q.cterm_from_label('microct') # lol ct ct - self.ct_hack = q.cterm_from_label('hack-associate-some-value') + self.ct_mod = q.cterm_from_label("microct") # lol ct ct + self.ct_hack = q.cterm_from_label("hack-associate-some-value") self.luct = { - 'ct-hack': self.ct_hack, - 'microct': self.ct_mod, + "ct-hack": self.ct_hack, + "microct": self.ct_mod, } @@ -326,20 +359,30 @@ class Inserts: def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_args=None): - """ generic ingest workflow - this_dataset_updated_uuid might not be needed in future, - add a kwarg to control it maybe? + """generic ingest workflow + this_dataset_updated_uuid might not be needed in future, + add a kwarg to control it maybe? """ - ocdn = ' ON CONFLICT DO NOTHING' if dev else '' + ocdn = " ON CONFLICT DO NOTHING" if dev else "" if extract_fun is None and values_args is None: - raise TypeError('need one of extract_fun or values_args') - - (updated_transitive, values_objects, values_dataset_object, - make_values_instances, make_values_parents, - make_void, make_vocd, make_voqd, make_values_cat, make_values_quant - ) = extract_fun(dataset_uuid) if values_args is None else values_args + raise TypeError("need one of extract_fun or values_args") + + ( + updated_transitive, + values_objects, + values_dataset_object, + make_values_instances, + make_values_parents, + make_void, + make_vocd, + make_voqd, + make_values_cat, + make_values_quant, + ) = ( + extract_fun(dataset_uuid) if values_args is None else values_args + ) q = Queries(session) i = InternalIds(q) @@ -348,13 +391,21 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a values_instances = make_values_instances(i) res0 = session.execute( - sql_text('INSERT INTO objects (id, id_type) VALUES (:id, :id_type) ON CONFLICT DO NOTHING'), - dict(id=dataset_uuid, id_type='dataset')) + sql_text("INSERT INTO objects (id, id_type) VALUES (:id, :id_type) ON CONFLICT DO NOTHING"), + dict(id=dataset_uuid, id_type="dataset"), + ) # oh dear https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql res1 = session.execute( - sql_text("WITH ins AS (INSERT INTO objects_internal (type, dataset, updated_transitive, label) VALUES ('path-metadata', :dataset, :updated_transitive, :label) ON CONFLICT DO NOTHING RETURNING id) SELECT id FROM ins UNION ALL SELECT id FROM objects_internal WHERE type = 'path-metadata' AND dataset = :dataset AND updated_transitive = :updated_transitive"), # TODO see whether we actually need union all here or whether union by itself is sufficient - dict(dataset=dataset_uuid, updated_transitive=updated_transitive, label=f'test-load-for-f001 {isoformat(updated_transitive)}')) + sql_text( + "WITH ins AS (INSERT INTO objects_internal (type, dataset, updated_transitive, label) VALUES ('path-metadata', :dataset, :updated_transitive, :label) ON CONFLICT DO NOTHING RETURNING id) SELECT id FROM ins UNION ALL SELECT id FROM objects_internal WHERE type = 'path-metadata' AND dataset = :dataset AND updated_transitive = :updated_transitive" + ), # TODO see whether we actually need union all here or whether union by itself is sufficient + dict( + dataset=dataset_uuid, + updated_transitive=updated_transitive, + label=f"test-load-for-f001 {isoformat(updated_transitive)}", + ), + ) # it is better to use this approach for all top down information # just assume that it is coming from some combination of the metadata files and the file system @@ -365,17 +416,23 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a voqd = make_voqd(this_dataset_updated_uuid, i) res1_1 = session.execute( - sql_text('INSERT INTO objects (id, id_type, id_internal) VALUES (:id, :id_type, :id) ON CONFLICT DO NOTHING'), # FIXME bad ocdn here - dict(id=this_dataset_updated_uuid, id_type='quantdb')) + sql_text( + "INSERT INTO objects (id, id_type, id_internal) VALUES (:id, :id_type, :id) ON CONFLICT DO NOTHING" + ), # FIXME bad ocdn here + dict(id=this_dataset_updated_uuid, id_type="quantdb"), + ) vt, params = makeParamsValues(values_objects) - session.execute(sql_text(f'INSERT INTO objects (id, id_type, id_file) VALUES {vt}{ocdn}'), params) + session.execute(sql_text(f"INSERT INTO objects (id, id_type, id_file) VALUES {vt}{ocdn}"), params) vt, params = makeParamsValues(values_dataset_object) - session.execute(sql_text(f'INSERT INTO dataset_object (dataset, object) VALUES {vt}{ocdn}'), params) + session.execute(sql_text(f"INSERT INTO dataset_object (dataset, object) VALUES {vt}{ocdn}"), params) vt, params = makeParamsValues(values_instances) - session.execute(sql_text(f'INSERT INTO values_inst (dataset, id_formal, type, desc_inst, id_sub, id_sam) VALUES {vt}{ocdn}'), params) + session.execute( + sql_text(f"INSERT INTO values_inst (dataset, id_formal, type, desc_inst, id_sub, id_sam) VALUES {vt}{ocdn}"), + params, + ) # inserts that depend on instances having already been inserted ilt = q.insts_from_dataset_ids(dataset_uuid, [f for d, f, *rest in values_instances]) @@ -385,27 +442,38 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a values_qv = make_values_quant(this_dataset_updated_uuid, i, luinst) vt, params = makeParamsValues(values_parents) - session.execute(sql_text(f'INSERT INTO instance_parent VALUES {vt}{ocdn}'), params) + session.execute(sql_text(f"INSERT INTO instance_parent VALUES {vt}{ocdn}"), params) vt, params = makeParamsValues(void) - session.execute(sql_text(f'INSERT INTO obj_desc_inst (object, desc_inst, addr_field, addr_desc_inst) VALUES {vt}{ocdn}'), params) + session.execute( + sql_text(f"INSERT INTO obj_desc_inst (object, desc_inst, addr_field, addr_desc_inst) VALUES {vt}{ocdn}"), params + ) vt, params = makeParamsValues(vocd) - session.execute(sql_text(f'INSERT INTO obj_desc_cat (object, desc_cat, addr_field) VALUES {vt}{ocdn}'), params) + session.execute(sql_text(f"INSERT INTO obj_desc_cat (object, desc_cat, addr_field) VALUES {vt}{ocdn}"), params) vt, params = makeParamsValues(voqd) - session.execute(sql_text(f'INSERT INTO obj_desc_quant (object, desc_quant, addr_field) VALUES {vt}{ocdn}'), params) + session.execute(sql_text(f"INSERT INTO obj_desc_quant (object, desc_quant, addr_field) VALUES {vt}{ocdn}"), params) vt, params = makeParamsValues(values_cv) - session.execute(sql_text(f'INSERT INTO values_cat (value_open, value_controlled, object, desc_inst, desc_cat, instance) VALUES {vt}{ocdn}'), params) + session.execute( + sql_text( + f"INSERT INTO values_cat (value_open, value_controlled, object, desc_inst, desc_cat, instance) VALUES {vt}{ocdn}" + ), + params, + ) vt, params, bindparams = makeParamsValues( # FIXME LOL the types spec here is atrocious ... but it does work ... # XXX and barring the unfortunate case, which we have now encountered where # now fixed in the local impl - values_qv, row_types=(None, None, None, None, None, JSONB)) + values_qv, + row_types=(None, None, None, None, None, JSONB), + ) - t = sql_text(f'INSERT INTO values_quant (value, object, desc_inst, desc_quant, instance, value_blob) VALUES {vt}{ocdn}') + t = sql_text( + f"INSERT INTO values_quant (value, object, desc_inst, desc_quant, instance, value_blob) VALUES {vt}{ocdn}" + ) tin = t.bindparams(*bindparams) session.execute(tin, params) @@ -415,11 +483,16 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a def extract_reva_ft(dataset_uuid, source_local=False, visualize=False): if source_local: - with open(pathlib.Path(f'~/.local/share/sparcur/export/datasets/{dataset_uuid}/LATEST/path-metadata.json').expanduser(), 'rt') as f: + with open( + pathlib.Path( + f"~/.local/share/sparcur/export/datasets/{dataset_uuid}/LATEST/path-metadata.json" + ).expanduser(), + "rt", + ) as f: blob = json.load(f) else: - resp = requests.get(f'https://cassava.ucsd.edu/sparc/datasets/{dataset_uuid}/LATEST/path-metadata.json') + resp = requests.get(f"https://cassava.ucsd.edu/sparc/datasets/{dataset_uuid}/LATEST/path-metadata.json") try: blob = resp.json() @@ -427,117 +500,129 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=False): breakpoint() raise e - for j in blob['data']: - j['type'] = 'pathmeta' + for j in blob["data"]: + j["type"] = "pathmeta" ir = fromJson(blob) - updated_transitive = max([i['timestamp_updated'] for i in ir['data'][1:]]) # 1: to skip the dataset object itself - - jpx = [r for r in ir['data'] if 'mimetype' in r and r['mimetype'] == 'image/jpx'] - + updated_transitive = max([i["timestamp_updated"] for i in ir["data"][1:]]) # 1: to skip the dataset object itself + jpx = [r for r in ir["data"] if "mimetype" in r and r["mimetype"] == "image/jpx"] exts = [ext(j) for j in jpx] - #hrm = sorted(exts, key=lambda j: j['raw_anat_index']) - #max_rai = max([e['raw_anat_index'] for e in exts]) - #import math - #log_max_rai = math.log10(max_rai) + # hrm = sorted(exts, key=lambda j: j['raw_anat_index']) + # max_rai = max([e['raw_anat_index'] for e in exts]) + # import math + # log_max_rai = math.log10(max_rai) # normalize the index by mapping distinct values to the integers - nondist = sorted([e['raw_anat_index_v1'] for e in exts]) - lin_distinct = {v:i for i, v in enumerate(sorted(set([e['raw_anat_index_v1'] for e in exts])))} + nondist = sorted([e["raw_anat_index_v1"] for e in exts]) + lin_distinct = {v: i for i, v in enumerate(sorted(set([e["raw_anat_index_v1"] for e in exts])))} max_distinct = len(lin_distinct) mdp1 = max_distinct + 0.1 # to simplify adding overlap dd = defaultdict(list) for e in exts: - #e['norm_anat_index'] = math.log10(e['raw_anat_index']) / log_max_rai - pos = lin_distinct[e['raw_anat_index_v1']] - e['norm_anat_index_v1'] = (pos + 0.55) / mdp1 - e['norm_anat_index_v1_min'] = pos / mdp1 - e['norm_anat_index_v1_max'] = (pos + 1.1) / mdp1 # ensure there is overlap between section for purposes of testing + # e['norm_anat_index'] = math.log10(e['raw_anat_index']) / log_max_rai + pos = lin_distinct[e["raw_anat_index_v1"]] + e["norm_anat_index_v1"] = (pos + 0.55) / mdp1 + e["norm_anat_index_v1_min"] = pos / mdp1 + e["norm_anat_index_v1_max"] = ( + pos + 1.1 + ) / mdp1 # ensure there is overlap between section for purposes of testing # TODO norm_anat_index_min # TODO norm_anat_index_max - dd[e['dataset'], e['sample']].append(e) + dd[e["dataset"], e["sample"]].append(e) inst_obj_index = dict(dd) - max_nai = max([e['norm_anat_index_v1'] for e in exts]) - min_nain = min([e['norm_anat_index_v1_min'] for e in exts]) - max_naix = max([e['norm_anat_index_v1_max'] for e in exts]) + max_nai = max([e["norm_anat_index_v1"] for e in exts]) + min_nain = min([e["norm_anat_index_v1_min"] for e in exts]) + max_naix = max([e["norm_anat_index_v1_max"] for e in exts]) if visualize: x = list(range(len(exts))) - #ry = sorted([e['raw_anat_index'] for e in exts]) - ny = sorted([e['norm_anat_index_v1'] for e in exts]) - nyn = sorted([e['norm_anat_index_v1_min'] for e in exts]) - nyx = sorted([e['norm_anat_index_v1_max'] for e in exts]) + # ry = sorted([e['raw_anat_index'] for e in exts]) + ny = sorted([e["norm_anat_index_v1"] for e in exts]) + nyn = sorted([e["norm_anat_index_v1_min"] for e in exts]) + nyx = sorted([e["norm_anat_index_v1_max"] for e in exts]) nnx = list(zip(nyn, nyx)) import pylab as plt import seaborn - #plt.figure() - #seaborn.scatterplot(x=x, y=ry) + + # plt.figure() + # seaborn.scatterplot(x=x, y=ry) plt.figure() - #end = 10 + # end = 10 end = len(x) seaborn.scatterplot(x=x[:end], y=ny[:end]) seaborn.scatterplot(x=x[:end], y=nyn[:end]) seaborn.scatterplot(x=x[:end], y=nyx[:end]) - datasets = {i.uuid: {'id_type': i.type} - for e in exts - if (i := e['dataset']) - } + datasets = {i.uuid: {"id_type": i.type} for e in exts if (i := e["dataset"])} - packages = {i.uuid: { - 'id_type': i.type, - 'id_file': e['file_id'], + packages = { + i.uuid: { + "id_type": i.type, + "id_file": e["file_id"], + } + for e in exts + if (i := e["object"]) } - for e in exts - if (i := e['object']) - } objects = {**datasets, **packages} - dataset_object = list(set((d.uuid, o.uuid) for e in exts - if (d := e['dataset']) and (o := e['object']) - )) - - subjects = {k: {'type': 'subject', - 'desc_inst': 'human', - 'id_sub': k[1], - } for k in sorted(set((e['dataset'], e['subject']) for e in exts))} - segments = {k[:2]: {'type': 'sample', # FIXME vs below ??? - 'desc_inst': 'nerve-volume', # FIXME should this be nerve-segment and then we use nerve-volume for the 1:1 with files? - 'id_sub': k[-1], - 'id_sam': k[1], - } for k in sorted(set((e['dataset'], e['sample'], e['subject']) for e in exts))} - parents = sorted(set((e['dataset'],) + p for e in exts for p in e['parents'])) - sam_other = {p[:2]:{'type': 'sample', 'desc_inst': 'nerve', 'id_sub': p[-1], 'id_sam': p[1]} for p in parents if p[:2] not in segments} + dataset_object = list(set((d.uuid, o.uuid) for e in exts if (d := e["dataset"]) and (o := e["object"]))) + + subjects = { + k: { + "type": "subject", + "desc_inst": "human", + "id_sub": k[1], + } + for k in sorted(set((e["dataset"], e["subject"]) for e in exts)) + } + segments = { + k[:2]: { + "type": "sample", # FIXME vs below ??? + "desc_inst": "nerve-volume", # FIXME should this be nerve-segment and then we use nerve-volume for the 1:1 with files? + "id_sub": k[-1], + "id_sam": k[1], + } + for k in sorted(set((e["dataset"], e["sample"], e["subject"]) for e in exts)) + } + parents = sorted(set((e["dataset"],) + p for e in exts for p in e["parents"])) + sam_other = { + p[:2]: {"type": "sample", "desc_inst": "nerve", "id_sub": p[-1], "id_sam": p[1]} + for p in parents + if p[:2] not in segments + } samples = {**segments, **sam_other} instances = {**subjects, **samples} values_objects = [ - (i, o['id_type'], o['id_file'] if 'id_file' in o else None) + (i, o["id_type"], o["id_file"] if "id_file" in o else None) for i, o in objects.items() - if o['id_type'] != 'dataset' # already did it above + if o["id_type"] != "dataset" # already did it above ] values_dataset_object = dataset_object def make_values_instances(i): values_instances = [ - (d.uuid, f, inst['type'], i.luid[inst['desc_inst']], - inst['id_sub'] if 'id_sub' in inst else None, - inst['id_sam'] if 'id_sam' in inst else None, + ( + d.uuid, + f, + inst["type"], + i.luid[inst["desc_inst"]], + inst["id_sub"] if "id_sub" in inst else None, + inst["id_sam"] if "id_sam" in inst else None, ) - for (d, f), inst in instances.items()] + for (d, f), inst in instances.items() + ] return values_instances def make_values_parents(luinst): - """ need the lookup for instances """ - values_parents = [ - (luinst[d.uuid, child], luinst[d.uuid, parent]) - for d, child, parent in parents] + """need the lookup for instances""" + values_parents = [(luinst[d.uuid, child], luinst[d.uuid, parent]) for d, child, parent in parents] return values_parents # XXX REMINDER an object descriptor pair can be associated with an arbitrary number of measured instances @@ -548,20 +633,25 @@ def make_values_parents(luinst): # XXX the external source is part of the issue I think def make_void(this_dataset_updated_uuid, i): void = [ # FIXME this is rather annoying because you have to list all expected types in advance, but I guess that is what we want - (this_dataset_updated_uuid, i.id_human, i.addr_jpsuid, i.addr_jpspec), (this_dataset_updated_uuid, i.id_nerve, i.addr_jpsaid, i.addr_jpsaty), (this_dataset_updated_uuid, i.id_nerve_volume, i.addr_jpsaid, i.addr_jpsaty), - # FIXME what about manifests? those link metadata as an extra hop ... everything meta related needs to come from combined object metadata ??? # that would certainly make more sense than the nonsense that is going on here, it would simplify the referencing for all the topdown # information that we have but it sort of obscures sources, however this _is_ contextual info ... sigh # XXX the other option would just be to just put the darned files in the instance measured table :/ because we do have data about them # annoying :/ - - #+ [(i, 'nerve-volume', addr_context) for i in packages] - ] + [(o, i.id_nerve_volume, i.addr_const_null, None) # XXX FIXME this is the only way I can think to do this right now ? - for o, b in objects.items() if b['id_type'] == 'package'] + # + [(i, 'nerve-volume', addr_context) for i in packages] + ] + [ + ( + o, + i.id_nerve_volume, + i.addr_const_null, + None, + ) # XXX FIXME this is the only way I can think to do this right now ? + for o, b in objects.items() + if b["id_type"] == "package" + ] return void @@ -570,8 +660,11 @@ def make_vocd(this_dataset_updated_uuid, i): # FIXME this reveals that there are cases where we may not have void for a single file or that the id comes from context and is not embedded # figuring out how to turn that around is going to take a bit of thinking (this_dataset_updated_uuid, i.cd_mod, i.addr_jpmod), - ] + [(o, i.cd_bot, i.addr_const_null) # XXX FIXME this is the only way I can think to do this right now ? - for o, b in objects.items() if b['id_type'] == 'package'] + ] + [ + (o, i.cd_bot, i.addr_const_null) # XXX FIXME this is the only way I can think to do this right now ? + for o, b in objects.items() + if b["id_type"] == "package" + ] return vocd @@ -579,35 +672,38 @@ def make_voqd(this_dataset_updated_uuid, i): voqd = [ # FIXME this isn't quite right, we should just do it to the segments and pretend it is from the samples file I think? # FIXME addr_aspect and addr_unit are ... implicit, there is no conditional dispatch but the choice of the constant quantative descriptor comes from ... tgbugs? or what? I think we just leave it as null because it is a constant across all fields, but maybe we just use constant null? but why bother in that case? (this_dataset_updated_uuid, i.qd_nai, i.addr_jpnai), - (this_dataset_updated_uuid, i.qd_nain, i.addr_jpnain), # XXX FIXME is this really an aggregation type in this case? I guess it technically if the sample space is over all the points inside the segment or something + ( + this_dataset_updated_uuid, + i.qd_nain, + i.addr_jpnain, + ), # XXX FIXME is this really an aggregation type in this case? I guess it technically if the sample space is over all the points inside the segment or something (this_dataset_updated_uuid, i.qd_naix, i.addr_jpnaix), ] return voqd def make_values_cat(this_dataset_updated_uuid, i, luinst): - #obj_index = {e['object']: e for e in exts} + # obj_index = {e['object']: e for e in exts} values_cv = [ # value_open, value_controlled, object, desc_inst, desc_cat - (e[k], - i.luct[e[k]], - this_dataset_updated_uuid, - #e['object'].uuid, # FIXME still not right this comes from the updated latest - i.id_nerve_volume, - cd, # if we mess this up the fk ok obj_desc_cat will catch it :) - luinst[e['dataset'].uuid, e['sample']], # get us the instance + ( + e[k], + i.luct[e[k]], + this_dataset_updated_uuid, + # e['object'].uuid, # FIXME still not right this comes from the updated latest + i.id_nerve_volume, + cd, # if we mess this up the fk ok obj_desc_cat will catch it :) + luinst[e["dataset"].uuid, e["sample"]], # get us the instance ) for e in exts - for k, cd in ( - ('modality', i.cd_mod), - ) - + for k, cd in (("modality", i.cd_mod),) ] + [ - (None, - i.ct_hack, - e['object'].uuid, - i.id_nerve_volume, - i.cd_bot, # if we mess this up the fk ok obj_desc_cat will catch it :) - luinst[e['dataset'].uuid, e['sample']], # get us the instance + ( + None, + i.ct_hack, + e["object"].uuid, + i.id_nerve_volume, + i.cd_bot, # if we mess this up the fk ok obj_desc_cat will catch it :) + luinst[e["dataset"].uuid, e["sample"]], # get us the instance ) for e in exts ] @@ -616,41 +712,48 @@ def make_values_cat(this_dataset_updated_uuid, i, luinst): def make_values_quant(this_dataset_updated_uuid, i, luinst): values_qv = [ # value, object, desc_inst, desc_quant, inst, value_blob - (e[k], - #e['object'].uuid, # FIXME TODO we could fill this here but we choose to use this_dataset_updated_uuid instead I think - this_dataset_updated_uuid, - i.id_nerve_volume, - qd, # if we mess this up the fk ok obj_desc_cat will catch it :) - luinst[e['dataset'].uuid, e['sample']], # get us the instance - e[k], + ( + e[k], + # e['object'].uuid, # FIXME TODO we could fill this here but we choose to use this_dataset_updated_uuid instead I think + this_dataset_updated_uuid, + i.id_nerve_volume, + qd, # if we mess this up the fk ok obj_desc_cat will catch it :) + luinst[e["dataset"].uuid, e["sample"]], # get us the instance + e[k], ) for e in exts for k, qd in ( - #('raw_anat_index', qd_rai), # XXX this is a bad place to store object -> field -> qd mappings also risks mismatch on address - ('norm_anat_index_v1', i.qd_nai), - ('norm_anat_index_v1_min', i.qd_nain), - ('norm_anat_index_v1_max', i.qd_naix), + # ('raw_anat_index', qd_rai), # XXX this is a bad place to store object -> field -> qd mappings also risks mismatch on address + ("norm_anat_index_v1", i.qd_nai), + ("norm_anat_index_v1_min", i.qd_nain), + ("norm_anat_index_v1_max", i.qd_naix), ) ] return values_qv - return (updated_transitive, values_objects, values_dataset_object, - make_values_instances, make_values_parents, - make_void, make_vocd, make_voqd, - make_values_cat, make_values_quant, - ) + return ( + updated_transitive, + values_objects, + values_dataset_object, + make_values_instances, + make_values_parents, + make_void, + make_vocd, + make_voqd, + make_values_cat, + make_values_quant, + ) # this is where things get annoying with needing selects on instance measured def ingest_reva_ft_all(session, source_local=False, do_insert=True, batch=False, commit=False, dev=False): - dataset_uuids = ( - 'aa43eda8-b29a-4c25-9840-ecbd57598afc', # f001 + "aa43eda8-b29a-4c25-9840-ecbd57598afc", # f001 # the rest have uuid1 issues :/ all in the undefined folder it seems, might be able to fix with a reupload - 'bc4cc558-727c-4691-ae6d-498b57a10085', # f002 # XXX has a uuid1 so breaking in prod right now have to push the new pipelines - 'ec6ad74e-7b59-409b-8fc7-a304319b6faf', # f003 # also uuid1 issue - 'a8b2bdc7-54df-46a3-810e-83cdf33cfc3a', # f004 - '04a5fed9-7ba6-4292-b1a6-9cab5c38895f', # f005 + "bc4cc558-727c-4691-ae6d-498b57a10085", # f002 # XXX has a uuid1 so breaking in prod right now have to push the new pipelines + "ec6ad74e-7b59-409b-8fc7-a304319b6faf", # f003 # also uuid1 issue + "a8b2bdc7-54df-46a3-810e-83cdf33cfc3a", # f004 + "04a5fed9-7ba6-4292-b1a6-9cab5c38895f", # f005 ) batched = [] @@ -670,9 +773,9 @@ def ingest_reva_ft_all(session, source_local=False, do_insert=True, batch=False, def main(source_local=False, commit=False, echo=True): from quantdb.config import auth - dbkwargs = {k:auth.get(f'db-{k}') # TODO integrate with cli options - for k in ('user', 'host', 'port', 'database')} - dbkwargs['dbuser'] = dbkwargs.pop('user') + + dbkwargs = {k: auth.get(f"db-{k}") for k in ("user", "host", "port", "database")} # TODO integrate with cli options + dbkwargs["dbuser"] = dbkwargs.pop("user") engine = create_engine(dbUri(**dbkwargs)) engine.echo = echo session = Session(engine) @@ -689,5 +792,5 @@ def main(source_local=False, commit=False, echo=True): engine.dispose() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/quantdb/main.py b/quantdb/main.py index ad52ff0..deb5246 100644 --- a/quantdb/main.py +++ b/quantdb/main.py @@ -1,5 +1,6 @@ from fastapi import FastAPI from fastapi.middleware.wsgi import WSGIMiddleware + from quantdb.api_server import app as flask_app app = FastAPI() diff --git a/quantdb/router.py b/quantdb/router.py index df75984..0d2913c 100644 --- a/quantdb/router.py +++ b/quantdb/router.py @@ -1,223 +1,21 @@ -from __future__ import annotations +from typing import Literal -from functools import lru_cache -from typing import Any, Generator +import uvicorn +from fastapi import FastAPI +from fastapi.middleware.wsgi import WSGIMiddleware +from fastapi.staticfiles import StaticFiles -from fastapi import Depends, FastAPI -from sqlalchemy import text -from sqlalchemy.orm import Session # type: ignore - -from quantdb.config import Settings - -from quantdb import mysql_app +from quantdb.api_server import app as quantdb_flask_app app = FastAPI() +app.mount("/quantdb", WSGIMiddleware(quantdb_flask_app)) -@lru_cache() -def get_settings(): - """ - Get settings from mongodb - alternative for test and production credientials - - Returns - ------- - Settings - pydantic BaseSettings model with MongoDB credentials - """ - return Settings() - - -def get_mysql_db() -> Generator[Session, Any, None]: - db = mysql_app.database.SessionLocal() - try: - yield db - finally: - db.close() - - -@app.get( - "/example", - status_code=200, -) -def get_example( - mysql_db: Session = Depends(get_mysql_db), # type: ignore -): - query = text( - """ - select * from limit 10 - """ - ) - result = mysql_db.execute(query) - data = result.mappings().all() - return data - - def default_flow(endpoint, record_type, query_fun, json_fun, alt_query_fun=None): - try: - kwargs = getArgs(request, endpoint, dev=dev) - except exc.UnknownArg as e: - return json.dumps({"error": e.args[0], "http_response_status": 422}), 422 - except Exception as e: - breakpoint() - raise e - - def gkw(k): - return k in kwargs and kwargs[k] - - if gkw("include-unused"): - query_fun = alt_query_fun - - # FIXME record_type is actually determined entirely in query_fun right now - try: - query, params = query_fun(endpoint, kwargs) - except Exception as e: - breakpoint() - raise e - - if gkw("return-query"): - return query - - try: - res = session.execute(sql_text(query), params) - except Exception as e: - breakpoint() - raise e - - try: - out = json_fun(record_type, res, prov=("prov" in kwargs and kwargs["prov"])) - resp = json.dumps(wrap_out(endpoint, kwargs, out), cls=JEncode), 200, {"Content-Type": "application/json"} - except Exception as e: - breakpoint() - raise e - - return resp - - -# @app.route(f"{bp}/test") -# def route_test_check(): -# "objects with derived values that match all criteria" -# return "testing-api" - -# @app.route(f"{bp}/objects") -# def route_1_objects(): -# "objects with derived values that match all criteria" -# return default_flow("objects", "object", main_query, to_json) - -# @app.route(f"{bp}/desc/inst") -# @app.route(f"{bp}/descriptors/inst") -# @app.route(f"{bp}/classes") -# def route_1_desc_inst(): -# def query(endpoint, kwargs): -# return ( -# ( -# "SELECT " -# "id.iri, " -# "id.label, " -# "idpar.label as subclassof" -# """ -# FROM descriptors_inst AS id -# LEFT OUTER JOIN class_parent AS ip ON ip.id = id.id -# LEFT OUTER JOIN descriptors_inst AS idpar ON idpar.id = ip.parent -# """ -# ), -# {}, -# ) - -# return default_flow("desc/inst", "desc-inst", main_query, to_json, alt_query_fun=query) - -# @app.route(f"{bp}/desc/cat") -# @app.route(f"{bp}/descriptors/cat") -# @app.route(f"{bp}/predicates") -# def route_1_desc_cat(): -# def query(endpoint, kwargs): -# return ( -# "select " -# "cd.label, " -# "cdid.label AS domain, " -# "cd.range, " -# "cd.description " -# "from descriptors_cat as cd " -# "left outer join descriptors_inst as cdid on cdid.id = cd.domain" -# ), {} - -# return default_flow( -# "desc/cat", "desc-cat", main_query, to_json, alt_query_fun=query -# ) # TODO likely need different args e.g. to filter by desc_inst - -# @app.route(f"{bp}/desc/quant") -# @app.route(f"{bp}/descriptors/quant") -# def route_1_desc_quant(): -# def query(endpoint, kwargs): -# return ( -# "select " -# "qd.label, " -# "id.label AS domain, " -# "qd.shape, " -# "qd.aggregation_type as agg_type, " -# "a.label AS aspect, " -# "u.label AS unit, " -# "qd.description " -# "from descriptors_quant as qd " -# "left outer join descriptors_inst as id on id.id = qd.domain " -# "left outer join units as u on u.id = qd.unit " -# "join aspects as a on a.id = qd.aspect" -# ), {} - -# return default_flow( -# "desc/quant", "desc-quant", main_query, to_json, alt_query_fun=query -# ) # TODO likely need different args e.g. to filter by desc_inst - -# @app.route(f"{bp}/values/inst") -# @app.route(f"{bp}/instances") -# def route_1_val_inst(): -# "instances associated with values that match all critiera" -# return default_flow("values/inst", "instance", main_query, to_json) - -# @app.route(f"{bp}/values") -# @app.route(f"{bp}/values/cat-quant") -# def route_1_val_cat_quant(): -# return default_flow("values/cat-quant", None, main_query, to_json) - -# @app.route(f"{bp}/values/cat") -# def route_1_val_cat(): -# return default_flow("values/cat", "value-cat", main_query, to_json) - -# @app.route(f"{bp}/values/quant") -# def route_1_val_quant(): -# return default_flow("values/quant", "value-quant", main_query, to_json) - -# @app.route(f"{bp}/terms") -# @app.route(f"{bp}/controlled-terms") -# def route_1_cterms(): -# def query(endpoint, kwargs): -# return ("select " "ct.iri, " "ct.label " "from controlled_terms as ct"), {} - -# return default_flow("terms", "term", main_query, to_json, alt_query_fun=query) - -# @app.route(f"{bp}/units") -# def route_1_units(): -# def query(endpoint, kwargs): -# return ("select " "u.iri, " "u.label " "from units as u"), {} - -# return default_flow("units", "unit", main_query, to_json, alt_query_fun=query) - -# @app.route(f"{bp}/aspects") -# def route_1_aspects(): -# def query(endpoint, kwargs): -# return ( -# ( -# "SELECT " -# "a.iri, " -# "a.label, " -# "aspar.label AS subclassof " -# """ -# FROM aspects AS a -# LEFT OUTER JOIN aspect_parent AS ap ON ap.id = a.id -# LEFT OUTER JOIN aspects AS aspar ON aspar.id = ap.parent -# """ -# ), -# {}, -# ) +# Root URL +@app.get("/") +def index() -> Literal["Hello"]: + return "Hello" -# return default_flow("aspects", "aspect", main_query, to_json, alt_query_fun=query) -# return app +if __name__ == "__main__": + uvicorn.run("router:app", host="localhost", port=8000, reload=True) diff --git a/quantdb/utils.py b/quantdb/utils.py index 169a5c1..7619e64 100644 --- a/quantdb/utils.py +++ b/quantdb/utils.py @@ -1,6 +1,6 @@ +import logging import os import sys -import logging from datetime import datetime @@ -12,46 +12,39 @@ def makeSimpleLogger(name, level=logging.INFO): logger.setLevel(level) ch = logging.StreamHandler() # FileHander goes to disk - fmt = ('[%(asctime)s] - %(levelname)8s - ' - '%(name)14s - ' - '%(filename)16s:%(lineno)-4d - ' - '%(message)s') + fmt = "[%(asctime)s] - %(levelname)8s - " "%(name)14s - " "%(filename)16s:%(lineno)-4d - " "%(message)s" formatter = logging.Formatter(fmt) ch.setFormatter(formatter) logger.addHandler(ch) return logger -log = makeSimpleLogger('quantdb') -logd = log.getChild('data') +log = makeSimpleLogger("quantdb") +logd = log.getChild("data") # from pyontutils.utils_fast import setPS1 def setPS1(script__file__): try: - text = 'Running ' + os.path.basename(script__file__) - os.sys.stdout.write('\x1b]2;{}\x07\n'.format(text)) + text = "Running " + os.path.basename(script__file__) + os.sys.stdout.write("\x1b]2;{}\x07\n".format(text)) except AttributeError as e: log.exception(e) def dbUri(dbuser, host, port, database): - if hasattr(sys, 'pypy_version_info'): - dialect = 'psycopg2cffi' + if hasattr(sys, "pypy_version_info"): + dialect = "psycopg2cffi" else: - dialect = 'psycopg2' - return f'postgresql+{dialect}://{dbuser}@{host}:{port}/{database}' + dialect = "psycopg2" + return f"postgresql+{dialect}://{dbuser}@{host}:{port}/{database}" # from pyontutils.utils_fast import isoformat -def isoformat(datetime_instance, timespec='auto'): +def isoformat(datetime_instance, timespec="auto"): kwargs = {} if isinstance(datetime_instance, datetime): # don't pass timespec if type is not date not datetime - kwargs['timespec'] = timespec - - return (datetime_instance - .isoformat(**kwargs) - .replace('.', ',') - .replace('+00:00', 'Z')) + kwargs["timespec"] = timespec + return datetime_instance.isoformat(**kwargs).replace(".", ",").replace("+00:00", "Z") diff --git a/sql/postgres.sql b/sql/postgres.sql index dbbe6ce..f556ff3 100644 --- a/sql/postgres.sql +++ b/sql/postgres.sql @@ -35,4 +35,3 @@ ALTER ROLE "quantdb-test-admin" SET search_path = quantdb, public; ALTER ROLE "quantdb-test-user" SET search_path = quantdb, public; ALTER ROLE "quantdb-admin" SET search_path = quantdb, public; ALTER ROLE "quantdb-user" SET search_path = quantdb, public; - diff --git a/test/test_api.py b/test/test_api.py index 88ef2bf..f1a97a9 100644 --- a/test/test_api.py +++ b/test/test_api.py @@ -1,6 +1,8 @@ import json import pprint + from flask_sqlalchemy import SQLAlchemy + from quantdb.api import make_app from quantdb.utils import log @@ -11,99 +13,80 @@ def test(): client = app.test_client() runner = app.test_cli_runner() - dataset_uuid = 'aa43eda8-b29a-4c25-9840-ecbd57598afc' - some_object = '414886a9-9ec7-447e-b4d8-3ae42fda93b7' # XXX FAKE - actual_package_uuid = '15bcbcd5-b054-40ef-9b5c-6a260d441621' - base = 'http://localhost:8989/api/1/' + dataset_uuid = "aa43eda8-b29a-4c25-9840-ecbd57598afc" + some_object = "414886a9-9ec7-447e-b4d8-3ae42fda93b7" # XXX FAKE + actual_package_uuid = "15bcbcd5-b054-40ef-9b5c-6a260d441621" + base = "http://localhost:8989/api/1/" urls = ( - f'{base}values/inst', - f'{base}values/inst?dataset={dataset_uuid}', - f'{base}values/inst?dataset={dataset_uuid}&union-cat-quant=true', - f'{base}values/inst?dataset={dataset_uuid}&aspect=distance&aspect=time', - f'{base}values/inst?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5', - f'{base}values/inst?desc-inst=nerve-volume', - - f'{base}objects?dataset={dataset_uuid}', - f'{base}objects?dataset={dataset_uuid}&aspect=distance', - f'{base}objects?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5', # expect nothing - f'{base}objects?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5&union-cat-quant=true', - - f'{base}objects?dataset={dataset_uuid}&subject=sub-f001', - f'{base}objects?subject=sub-f001', - f'{base}objects?subject=sub-f001&union-cat-quant=true', - f'{base}objects?subject=sub-f001&subject=sub-f002&subject=sub-f003&subject=sub-f004&subject=sub-f005', - f'{base}objects?subject=sub-f001&subject=sub-f002&subject=sub-f003&subject=sub-f004&subject=sub-f005&union-cat-quant=true', - f'{base}objects?subject=sub-f001&desc-cat=none&value-quant-min=0.5&union-cat-quant=true', - f'{base}objects?subject=sub-f001&desc-cat=none&aspect=distance&value-quant-min=0.5&union-cat-quant=true', - f'{base}objects?subject=sub-f001&aspect=distance&value-quant-min=0.5&union-cat-quant=true', - f'{base}objects?aspect=distance&value-quant-min=0.5&union-cat-quant=true', - f'{base}objects?desc-cat=none&aspect=distance&value-quant-min=0.5&union-cat-quant=true', - f'{base}objects?desc-cat=none&aspect=distance&value-quant-min=0.5', - f'{base}objects?aspect=distance&value-quant-min=0.5', - f'{base}objects?aspect=distance&value-quant-min=0.5&source-only=true', - f'{base}objects?desc-inst=nerve-volume&aspect=distance&value-quant-min=0.5&source-only=true', - + f"{base}values/inst", + f"{base}values/inst?dataset={dataset_uuid}", + f"{base}values/inst?dataset={dataset_uuid}&union-cat-quant=true", + f"{base}values/inst?dataset={dataset_uuid}&aspect=distance&aspect=time", + f"{base}values/inst?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5", + f"{base}values/inst?desc-inst=nerve-volume", + f"{base}objects?dataset={dataset_uuid}", + f"{base}objects?dataset={dataset_uuid}&aspect=distance", + f"{base}objects?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5", # expect nothing + f"{base}objects?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5&union-cat-quant=true", + f"{base}objects?dataset={dataset_uuid}&subject=sub-f001", + f"{base}objects?subject=sub-f001", + f"{base}objects?subject=sub-f001&union-cat-quant=true", + f"{base}objects?subject=sub-f001&subject=sub-f002&subject=sub-f003&subject=sub-f004&subject=sub-f005", + f"{base}objects?subject=sub-f001&subject=sub-f002&subject=sub-f003&subject=sub-f004&subject=sub-f005&union-cat-quant=true", + f"{base}objects?subject=sub-f001&desc-cat=none&value-quant-min=0.5&union-cat-quant=true", + f"{base}objects?subject=sub-f001&desc-cat=none&aspect=distance&value-quant-min=0.5&union-cat-quant=true", + f"{base}objects?subject=sub-f001&aspect=distance&value-quant-min=0.5&union-cat-quant=true", + f"{base}objects?aspect=distance&value-quant-min=0.5&union-cat-quant=true", + f"{base}objects?desc-cat=none&aspect=distance&value-quant-min=0.5&union-cat-quant=true", + f"{base}objects?desc-cat=none&aspect=distance&value-quant-min=0.5", + f"{base}objects?aspect=distance&value-quant-min=0.5", + f"{base}objects?aspect=distance&value-quant-min=0.5&source-only=true", + f"{base}objects?desc-inst=nerve-volume&aspect=distance&value-quant-min=0.5&source-only=true", # values-quant - f'{base}values/quant?dataset={dataset_uuid}&aspect=distance', - f'{base}values/quant?object={actual_package_uuid}&aspect=distance', - f'{base}values/quant?aspect=distance', - f'{base}values/quant?aspect=distance-via-reva-ft-sample-id-normalized-v1', - + f"{base}values/quant?dataset={dataset_uuid}&aspect=distance", + f"{base}values/quant?object={actual_package_uuid}&aspect=distance", + f"{base}values/quant?aspect=distance", + f"{base}values/quant?aspect=distance-via-reva-ft-sample-id-normalized-v1", # values-cat - f'{base}values/cat?object={actual_package_uuid}', - f'{base}values/cat?object={actual_package_uuid}&union-cat-quant=true', # shouldn't need it in this case - - f'{base}values/cat-quant?object={actual_package_uuid}', - f'{base}values/cat-quant?object={actual_package_uuid}&union-cat-quant=true', - + f"{base}values/cat?object={actual_package_uuid}", + f"{base}values/cat?object={actual_package_uuid}&union-cat-quant=true", # shouldn't need it in this case + f"{base}values/cat-quant?object={actual_package_uuid}", + f"{base}values/cat-quant?object={actual_package_uuid}&union-cat-quant=true", # values-cat-quant - f'{base}values?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5', - f'{base}values?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5&union-cat-quant=true', - - f'{base}values?object={actual_package_uuid}', - f'{base}values?object={actual_package_uuid}&union-cat-quant=true', - - f'{base}values/inst?object={actual_package_uuid}', - f'{base}values/inst?object={actual_package_uuid}&union-cat-quant=true', - + f"{base}values?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5", + f"{base}values?dataset={dataset_uuid}&aspect=distance&value-quant-min=0.5&union-cat-quant=true", + f"{base}values?object={actual_package_uuid}", + f"{base}values?object={actual_package_uuid}&union-cat-quant=true", + f"{base}values/inst?object={actual_package_uuid}", + f"{base}values/inst?object={actual_package_uuid}&union-cat-quant=true", # prov - f'{base}values/inst?prov=true', - - f'{base}values/quant?aspect=distance&prov=true', - - f'{base}values/cat?object={actual_package_uuid}', - f'{base}values/cat?object={actual_package_uuid}&prov=true', # FIXME somehow this has a 3x increase in records, and non-distinct - - f'{base}values/cat-quant?object={actual_package_uuid}&union-cat-quant=true', - f'{base}values/cat-quant?object={actual_package_uuid}&union-cat-quant=true&prov=true', - - f'{base}values/cat-quant', - f'{base}values/cat-quant?prov=true', - - f'{base}values/cat-quant?union-cat-quant=true', - f'{base}values/cat-quant?union-cat-quant=true&prov=true', - + f"{base}values/inst?prov=true", + f"{base}values/quant?aspect=distance&prov=true", + f"{base}values/cat?object={actual_package_uuid}", + f"{base}values/cat?object={actual_package_uuid}&prov=true", # FIXME somehow this has a 3x increase in records, and non-distinct + f"{base}values/cat-quant?object={actual_package_uuid}&union-cat-quant=true", + f"{base}values/cat-quant?object={actual_package_uuid}&union-cat-quant=true&prov=true", + f"{base}values/cat-quant", + f"{base}values/cat-quant?prov=true", + f"{base}values/cat-quant?union-cat-quant=true", + f"{base}values/cat-quant?union-cat-quant=true&prov=true", # desc - f'{base}desc/inst', - f'{base}desc/cat', - f'{base}desc/quant', - - f'{base}desc/inst?include-unused=true', - f'{base}desc/cat?include-unused=true', - f'{base}desc/quant?include-unused=true', - + f"{base}desc/inst", + f"{base}desc/cat", + f"{base}desc/quant", + f"{base}desc/inst?include-unused=true", + f"{base}desc/cat?include-unused=true", + f"{base}desc/quant?include-unused=true", # descriptor values - f'{base}terms', - f'{base}aspects', - f'{base}units', - - f'{base}terms?include-unused=true', - f'{base}aspects?include-unused=true', - f'{base}units?include-unused=true', + f"{base}terms", + f"{base}aspects", + f"{base}units", + f"{base}terms?include-unused=true", + f"{base}aspects?include-unused=true", + f"{base}units?include-unused=true", # TODO maybe shapes here as well? - ) - #log.setLevel(9) + # log.setLevel(9) resps = [] for url in urls: log.debug(url) @@ -115,8 +98,7 @@ def test(): pprint.pprint(resps, width=120) # (i := 6, resps[i], urls[i]) - #q = client.get(f'{base}values/quant?dataset={dataset_uuid}&aspect=distance&return-query=true').data.decode() - #q = client.get(f'{base}values/cat?object={actual_package_uuid}&prov=true&return-query=true').data.decode() - #print(q) + # q = client.get(f'{base}values/quant?dataset={dataset_uuid}&aspect=distance&return-query=true').data.decode() + # q = client.get(f'{base}values/cat?object={actual_package_uuid}&prov=true&return-query=true').data.decode() + # print(q) breakpoint() - From d2df004fb7c6e202a805a365cdb2765332b06593 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Tue, 25 Jun 2024 19:51:13 +0000 Subject: [PATCH 09/13] pydantic not needed --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a216a3b..40438fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] dependencies = [ "sqlalchemy ~= 2.0.29", - "pydantic ~= 2.6.4", + # "pydantic ~= 2.6.4", "fastapi ~= 0.110.1", "uvicorn ~= 0.29.0", "pydantic-settings ~= 2.2.1", From a6c144df8f637428212882e6f44f228e327c4dca Mon Sep 17 00:00:00 2001 From: Troy Sincomb Date: Mon, 28 Apr 2025 13:38:25 -0700 Subject: [PATCH 10/13] pre-commit try --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ce8cf5..2fa1306 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,6 @@ repos: exclude: | (?x)^( quantdb/api.py| - quantdb/ingest.py| + quantdb/ingest.py|ls sql;/.* )$ From dc0776f7e5e7a2f06fceb0e384969b76bfac6ebc Mon Sep 17 00:00:00 2001 From: Troy Sincomb Date: Mon, 28 Apr 2025 13:40:24 -0700 Subject: [PATCH 11/13] accepted all incoming changes for hangnail merge --- .pre-commit-config.yaml | 2 +- quantdb/ingest.py | 360 ---------------------------------------- 2 files changed, 1 insertion(+), 361 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2fa1306..6ce8cf5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,6 @@ repos: exclude: | (?x)^( quantdb/api.py| - quantdb/ingest.py|ls + quantdb/ingest.py| sql;/.* )$ diff --git a/quantdb/ingest.py b/quantdb/ingest.py index 6c52f19..b3f514d 100644 --- a/quantdb/ingest.py +++ b/quantdb/ingest.py @@ -5,14 +5,8 @@ import requests from sparcur import objects as sparcur_objects # register pathmeta type - -<<<<<<< HEAD -# FIXME sparcur dependencies, or keep ingest separate -======= from sparcur.paths import Path from sparcur.utils import PennsieveId as RemoteId - ->>>>>>> master from sparcur.utils import fromJson from sqlalchemy import create_engine from sqlalchemy.dialects.postgresql import JSONB @@ -22,20 +16,12 @@ from quantdb.utils import dbUri, isoformat, log -<<<<<<< HEAD -======= - # FIXME sparcur dependencies, or keep ingest separate ->>>>>>> master ######### start database interaction section -<<<<<<< HEAD -log = log.getChild("ingest") -======= log = log.getChild('ingest') ->>>>>>> master try: if get_ipython().__class__.__name__ == "ZMQInteractiveShell": @@ -115,13 +101,8 @@ def makeParamsValues(*value_sets, constants=tuple(), types=tuple(), row_types=tu else: proto_params = [(tuple(getname(value) for value in row), row) for row in values] -<<<<<<< HEAD - values_template = ", ".join( - "(" + ", ".join(constants + tuple(":" + name for name in names)) + ")" for names, _ in proto_params -======= values_template = ', '.join( '(' + ', '.join(constants + tuple(':' + name for name in names)) + ')' for names, _ in proto_params ->>>>>>> master ) yield values_template if row_types: @@ -157,19 +138,11 @@ def makeParamsValues(*value_sets, constants=tuple(), types=tuple(), row_types=tu a abdominal """ sam_ordering = { -<<<<<<< HEAD - "l": 0, # left - "r": 0, # right - "c": 0, # cardiac safe to keep at zero since the c index usually come after t - "a": 1, # anterior abdominal - "p": 1, # posterior abdominal -======= 'l': 0, # left 'r': 0, # right 'c': 1, # central +cardiac safe to keep at zero since the c index usually come after t+ FIXME means central so it is where both sides merge so have to redo the ordering which will force a v2 'a': 2, # anterior 'p': 2, # posterior ->>>>>>> master } seg_ordering = { "c": 0, # cervical @@ -192,11 +165,7 @@ def anat_index(sample): seg_ind = v break else: -<<<<<<< HEAD - if sam_id == "c": -======= if sam_id == 'c': ->>>>>>> master # print('c sample', sample) # rest = int(''.join(_ for _ in seg_id if _.isdigit())) rest = int(seg_id[:-1]) @@ -219,18 +188,6 @@ def pps(path_structure): if len(path_structure) == 6: # FIXME utter hack top, subject, sam_1, segment, modality, file = path_structure -<<<<<<< HEAD - p1 = sam_1, subject # child, parent to match db convention wasDerivedFrom - p2 = segment, sam_1 - return { - "parents": (p1, p2), - "subject": subject, - "sample": segment, - "modality": modality, - # note that because we do not convert to a single value we cannot include raw_anat_index in the qdb but that's ok - "raw_anat_index_v1": anat_index(segment), - } -======= elif len(path_structure) == 5: top, subject, sam_1, segment, file = path_structure modality = None # FIXME from metadata sample id @@ -238,7 +195,6 @@ def pps(path_structure): modality = 'microct' else: raise NotImplementedError(path_structure) ->>>>>>> master else: raise NotImplementedError(path_structure) @@ -273,19 +229,6 @@ def pps123(path_structure): def ext_pmeta(j, _pps=pps): out = {} -<<<<<<< HEAD - out["dataset"] = j["dataset_id"] - out["object"] = j["remote_id"] - out["file_id"] = ( - j["file_id"] if "file_id" in j else int(j["uri_api"].rsplit("/")[-1]) - ) # XXX old pathmeta schema that didn't include file id - ps = pathlib.Path(j["dataset_relative_path"]).parts - [p for p in ps if p.startswith("sub-") or p.startswith("sam-")] - out.update(pps(ps)) - return out - - -======= out['dataset'] = j['dataset_id'] out['object'] = j['remote_id'] out['file_id'] = ( @@ -301,52 +244,12 @@ def ext_pmeta123(j): return ext_pmeta(j, _pps=pps123) ->>>>>>> master class Queries: def __init__(self, session): self.session = session def address_from_fadd_type_fadd(self, fadd_type, fadd): # FIXME multi etc. -<<<<<<< HEAD - res = [ - i - for i, in self.session.execute( - sql_text("select * from address_from_fadd_type_fadd(:fadd_type, :fadd)"), - dict(fadd_type=fadd_type, fadd=fadd), - ) - ] - if res: - return res[0] - - def desc_inst_from_label(self, label): - # FIXME multi etc. - res = [ - i for i, in self.session.execute(sql_text("select * from desc_inst_from_label(:label)"), dict(label=label)) - ] - if res: - return res[0] - - def desc_quant_from_label(self, label): - # FIXME multi etc. - res = [ - i for i, in self.session.execute(sql_text("select * from desc_quant_from_label(:label)"), dict(label=label)) - ] - if res: - return res[0] - - def desc_cat_from_label_domain_label(self, label, domain_label): - # FIXME multi etc. - res = [ - i - for i, in self.session.execute( - sql_text("select * from desc_cat_from_label_domain_label(:label, :domain_label)"), - dict(label=label, domain_label=domain_label), - ) - ] - if res: - return res[0] -======= params = dict(fadd_type=fadd_type, fadd=fadd) res = [ i @@ -398,7 +301,6 @@ def desc_cat_from_label_domain_label(self, label, domain_label): raise ValueError(f'needed a result here {params}') else: return out ->>>>>>> master def cterm_from_label(self, label): # FIXME multi etc. @@ -411,12 +313,6 @@ def cterm_from_label(self, label): else: return out -<<<<<<< HEAD - def insts_from_dataset_ids(self, dataset, ids): - return list( - self.session.execute( - sql_text("select * from insts_from_dataset_ids(:dataset, :ids)"), dict(dataset=dataset, ids=ids) -======= def insts_from_dataset(self, dataset): return list(self.session.execute(sql_text('select * from insts_from_dataset(:dataset)'), dict(dataset=dataset))) @@ -424,7 +320,6 @@ def insts_from_dataset_ids(self, dataset, ids): return list( self.session.execute( sql_text('select * from insts_from_dataset_ids(:dataset, :ids)'), dict(dataset=dataset, ids=ids) ->>>>>>> master ) ) @@ -433,19 +328,6 @@ class InternalIds: def __init__(self, queries): q = queries self._q = queries -<<<<<<< HEAD - - self.addr_suid = q.address_from_fadd_type_fadd("tabular-header", "id_sub") - self.addr_said = q.address_from_fadd_type_fadd("tabular-header", "id_sam") - self.addr_spec = q.address_from_fadd_type_fadd("tabular-header", "species") - self.addr_saty = q.address_from_fadd_type_fadd("tabular-header", "sample_type") - - self.addr_tmod = q.address_from_fadd_type_fadd("tabular-header", "modality") - # addr_trai = address_from_fadd_type_fadd('tabular-header', 'raw_anat_index') - # addr_tnai = address_from_fadd_type_fadd('tabular-header', 'norm_anat_index') - # addr_context = address_from_fadd_type_fadd('context', '#/path-metadata/{index of match remote_id}/dataset_relative_path') # XXX this doesn't do what we want, I think what we really would want in these contexts are objects_internal that reference the file system state for a given updated snapshot, that is the real "object" that corresponds to the path-metadata.json that we are working from - -======= self.addr_suid = q.address_from_fadd_type_fadd('tabular-header', 'id_sub') self.addr_said = q.address_from_fadd_type_fadd('tabular-header', 'id_sam') @@ -472,40 +354,16 @@ def __init__(self, queries): # addr_tnai = address_from_fadd_type_fadd('tabular-header', 'norm_anat_index') # addr_context = address_from_fadd_type_fadd('context', '#/path-metadata/{index of match remote_id}/dataset_relative_path') # XXX this doesn't do what we want, I think what we really would want in these contexts are objects_internal that reference the file system state for a given updated snapshot, that is the real "object" that corresponds to the path-metadata.json that we are working from ->>>>>>> master # addr_jpmod = address_from_fadd_type_fadd('json-path-with-types', '#/#int/modality') # addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/#int/anat_index') # addr_jpnai = address_from_fadd_type_fadd('json-path-with-types', '#/#int/norm_anat_index') self.addr_jpdrp = q.address_from_fadd_type_fadd( -<<<<<<< HEAD - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path" -======= 'json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path' ->>>>>>> master ) # XXX these are more accurate if opaque self.addr_jpmod = q.address_from_fadd_type_fadd( -<<<<<<< HEAD - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-modality" - ) - # addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-raw-anat-index') - self.addr_jpnai = q.address_from_fadd_type_fadd( - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1" - ) - self.addr_jpnain = q.address_from_fadd_type_fadd( - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1-min" - ) - self.addr_jpnaix = q.address_from_fadd_type_fadd( - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-norm-anat-index-v1-max" - ) - self.addr_jpsuid = q.address_from_fadd_type_fadd( - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-subject-id" - ) - self.addr_jpsaid = q.address_from_fadd_type_fadd( - "json-path-with-types", "#/path-metadata/data/#int/dataset_relative_path#derive-sample-id" -======= 'json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-modality' ) # addr_jprai = address_from_fadd_type_fadd('json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-raw-anat-index') @@ -532,7 +390,6 @@ def __init__(self, queries): ) self.addr_jpsaid = q.address_from_fadd_type_fadd( 'json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-sample-id' ->>>>>>> master ) self.addr_jpspec = q.address_from_fadd_type_fadd("json-path-with-types", "#/local/tom-made-it-up/species") @@ -547,25 +404,6 @@ def __init__(self, queries): self.addr_const_null = q.address_from_fadd_type_fadd("constant", None) -<<<<<<< HEAD - # qd_rai = desc_quant_from_label('reva ft sample anatomical location distance index raw') - self.qd_nai = q.desc_quant_from_label("reva ft sample anatomical location distance index normalized v1") - self.qd_nain = q.desc_quant_from_label("reva ft sample anatomical location distance index normalized v1 min") - self.qd_naix = q.desc_quant_from_label("reva ft sample anatomical location distance index normalized v1 max") - - self.cd_mod = q.desc_cat_from_label_domain_label("hasDataAboutItModality", None) - self.cd_bot = q.desc_cat_from_label_domain_label( - "bottom", None - ) # we just need something we can reference that points to null so we can have a refernce to all the objects - - self.id_human = q.desc_inst_from_label("human") - self.id_nerve = q.desc_inst_from_label("nerve") - self.id_nerve_volume = q.desc_inst_from_label("nerve-volume") - self.luid = { - "human": self.id_human, - "nerve": self.id_nerve, - "nerve-volume": self.id_nerve_volume, -======= self.qd_nvlai1 = q.desc_quant_from_label('vagus level anatomical location distance index normalized v1') self.qd_nvlain1 = q.desc_quant_from_label('vagus level anatomical location distance index normalized v1 min') self.qd_nvlaix1 = q.desc_quant_from_label('vagus level anatomical location distance index normalized v1 max') @@ -600,7 +438,6 @@ def __init__(self, queries): 'nerve-volume': self.id_nerve_volume, 'nerve-cross-section': self.id_nerve_cross_section, 'fascicle-cross-section': self.id_fascicle_cross_section, ->>>>>>> master } self.ct_mod = q.cterm_from_label("microct") # lol ct ct @@ -616,11 +453,7 @@ class Inserts: pass -<<<<<<< HEAD -def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_args=None): -======= def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_args=None, **kwargs): ->>>>>>> master """generic ingest workflow this_dataset_updated_uuid might not be needed in future, add a kwarg to control it maybe? @@ -643,11 +476,7 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a make_values_cat, make_values_quant, ) = ( -<<<<<<< HEAD - extract_fun(dataset_uuid) if values_args is None else values_args -======= extract_fun(dataset_uuid, **kwargs) if values_args is None else values_args ->>>>>>> master ) q = Queries(session) @@ -657,23 +486,6 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a values_instances = make_values_instances(i) res0 = session.execute( -<<<<<<< HEAD - sql_text("INSERT INTO objects (id, id_type) VALUES (:id, :id_type) ON CONFLICT DO NOTHING"), - dict(id=dataset_uuid, id_type="dataset"), - ) - - # oh dear https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql - res1 = session.execute( - sql_text( - "WITH ins AS (INSERT INTO objects_internal (type, dataset, updated_transitive, label) VALUES ('path-metadata', :dataset, :updated_transitive, :label) ON CONFLICT DO NOTHING RETURNING id) SELECT id FROM ins UNION ALL SELECT id FROM objects_internal WHERE type = 'path-metadata' AND dataset = :dataset AND updated_transitive = :updated_transitive" - ), # TODO see whether we actually need union all here or whether union by itself is sufficient - dict( - dataset=dataset_uuid, - updated_transitive=updated_transitive, - label=f"test-load-for-f001 {isoformat(updated_transitive)}", - ), - ) -======= sql_text('INSERT INTO objects (id, id_type) VALUES (:id, :id_type) ON CONFLICT DO NOTHING'), dict(id=dataset_uuid, id_type='dataset'), ) @@ -697,20 +509,11 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a this_dataset_updated_uuid = [_ for _, in res1][0] else: this_dataset_updated_uuid = None ->>>>>>> master void = make_void(this_dataset_updated_uuid, i) vocd = make_vocd(this_dataset_updated_uuid, i) voqd = make_voqd(this_dataset_updated_uuid, i) -<<<<<<< HEAD - res1_1 = session.execute( - sql_text( - "INSERT INTO objects (id, id_type, id_internal) VALUES (:id, :id_type, :id) ON CONFLICT DO NOTHING" - ), # FIXME bad ocdn here - dict(id=this_dataset_updated_uuid, id_type="quantdb"), - ) -======= if updated_transitive: res1_1 = session.execute( sql_text( @@ -718,7 +521,6 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a ), # FIXME bad ocdn here dict(id=this_dataset_updated_uuid, id_type='quantdb'), ) ->>>>>>> master vt, params = makeParamsValues(values_objects) session.execute(sql_text(f"INSERT INTO objects (id, id_type, id_file) VALUES {vt}{ocdn}"), params) @@ -728,11 +530,7 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a vt, params = makeParamsValues(values_instances) session.execute( -<<<<<<< HEAD - sql_text(f"INSERT INTO values_inst (dataset, id_formal, type, desc_inst, id_sub, id_sam) VALUES {vt}{ocdn}"), -======= sql_text(f'INSERT INTO values_inst (dataset, id_formal, type, desc_inst, id_sub, id_sam) VALUES {vt}{ocdn}'), ->>>>>>> master params, ) @@ -751,38 +549,6 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a vt, params = makeParamsValues(void) session.execute( -<<<<<<< HEAD - sql_text(f"INSERT INTO obj_desc_inst (object, desc_inst, addr_field, addr_desc_inst) VALUES {vt}{ocdn}"), params - ) - - vt, params = makeParamsValues(vocd) - session.execute(sql_text(f"INSERT INTO obj_desc_cat (object, desc_cat, addr_field) VALUES {vt}{ocdn}"), params) - - vt, params = makeParamsValues(voqd) - session.execute(sql_text(f"INSERT INTO obj_desc_quant (object, desc_quant, addr_field) VALUES {vt}{ocdn}"), params) - - vt, params = makeParamsValues(values_cv) - session.execute( - sql_text( - f"INSERT INTO values_cat (value_open, value_controlled, object, desc_inst, desc_cat, instance) VALUES {vt}{ocdn}" - ), - params, - ) - - vt, params, bindparams = makeParamsValues( - # FIXME LOL the types spec here is atrocious ... but it does work ... - # XXX and barring the unfortunate case, which we have now encountered where - # now fixed in the local impl - values_qv, - row_types=(None, None, None, None, None, JSONB), - ) - - t = sql_text( - f"INSERT INTO values_quant (value, object, desc_inst, desc_quant, instance, value_blob) VALUES {vt}{ocdn}" - ) - tin = t.bindparams(*bindparams) - session.execute(tin, params) -======= sql_text(f'INSERT INTO obj_desc_inst (object, desc_inst, addr_field, addr_desc_inst) VALUES {vt}{ocdn}'), params ) @@ -819,7 +585,6 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a ) tin = t.bindparams(*bindparams) session.execute(tin, params) ->>>>>>> master if commit: session.commit() @@ -829,15 +594,9 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): if source_local: with open( pathlib.Path( -<<<<<<< HEAD - f"~/.local/share/sparcur/export/datasets/{dataset_uuid}/LATEST/path-metadata.json" - ).expanduser(), - "rt", -======= f'~/.local/share/sparcur/export/datasets/{dataset_uuid}/LATEST/path-metadata.json' ).expanduser(), 'rt', ->>>>>>> master ) as f: blob = json.load(f) @@ -855,49 +614,29 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): ir = fromJson(blob) -<<<<<<< HEAD - updated_transitive = max([i["timestamp_updated"] for i in ir["data"][1:]]) # 1: to skip the dataset object itself - - jpx = [r for r in ir["data"] if "mimetype" in r and r["mimetype"] == "image/jpx"] - - exts = [ext(j) for j in jpx] -======= updated_transitive = max([i['timestamp_updated'] for i in ir['data'][1:]]) # 1: to skip the dataset object itself jpx = [r for r in ir['data'] if 'mimetype' in r and r['mimetype'] == 'image/jpx'] exts = [ext_pmeta(j) for j in jpx] ->>>>>>> master # hrm = sorted(exts, key=lambda j: j['raw_anat_index']) # max_rai = max([e['raw_anat_index'] for e in exts]) # import math # log_max_rai = math.log10(max_rai) # normalize the index by mapping distinct values to the integers -<<<<<<< HEAD - nondist = sorted([e["raw_anat_index_v1"] for e in exts]) - lin_distinct = {v: i for i, v in enumerate(sorted(set([e["raw_anat_index_v1"] for e in exts])))} -======= nondist = sorted([e['raw_anat_index_v2'] for e in exts]) lin_distinct = {v: i for i, v in enumerate(sorted(set([e['raw_anat_index_v2'] for e in exts])))} ->>>>>>> master max_distinct = len(lin_distinct) mdp1 = max_distinct + 0.1 # to simplify adding overlap dd = defaultdict(list) for e in exts: # e['norm_anat_index'] = math.log10(e['raw_anat_index']) / log_max_rai -<<<<<<< HEAD - pos = lin_distinct[e["raw_anat_index_v1"]] - e["norm_anat_index_v1"] = (pos + 0.55) / mdp1 - e["norm_anat_index_v1_min"] = pos / mdp1 - e["norm_anat_index_v1_max"] = ( -======= pos = lin_distinct[e['raw_anat_index_v2']] e['norm_anat_index_v2'] = (pos + 0.55) / mdp1 e['norm_anat_index_v2_min'] = pos / mdp1 e['norm_anat_index_v2_max'] = ( ->>>>>>> master pos + 1.1 ) / mdp1 # ensure there is overlap between section for purposes of testing # TODO norm_anat_index_min @@ -905,15 +644,9 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): dd[e["dataset"], e["sample"]].append(e) inst_obj_index = dict(dd) -<<<<<<< HEAD - max_nai = max([e["norm_anat_index_v1"] for e in exts]) - min_nain = min([e["norm_anat_index_v1_min"] for e in exts]) - max_naix = max([e["norm_anat_index_v1_max"] for e in exts]) -======= max_nai = max([e['norm_anat_index_v2'] for e in exts]) min_nain = min([e['norm_anat_index_v2_min'] for e in exts]) max_naix = max([e['norm_anat_index_v2_max'] for e in exts]) ->>>>>>> master if visualize: mexts = [] @@ -927,16 +660,10 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): exts = mexts x = list(range(len(exts))) # ry = sorted([e['raw_anat_index'] for e in exts]) -<<<<<<< HEAD - ny = sorted([e["norm_anat_index_v1"] for e in exts]) - nyn = sorted([e["norm_anat_index_v1_min"] for e in exts]) - nyx = sorted([e["norm_anat_index_v1_max"] for e in exts]) -======= idy = [b for a, b in sorted([(e['norm_anat_index_v2'], e['sample']) for e in exts])] ny = sorted([e['norm_anat_index_v2'] for e in exts]) nyn = sorted([e['norm_anat_index_v2_min'] for e in exts]) nyx = sorted([e['norm_anat_index_v2_max'] for e in exts]) ->>>>>>> master nnx = list(zip(nyn, nyx)) import pylab as plt import seaborn @@ -960,42 +687,6 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): plt.savefig(f'ft-norm-anat-index-v2-{_sid}.png') exts = _exts -<<<<<<< HEAD - datasets = {i.uuid: {"id_type": i.type} for e in exts if (i := e["dataset"])} - - packages = { - i.uuid: { - "id_type": i.type, - "id_file": e["file_id"], - } - for e in exts - if (i := e["object"]) - } - - objects = {**datasets, **packages} - dataset_object = list(set((d.uuid, o.uuid) for e in exts if (d := e["dataset"]) and (o := e["object"]))) - - subjects = { - k: { - "type": "subject", - "desc_inst": "human", - "id_sub": k[1], - } - for k in sorted(set((e["dataset"], e["subject"]) for e in exts)) - } - segments = { - k[:2]: { - "type": "sample", # FIXME vs below ??? - "desc_inst": "nerve-volume", # FIXME should this be nerve-segment and then we use nerve-volume for the 1:1 with files? - "id_sub": k[-1], - "id_sam": k[1], - } - for k in sorted(set((e["dataset"], e["sample"], e["subject"]) for e in exts)) - } - parents = sorted(set((e["dataset"],) + p for e in exts for p in e["parents"])) - sam_other = { - p[:2]: {"type": "sample", "desc_inst": "nerve", "id_sub": p[-1], "id_sam": p[1]} -======= datasets = {i.uuid: {'id_type': i.type} for e in exts if (i := e['dataset'])} packages = { @@ -1030,7 +721,6 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): parents = sorted(set((e['dataset'],) + p for e in exts for p in e['parents'])) sam_other = { p[:2]: {'type': 'sample', 'desc_inst': 'nerve', 'id_sub': p[-1], 'id_sam': p[1]} ->>>>>>> master for p in parents if p[:2] not in segments } @@ -1049,17 +739,10 @@ def make_values_instances(i): ( d.uuid, f, -<<<<<<< HEAD - inst["type"], - i.luid[inst["desc_inst"]], - inst["id_sub"] if "id_sub" in inst else None, - inst["id_sam"] if "id_sam" in inst else None, -======= inst['type'], i.luid[inst['desc_inst']], inst['id_sub'] if 'id_sub' in inst else None, inst['id_sam'] if 'id_sam' in inst else None, ->>>>>>> master ) for (d, f), inst in instances.items() ] @@ -1096,11 +779,7 @@ def make_void(this_dataset_updated_uuid, i): None, ) # XXX FIXME this is the only way I can think to do this right now ? for o, b in objects.items() -<<<<<<< HEAD - if b["id_type"] == "package" -======= if b['id_type'] == 'package' ->>>>>>> master ] return void @@ -1111,15 +790,9 @@ def make_vocd(this_dataset_updated_uuid, i): # figuring out how to turn that around is going to take a bit of thinking (this_dataset_updated_uuid, i.cd_mod, i.addr_jpmod), ] + [ -<<<<<<< HEAD - (o, i.cd_bot, i.addr_const_null) # XXX FIXME this is the only way I can think to do this right now ? - for o, b in objects.items() - if b["id_type"] == "package" -======= (o, i.cd_obj, i.addr_const_null) # XXX FIXME this is the only way I can think to do this right now ? for o, b in objects.items() if b['id_type'] == 'package' ->>>>>>> master ] return vocd @@ -1148,32 +821,18 @@ def make_values_cat(this_dataset_updated_uuid, i, luinst): # e['object'].uuid, # FIXME still not right this comes from the updated latest i.id_nerve_volume, cd, # if we mess this up the fk ok obj_desc_cat will catch it :) -<<<<<<< HEAD - luinst[e["dataset"].uuid, e["sample"]], # get us the instance - ) - for e in exts - for k, cd in (("modality", i.cd_mod),) -======= luinst[e['dataset'].uuid, e['sample']], # get us the instance ) for e in exts for k, cd in (('modality', i.cd_mod),) ->>>>>>> master ] + [ ( None, i.ct_hack, -<<<<<<< HEAD - e["object"].uuid, - i.id_nerve_volume, - i.cd_bot, # if we mess this up the fk ok obj_desc_cat will catch it :) - luinst[e["dataset"].uuid, e["sample"]], # get us the instance -======= e['object'].uuid, i.id_nerve_volume, i.cd_obj, # if we mess this up the fk ok obj_desc_cat will catch it :) luinst[e['dataset'].uuid, e['sample']], # get us the instance ->>>>>>> master ) for e in exts ] @@ -1188,25 +847,15 @@ def make_values_quant(this_dataset_updated_uuid, i, luinst): this_dataset_updated_uuid, i.id_nerve_volume, qd, # if we mess this up the fk ok obj_desc_cat will catch it :) -<<<<<<< HEAD - luinst[e["dataset"].uuid, e["sample"]], # get us the instance -======= luinst[e['dataset'].uuid, e['sample']], # get us the instance ->>>>>>> master e[k], ) for e in exts for k, qd in ( # ('raw_anat_index', qd_rai), # XXX this is a bad place to store object -> field -> qd mappings also risks mismatch on address -<<<<<<< HEAD - ("norm_anat_index_v1", i.qd_nai), - ("norm_anat_index_v1_min", i.qd_nain), - ("norm_anat_index_v1_max", i.qd_naix), -======= ('norm_anat_index_v2', i.qd_nai), ('norm_anat_index_v2_min', i.qd_nain), ('norm_anat_index_v2_max', i.qd_naix), ->>>>>>> master ) ] return values_qv @@ -1223,10 +872,6 @@ def make_values_quant(this_dataset_updated_uuid, i, luinst): make_values_cat, make_values_quant, ) -<<<<<<< HEAD -======= - ->>>>>>> master # this is where things get annoying with needing selects on instance measured @@ -1696,13 +1341,8 @@ def ingest_reva_ft_all(session, source_local=False, do_insert=True, batch=False, def main(source_local=False, commit=False, echo=True): from quantdb.config import auth -<<<<<<< HEAD - dbkwargs = {k: auth.get(f"db-{k}") for k in ("user", "host", "port", "database")} # TODO integrate with cli options - dbkwargs["dbuser"] = dbkwargs.pop("user") -======= dbkwargs = {k: auth.get(f'db-{k}') for k in ('user', 'host', 'port', 'database')} # TODO integrate with cli options dbkwargs['dbuser'] = dbkwargs.pop('user') ->>>>>>> master engine = create_engine(dbUri(**dbkwargs)) engine.echo = echo session = Session(engine) From 81487b7e936071a4df10589f59789a41fdc818fa Mon Sep 17 00:00:00 2001 From: Troy Sincomb Date: Mon, 28 Apr 2025 14:31:41 -0700 Subject: [PATCH 12/13] added fastapi postgresql folder --- quantdb/postgresql_app/__init__.py | 5 +++++ quantdb/postgresql_app/database.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 quantdb/postgresql_app/__init__.py create mode 100644 quantdb/postgresql_app/database.py diff --git a/quantdb/postgresql_app/__init__.py b/quantdb/postgresql_app/__init__.py new file mode 100644 index 0000000..e73dd2a --- /dev/null +++ b/quantdb/postgresql_app/__init__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from . import database + +__all__ = ['database'] diff --git a/quantdb/postgresql_app/database.py b/quantdb/postgresql_app/database.py new file mode 100644 index 0000000..411d706 --- /dev/null +++ b/quantdb/postgresql_app/database.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from typing import Any + +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +from quantdb.config import Settings + +# TODO: get new setting env setup first +# SQLALCHEMY_DATABASE_URL = Settings().MYSQL_URL + +# engine = create_engine(SQLALCHEMY_DATABASE_URL) +# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Base: Any = declarative_base() From 542f773ea21fc159bd05c886d3dd50a5c8e488a4 Mon Sep 17 00:00:00 2001 From: Troy Sincomb Date: Mon, 28 Apr 2025 14:40:35 -0700 Subject: [PATCH 13/13] remove ingest from blue ignore and found internal bug on why api was formating --- .pre-commit-config.yaml | 1 - quantdb/api.py | 602 +++++++++++++++++----------------- quantdb/ingest.py | 66 ++-- quantdb/mysql_app/__init__.py | 5 - quantdb/mysql_app/database.py | 17 - 5 files changed, 330 insertions(+), 361 deletions(-) delete mode 100644 quantdb/mysql_app/__init__.py delete mode 100644 quantdb/mysql_app/database.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ce8cf5..c1fdb6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,5 @@ repos: exclude: | (?x)^( quantdb/api.py| - quantdb/ingest.py| sql;/.* )$ diff --git a/quantdb/api.py b/quantdb/api.py index b66bd3d..cec693f 100644 --- a/quantdb/api.py +++ b/quantdb/api.py @@ -28,6 +28,7 @@ def default(self, obj): url_sql_where = ( # TODO arity spec here + # dupes overwrite params but that is ok, this way we get the correct table alias for both cases ('object', 'object', 'cv.object = any(:object)', 'cat'), # XXX should not use this outside values/ unless we left outer due to intersect ? ('object', 'object', 'qv.object = any(:object)', 'quant'), # XXX should not use this outside values/ unless we left outer due to intersect ? @@ -63,7 +64,7 @@ def get_where(kwargs): for u, s, w, t in url_sql_where: if u in kwargs and kwargs[u]: params[s] = kwargs[u] - if t == "cat": + if t == 'cat': _where_cat.append(w) elif t == 'quant': # do not include value-quant if value-quant-margin is provided @@ -77,23 +78,23 @@ def get_where(kwargs): _where_cat.append(w) _where_quant.append(w) else: - raise ValueError("wat") + raise ValueError('wat') - where_cat = " AND ".join(_where_cat) - where_quant = " AND ".join(_where_quant) - log.log(9, f"\nwhere-quant\n{where_quant}\nwhere-quant") + where_cat = ' AND '.join(_where_cat) + where_quant = ' AND '.join(_where_quant) + log.log(9, f'\nwhere-quant\n{where_quant}\nwhere-quant') return where_cat, where_quant, params def main_query(endpoint, kwargs): ep_select = { #'instances': 'im.dataset, im.id_formal, im.id_sam, im.id_sub, id.label', - "values/inst": ( - "im.dataset, " - "im.id_formal AS inst, " - "im.id_sam AS sample, " - "im.id_sub AS subject, " - "id.label AS desc_inst" + 'values/inst': ( + 'im.dataset, ' + 'im.id_formal AS inst, ' + 'im.id_sam AS sample, ' + 'im.id_sub AS subject, ' + 'id.label AS desc_inst' ), 'objects': ( # TODO probably some path metadata file type, etc. too 'im.dataset, ' @@ -102,86 +103,97 @@ def main_query(endpoint, kwargs): 'o.id_file, ' # beware that there might be more than one id_file if a package is multi-file, but we usually ban those 'oi.updated_transitive' ), - "values/cat": ( - "im.dataset, " - "im.id_formal AS inst, " - "id.label AS desc_inst, " - "cdid.label AS domain, " - "cd.range, " - "cd.label AS desc_cat, " - "cv.value_open, " - "ct.label AS value_controlled" # TODO and where did it come from TODO iri + 'values/cat': ( + 'im.dataset, ' + 'im.id_formal AS inst, ' + 'id.label AS desc_inst, ' + 'cdid.label AS domain, ' + 'cd.range, ' + 'cd.label AS desc_cat, ' + 'cv.value_open, ' + 'ct.label AS value_controlled' # TODO and where did it come from TODO iri ), # TODO will want/need to return the shape of the value for these as well since that will be needed to correctly interpret the contents of the value field in the future - "values/quant": ( - "im.dataset, " - "im.id_formal AS inst, " - "id.label AS desc_inst, " - "qd.aggregation_type AS agg_type, " - "a.label AS aspect, " - "u.label AS unit, qv.value" # TODO and where did it come from + 'values/quant': ( + 'im.dataset, ' + 'im.id_formal AS inst, ' + 'id.label AS desc_inst, ' + 'qd.aggregation_type AS agg_type, ' + 'a.label AS aspect, ' + 'u.label AS unit, qv.value' # TODO and where did it come from ), - "values/cat-quant": ( + 'values/cat-quant': ( ( "'value-cat' AS type, " - "im.dataset, " - "im.id_formal AS inst, " - "id.label AS desc_inst, " - "cdid.label AS domain, " - "cd.range, " - "NULL::quant_agg_type as agg_type, " # have to annoate the nulls because distinct causes type inference to fail ??? - "cd.label AS pred_or_asp, " - "cv.value_open AS vo_or_unit, " - "ct.label AS value_controlled, " - "NULL::numeric AS value" - ), - ( + 'im.dataset, ' + 'im.id_formal AS inst, ' + 'id.label AS desc_inst, ' + 'cdid.label AS domain, ' + 'cd.range, ' + 'NULL::quant_agg_type as agg_type, ' # have to annoate the nulls because distinct causes type inference to fail ??? + 'cd.label AS pred_or_asp, ' + 'cv.value_open AS vo_or_unit, ' + 'ct.label AS value_controlled, ' + 'NULL::numeric AS value') + , ( "'value-quant' AS type, im.dataset, " - "im.id_formal AS inst, id.label AS desc_inst, " - "NULL AS domain, " - "NULL::cat_range_type AS range, " - "qd.aggregation_type AS agg_type, " - "a.label AS aspect, " - "u.label AS unit, " - "NULL AS vc, qv.value" - ), + 'im.id_formal AS inst, id.label AS desc_inst, ' + 'NULL AS domain, ' + 'NULL::cat_range_type AS range, ' + 'qd.aggregation_type AS agg_type, ' + 'a.label AS aspect, ' + 'u.label AS unit, ' + 'NULL AS vc, qv.value' + )), + 'desc/inst': ( + 'id.iri, ' + 'id.label, ' + 'idpar.label as subclassof ' ), - "desc/inst": ("id.iri, " "id.label, " "idpar.label as subclassof "), - "desc/cat": ("cd.label, " "cdid.label AS domain, " "cd.range, " "cd.description "), - "desc/quant": ( - "qd.label, " - "id.label AS domain, " - "qd.shape, " - "qd.aggregation_type as agg_type, " - "a.label AS aspect, " - "u.label AS unit, " - "qd.description " + 'desc/cat': ( + 'cd.label, ' + 'cdid.label AS domain, ' + 'cd.range, ' + 'cd.description ' + ), + 'desc/quant': ( + 'qd.label, ' + 'id.label AS domain, ' + 'qd.shape, ' + 'qd.aggregation_type as agg_type, ' + 'a.label AS aspect, ' + 'u.label AS unit, ' + 'qd.description ' + ), + 'terms': ( + 'ct.iri, ' + 'ct.label ' + ), + 'units': ( + 'u.iri, ' + 'u.label ' + ), + 'aspects': ( + 'a.iri, ' + 'a.label, ' + 'aspar.label as subclassof ' ), - "terms": ("ct.iri, " "ct.label "), - "units": ("u.iri, " "u.label "), - "aspects": ("a.iri, " "a.label, " "aspar.label as subclassof "), }[endpoint] # FIXME move extra and select out and pass then in in as arguments ? or retain control here? - def gkw(k): - return k in kwargs and kwargs[k] + def gkw(k): return k in kwargs and kwargs[k] class sn: # select needs - objects = endpoint == "objects" - desc_inst = endpoint not in ( - "objects", - "terms", - "units", - "aspects", - ) - desc_cat = endpoint in ("values/cat", "values/cat-quant", "desc/cat") - value_cat = endpoint in ("values/cat", "values/cat-quant", "terms") - aspect = endpoint in ("values/quant", "values/cat-quant", "desc/quant", "aspects") - unit = endpoint in ("values/quant", "values/cat-quant", "desc/quant", "units") - agg_type = endpoint in ("values/quant", "values/cat-quant") - desc_quant = aspect or unit or agg_type - parent_aspect = endpoint == "aspects" - parent_desc_inst = endpoint == "desc/inst" + objects = endpoint == 'objects' + desc_inst = endpoint not in ('objects', 'terms', 'units', 'aspects',) + desc_cat = endpoint in ('values/cat', 'values/cat-quant', 'desc/cat') + value_cat = endpoint in ('values/cat', 'values/cat-quant', 'terms') + aspect = endpoint in ('values/quant', 'values/cat-quant', 'desc/quant', 'aspects') + unit = endpoint in ('values/quant', 'values/cat-quant', 'desc/quant', 'units') + agg_type = endpoint in ('values/quant', 'values/cat-quant') + desc_quant = (aspect or unit or agg_type) + parent_aspect = endpoint == 'aspects' + parent_desc_inst = endpoint == 'desc/inst' class kw: # keywords prov = gkw('prov') @@ -233,8 +245,7 @@ class kw: # keywords adc.addr_type as prov_value_addr_type, adc.addr_field as prov_value_addr_field, adc.value_type as prov_value_type -""" + ( - """, +""" + (""", NULL::address_type as prov_unit_addr_type, NULL as prov_unit_addr_field, NULL::field_value_type as prov_unit_type, @@ -242,10 +253,7 @@ class kw: # keywords NULL::address_type as prov_aspect_addr_type, NULL as prov_aspect_addr_field, NULL::field_value_type as prov_aspect_type -""" - if sn.unit or endpoint == "values/inst" - else "" - ) +""" if sn.unit or endpoint == 'values/inst' else '') s_prov_q = """ adq.addr_type as prov_value_addr_type, @@ -279,26 +287,18 @@ class kw: # keywords LEFT OUTER JOIN addresses AS ada ON ada.id = odq.addr_aspect """ - maybe_distinct = ( - "DISTINCT " - if ( - endpoint.startswith("desc/") - or endpoint in ("terms", "units", "aspects") - or (sn.objects or kw.prov) - and not kw.source_only - ) - else "" - ) + maybe_distinct = 'DISTINCT ' if ( + endpoint.startswith('desc/') or + endpoint in ('terms', 'units', 'aspects') or + (sn.objects or kw.prov) and not kw.source_only) else '' ep_select_cat, ep_select_quant = ep_select if isinstance(ep_select, tuple) else (ep_select, ep_select) - select_cat = f"SELECT {maybe_distinct}{ep_select_cat}" + ( - (s_prov_objs + s_prov_i + ((",\n" + s_prov_c) if endpoint != "values/inst" else "")) if kw.prov else "" - ) - select_quant = f"SELECT {maybe_distinct}{ep_select_quant}" + ( - (s_prov_objs + s_prov_i + ((",\n" + s_prov_q) if endpoint != "values/inst" else "")) if kw.prov else "" - ) + select_cat = f'SELECT {maybe_distinct}{ep_select_cat}' + ( + (s_prov_objs + s_prov_i + ((',\n' + s_prov_c) if endpoint != 'values/inst' else '')) if kw.prov else '') + select_quant = f'SELECT {maybe_distinct}{ep_select_quant}' + ( + (s_prov_objs + s_prov_i + ((',\n' + s_prov_q) if endpoint != 'values/inst' else '')) if kw.prov else '') _where_cat, _where_quant, params = get_where(kwargs) - where_cat = f"WHERE {_where_cat}" if _where_cat else "" - where_quant = f"WHERE {_where_quant}" if _where_quant else "" + where_cat = f'WHERE {_where_cat}' if _where_cat else '' + where_quant = f'WHERE {_where_quant}' if _where_quant else '' q_inst_parent = '\n'.join(( 'JOIN values_inst AS icin', @@ -371,66 +371,55 @@ class kw: # keywords (q_prov_i + q_prov_q) if kw.prov else '', )) - sw_cat = f"{select_cat}\n{q_cat}\n{where_cat}" # XXX yes this can be malformed in some cases - sw_quant = f"{select_quant}\n{q_quant}\n{where_quant}" # XXX yes this can be malformed in some cases - if endpoint in ("values/cat", "terms", "desc/cat"): + sw_cat = f'{select_cat}\n{q_cat}\n{where_cat}' # XXX yes this can be malformed in some cases + sw_quant = f'{select_quant}\n{q_quant}\n{where_quant}' # XXX yes this can be malformed in some cases + if endpoint in ('values/cat', 'terms', 'desc/cat'): query = sw_cat - elif endpoint in ( - "values/quant", - "units", - "aspects", - "desc/quant", - ): # FIXME TODO make it possible to cross query terms, units, aspects + elif endpoint in ('values/quant', 'units', 'aspects', 'desc/quant'): # FIXME TODO make it possible to cross query terms, units, aspects query = sw_quant else: - operator = "UNION" if "union-cat-quant" in kwargs and kwargs["union-cat-quant"] else "INTERSECT" - query = f"{sw_cat}\n{operator}\n{sw_quant}" + operator = 'UNION' if 'union-cat-quant' in kwargs and kwargs['union-cat-quant'] else 'INTERSECT' + query = f'{sw_cat}\n{operator}\n{sw_quant}' - log.log(9, "\n" + query) + log.log(9, '\n' + query) return query, params def to_json(record_type, res, prov=False): rows = list(res) if rows: - if record_type == "object": - result = [ - ( - {k: v for k, v in r._asdict().items() if k != "id"} - # do not leak internal ids because the might change and are not meaningful - if r.id_type == "quantdb" - else {k: v for k, v in r._asdict().items() if k != "updated_transitive"} - ) - for r in rows - ] - elif record_type is None and "type" in rows[0]._fields: - rem_cat = "value", "agg_type" - + if record_type == 'object': + result = [{k: v for k, v in r._asdict().items() if k != 'id'} + # do not leak internal ids because the might change and are not meaningful + if r.id_type == 'quantdb' else + {k: v for k, v in r._asdict().items() if k != 'updated_transitive'} + for r in rows] + elif record_type is None and 'type' in rows[0]._fields: + rem_cat = 'value', 'agg_type' def type_fields_cat(k): - if k == "pred_or_asp": - return "desc_cat" - elif k == "vo_or_unit": - return "value_open" + if k == 'pred_or_asp': + return 'desc_cat' + elif k == 'vo_or_unit': + return 'value_open' else: return k - rem_quant = "domain", "range", "value_controlled" - + rem_quant = 'domain', 'range', 'value_controlled' def type_fields_quant(k): - if k == "pred_or_asp": - return "aspect" - elif k == "vo_or_unit": - return "unit" + if k == 'pred_or_asp': + return 'aspect' + elif k == 'vo_or_unit': + return 'unit' else: return k def prow(r): - if r.type == "value-cat": + if r.type == 'value-cat': rem, type_fields = rem_cat, type_fields_cat - elif r.type == "value-quant": + elif r.type == 'value-quant': rem, type_fields = rem_quant, type_fields_quant else: - raise NotImplementedError(f"wat {r.type}") + raise NotImplementedError(f'wat {r.type}') return {type_fields(k): v for k, v in r._asdict().items() if k not in rem} @@ -440,40 +429,35 @@ def prow(r): for r in result: if record_type is not None: - r["type"] = record_type + r['type'] = record_type - for cull_none in ("subclassof",): + for cull_none in ('subclassof',): if cull_none in r and r[cull_none] is None: r.pop(cull_none) if prov: - def pop_prefix(d, prefix): - usc = prefix.count("_") - return { - k.split("_", 1 + usc)[-1]: v - for k in list(d) - if k.startswith(prefix + "_") and (v := d.pop(k)) is not None - } + usc = prefix.count('_') + return {k.split('_', 1 + usc)[-1]:v for k in list(d) if k.startswith(prefix + '_') and (v := d.pop(k)) is not None} for r in result: - provs = pop_prefix(r, "prov") - if "source_id_type" in provs and provs["source_id_type"] == "quantdb": - provs.pop("source_id", None) # don't leak internal ids + provs = pop_prefix(r, 'prov') + if 'source_id_type' in provs and provs['source_id_type'] == 'quantdb': + provs.pop('source_id', None) # don't leak internal ids else: - provs.pop("source_updated_transitive", None) # always None in this case + provs.pop('source_updated_transitive', None) # always None in this case - for prefix in ("desc_inst", "inst", "value", "value", "source"): + for prefix in ('desc_inst', 'inst', 'value', 'value', 'source'): d = pop_prefix(provs, prefix) if d: - d["type"] = "address" if prefix != "source" else "object" + d['type'] = 'address' if prefix != 'source' else 'object' provs[prefix] = d - provs["type"] = "prov" - r["prov"] = provs + provs['type'] = 'prov' + r['prov'] = provs out = result - # breakpoint() + #breakpoint() else: out = [] @@ -488,20 +472,22 @@ def wrap_out(endpoint, kwargs, out): parameters = {k: v for k, v in kwargs.items() if v} n_records = len(out) blob = { - "type": "quantdb-query-result", - "endpoint": endpoint, - "parameters": parameters, - "records": n_records, - "result": out, + 'type': 'quantdb-query-result', + 'endpoint': endpoint, + 'parameters': parameters, + 'records': n_records, + 'result': out, } return blob args_default = { - "object": [], - "updated-transitive": None, # TODO needed to query for some internal + 'object': [], + 'updated-transitive': None, # TODO needed to query for some internal + ## inst - "desc-inst": [], # aka class + 'desc-inst': [], # aka class + # value-inst 'dataset': None, 'inst': [], @@ -511,30 +497,36 @@ def wrap_out(endpoint, kwargs, out): 'include-equivalent': False, ## cat - "desc-cat": [], # aka predicate - "value-cat": [], - "value-cat-open": [], + 'desc-cat': [], # aka predicate + + 'value-cat': [], + 'value-cat-open': [], + ## quant # desc-quant - "unit": [], - "aspect": [], - "agg-type": None, + 'unit': [], + 'aspect': [], + 'agg-type': None, # TODO shape - "value-quant": None, - "value-quant-margin": None, - "value-quant-min": None, - "value-quant-max": None, - "limit": 100, + + 'value-quant': None, + 'value-quant-margin': None, + 'value-quant-min': None, + 'value-quant-max': None, + + 'limit': 100, #'operator': 'INTERSECT', # XXX ... - "union-cat-quant": False, # by default we intersect but sometimes you want the union instead e.g. if object is passed - "source-only": False, - "include-unused": False, - "prov": False, + 'union-cat-quant': False, # by default we intersect but sometimes you want the union instead e.g. if object is passed + 'source-only': False, + 'include-unused': False, + 'prov': False, + #'cat-value': [], #'class': [], #'predicate': None, #'object': None, #'filter': [], + #'quant-value': None, #'quant-margin': None, #'quant-min': None, @@ -546,33 +538,33 @@ def getArgs(request, endpoint, dev=False): default = copy.deepcopy(args_default) if dev: - default["return-query"] = False + default['return-query'] = False # modify defaults by endpoint - if endpoint != "objects": - default.pop("source-only") + if endpoint != 'objects': + default.pop('source-only') - if not (endpoint.startswith("desc/") or endpoint in ("terms", "units", "aspects")): - default.pop("include-unused") + if not (endpoint.startswith('desc/') or endpoint in ('terms', 'units', 'aspects')): + default.pop('include-unused') else: # prevent filtering on the thing we are trying to query - if endpoint == "terms": - default.pop("value-cat") - elif endpoint == "units": - default.pop("unit") - elif endpoint == "aspects": - default.pop("aspect") - elif endpoint == "desc/inst": - default.pop("desc-inst") - elif endpoint == "desc/cat": - default.pop("desc-cat") - - if not endpoint.startswith("values/"): - default.pop("prov") - elif endpoint == "values/cat": - [default.pop(k) for k in list(default) if k.startswith("value-quant") or k in ("unit", "aspect", "agg-type")] - elif endpoint == "values/quant": - [default.pop(k) for k in list(default) if k in ("desc-cat", "value-cat", "value-cat-open")] + if endpoint == 'terms': + default.pop('value-cat') + elif endpoint == 'units': + default.pop('unit') + elif endpoint == 'aspects': + default.pop('aspect') + elif endpoint == 'desc/inst': + default.pop('desc-inst') + elif endpoint == 'desc/cat': + default.pop('desc-cat') + + if not endpoint.startswith('values/'): + default.pop('prov') + elif endpoint == 'values/cat': + [default.pop(k) for k in list(default) if k.startswith('value-quant') or k in ('unit', 'aspect', 'agg-type')] + elif endpoint == 'values/quant': + [default.pop(k) for k in list(default) if k in ('desc-cat', 'value-cat', 'value-cat-open')] if (endpoint == 'values/inst') or (endpoint == 'objects'): # prevent getting no results if only cat or quant @@ -584,8 +576,8 @@ def getArgs(request, endpoint, dev=False): extras = set(request.args) - set(default) if extras: # FIXME raise this as a 401, TODO need error types for this - nl = "\n" - raise exc.UnknownArg(f"unknown args: {nl.join(extras)}") + nl = '\n' + raise exc.UnknownArg(f'unknown args: {nl.join(extras)}') def convert(k, d): if k in request.args: @@ -620,14 +612,14 @@ def convert(k, d): else: return d - if k in ("include-equivalent", "union-cat-quant", "include-unused"): - if v.lower() == "true": + if k in ('include-equivalent', 'union-cat-quant', 'include-unused'): + if v.lower() == 'true': return True - elif v.lower() == "false": + elif v.lower() == 'false': return False else: raise TypeError(f'Expected a bool, got "{v}" instead.') - elif k.startswith("value-quant") or k in ("limit",): + elif k.startswith('value-quant') or k in ('limit',): try: return float(v) except ValueError as e: @@ -635,20 +627,21 @@ def convert(k, d): else: return v - out = {k: convert(k, v) for k, v in default.items()} + out = {k:convert(k, v) for k, v in default.items()} return out -def make_app(db=None, name="quantdb-api-server", dev=False): +def make_app(db=None, name='quantdb-api-server', dev=False): app = Flask(name) - kwargs = {k: auth.get(f"db-{k}") for k in ("user", "host", "port", "database")} # TODO integrate with cli options - kwargs["dbuser"] = kwargs.pop("user") - app.config["SQLALCHEMY_DATABASE_URI"] = dbUri(**kwargs) # use os.environ.update - app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + kwargs = {k:auth.get(f'db-{k}') # TODO integrate with cli options + for k in ('user', 'host', 'port', 'database')} + kwargs['dbuser'] = kwargs.pop('user') + app.config['SQLALCHEMY_DATABASE_URI'] = dbUri(**kwargs) # use os.environ.update + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db.init_app(app) session = db.session - bp = "/api/1/" + bp = '/api/1/' def default_flow(endpoint, record_type, query_fun, json_fun, alt_query_fun=None): try: @@ -659,10 +652,9 @@ def default_flow(endpoint, record_type, query_fun, json_fun, alt_query_fun=None) breakpoint() raise e - def gkw(k): - return k in kwargs and kwargs[k] + def gkw(k): return k in kwargs and kwargs[k] - if gkw("include-unused"): + if gkw('include-unused'): query_fun = alt_query_fun # FIXME record_type is actually determined entirely in query_fun right now @@ -708,137 +700,137 @@ def gkw(k): raise e try: - out = json_fun(record_type, res, prov=("prov" in kwargs and kwargs["prov"])) - resp = json.dumps(wrap_out(endpoint, kwargs, out), cls=JEncode), 200, {"Content-Type": "application/json"} + out = json_fun(record_type, res, prov=('prov' in kwargs and kwargs['prov'])) + resp = json.dumps(wrap_out(endpoint, kwargs, out), cls=JEncode), 200, {'Content-Type': 'application/json'} except Exception as e: breakpoint() raise e return resp - @app.route(f"{bp}/test") - def route_test_check(): - "objects with derived values that match all criteria" - return "testing-api" - - @app.route(f"{bp}/objects") + @app.route(f'{bp}/objects') def route_1_objects(): "objects with derived values that match all criteria" - return default_flow("objects", "object", main_query, to_json) + return default_flow('objects', 'object', main_query, to_json) - @app.route(f"{bp}/desc/inst") - @app.route(f"{bp}/descriptors/inst") - @app.route(f"{bp}/classes") + @app.route(f'{bp}/desc/inst') + @app.route(f'{bp}/descriptors/inst') + @app.route(f'{bp}/classes') def route_1_desc_inst(): def query(endpoint, kwargs): - return ( - ( - "SELECT " - "id.iri, " - "id.label, " - "idpar.label as subclassof" + return ('SELECT ' + + 'id.iri, ' + 'id.label, ' + 'idpar.label as subclassof' + """ FROM descriptors_inst AS id LEFT OUTER JOIN class_parent AS clp ON clp.id = id.id LEFT OUTER JOIN descriptors_inst AS idpar ON idpar.id = clp.parent """), {} - return default_flow("desc/inst", "desc-inst", main_query, to_json, alt_query_fun=query) + return default_flow('desc/inst', 'desc-inst', main_query, to_json, alt_query_fun=query) - @app.route(f"{bp}/desc/cat") - @app.route(f"{bp}/descriptors/cat") - @app.route(f"{bp}/predicates") + @app.route(f'{bp}/desc/cat') + @app.route(f'{bp}/descriptors/cat') + @app.route(f'{bp}/predicates') def route_1_desc_cat(): def query(endpoint, kwargs): - return ( - "select " - "cd.label, " - "cdid.label AS domain, " - "cd.range, " - "cd.description " - "from descriptors_cat as cd " - "left outer join descriptors_inst as cdid on cdid.id = cd.domain" - ), {} - - return default_flow( - "desc/cat", "desc-cat", main_query, to_json, alt_query_fun=query - ) # TODO likely need different args e.g. to filter by desc_inst - - @app.route(f"{bp}/desc/quant") - @app.route(f"{bp}/descriptors/quant") + return ('select ' + + 'cd.label, ' + 'cdid.label AS domain, ' + 'cd.range, ' + 'cd.description ' + + 'from descriptors_cat as cd ' + 'left outer join descriptors_inst as cdid on cdid.id = cd.domain' + ), {} + + return default_flow('desc/cat', 'desc-cat', main_query, to_json, alt_query_fun=query) # TODO likely need different args e.g. to filter by desc_inst + + @app.route(f'{bp}/desc/quant') + @app.route(f'{bp}/descriptors/quant') def route_1_desc_quant(): def query(endpoint, kwargs): - return ( - "select " - "qd.label, " - "id.label AS domain, " - "qd.shape, " - "qd.aggregation_type as agg_type, " - "a.label AS aspect, " - "u.label AS unit, " - "qd.description " - "from descriptors_quant as qd " - "left outer join descriptors_inst as id on id.id = qd.domain " - "left outer join units as u on u.id = qd.unit " - "join aspects as a on a.id = qd.aspect" - ), {} - - return default_flow( - "desc/quant", "desc-quant", main_query, to_json, alt_query_fun=query - ) # TODO likely need different args e.g. to filter by desc_inst - - @app.route(f"{bp}/values/inst") - @app.route(f"{bp}/instances") + return ('select ' + + 'qd.label, ' + 'id.label AS domain, ' + 'qd.shape, ' + 'qd.aggregation_type as agg_type, ' + 'a.label AS aspect, ' + 'u.label AS unit, ' + 'qd.description ' + + 'from descriptors_quant as qd ' + 'left outer join descriptors_inst as id on id.id = qd.domain ' + 'left outer join units as u on u.id = qd.unit ' + 'join aspects as a on a.id = qd.aspect' + ), {} + + return default_flow('desc/quant', 'desc-quant', main_query, to_json, alt_query_fun=query) # TODO likely need different args e.g. to filter by desc_inst + + @app.route(f'{bp}/values/inst') + @app.route(f'{bp}/instances') def route_1_val_inst(): "instances associated with values that match all critiera" - return default_flow("values/inst", "instance", main_query, to_json) + return default_flow('values/inst', 'instance', main_query, to_json) - @app.route(f"{bp}/values") - @app.route(f"{bp}/values/cat-quant") + @app.route(f'{bp}/values') + @app.route(f'{bp}/values/cat-quant') def route_1_val_cat_quant(): - return default_flow("values/cat-quant", None, main_query, to_json) + return default_flow('values/cat-quant', None, main_query, to_json) - @app.route(f"{bp}/values/cat") + @app.route(f'{bp}/values/cat') def route_1_val_cat(): - return default_flow("values/cat", "value-cat", main_query, to_json) + return default_flow('values/cat', 'value-cat', main_query, to_json) - @app.route(f"{bp}/values/quant") + @app.route(f'{bp}/values/quant') def route_1_val_quant(): - return default_flow("values/quant", "value-quant", main_query, to_json) + return default_flow('values/quant', 'value-quant', main_query, to_json) - @app.route(f"{bp}/terms") - @app.route(f"{bp}/controlled-terms") + @app.route(f'{bp}/terms') + @app.route(f'{bp}/controlled-terms') def route_1_cterms(): def query(endpoint, kwargs): - return ("select " "ct.iri, " "ct.label " "from controlled_terms as ct"), {} + return ('select ' + + 'ct.iri, ' + 'ct.label ' + + 'from controlled_terms as ct'), {} - return default_flow("terms", "term", main_query, to_json, alt_query_fun=query) + return default_flow('terms', 'term', main_query, to_json, alt_query_fun=query) - @app.route(f"{bp}/units") + @app.route(f'{bp}/units') def route_1_units(): def query(endpoint, kwargs): - return ("select " "u.iri, " "u.label " "from units as u"), {} + return ('select ' + + 'u.iri, ' + 'u.label ' + + 'from units as u'), {} - return default_flow("units", "unit", main_query, to_json, alt_query_fun=query) + return default_flow('units', 'unit', main_query, to_json, alt_query_fun=query) - @app.route(f"{bp}/aspects") + @app.route(f'{bp}/aspects') def route_1_aspects(): def query(endpoint, kwargs): - return ( - ( - "SELECT " - "a.iri, " - "a.label, " - "aspar.label AS subclassof " + return ('SELECT ' + + 'a.iri, ' + 'a.label, ' + 'aspar.label AS subclassof ' + """ FROM aspects AS a LEFT OUTER JOIN aspect_parent AS ap ON ap.id = a.id LEFT OUTER JOIN aspects AS aspar ON aspar.id = ap.parent -""" - ), - {}, - ) +"""), {} - return default_flow("aspects", "aspect", main_query, to_json, alt_query_fun=query) + return default_flow('aspects', 'aspect', main_query, to_json, alt_query_fun=query) return app diff --git a/quantdb/ingest.py b/quantdb/ingest.py index b3f514d..85c37a1 100644 --- a/quantdb/ingest.py +++ b/quantdb/ingest.py @@ -24,7 +24,7 @@ log = log.getChild('ingest') try: - if get_ipython().__class__.__name__ == "ZMQInteractiveShell": + if get_ipython().__class__.__name__ == 'ZMQInteractiveShell': import sys # FIXME hack that should be in jupyter-repl env or something sys.breakpointhook = lambda: None @@ -66,7 +66,7 @@ def __call__(self, value, type=None): return self.value_to_name[value] else: self.counter += 1 - name = "v" + str(self.counter) + name = 'v' + str(self.counter) if type is None: self.value_to_name[value] = name @@ -80,8 +80,8 @@ def __call__(self, value, type=None): def makeParamsValues(*value_sets, constants=tuple(), types=tuple(), row_types=tuple()): # TODO variable sized records and # common value names - if constants and not all(":" in c for c in constants): - raise ValueError(f"All constants must pass variables in via params {constants}") + if constants and not all(':' in c for c in constants): + raise ValueError(f'All constants must pass variables in via params {constants}') getname = getName() @@ -145,9 +145,9 @@ def makeParamsValues(*value_sets, constants=tuple(), types=tuple(), row_types=tu 'p': 2, # posterior } seg_ordering = { - "c": 0, # cervical - "t": 1, # thoracic - "a": 2, # abdominal + 'c': 0, # cervical + 't': 1, # thoracic + 'a': 2, # abdominal } @@ -155,7 +155,7 @@ def anat_index(sample): # count the number of distinct values less than a given integer # create the map - sam, sam_id, seg, seg_id = sample.split("-") + sam, sam_id, seg, seg_id = sample.split('-') # FIXME bad becase left and right are unstable and we don't care about this, we just want relative to max possible # don't do this with sort sam_ind = sam_ordering[sam_id] @@ -172,7 +172,7 @@ def anat_index(sample): suffix = int(seg_id[-1].encode().hex()) return sam_ind, 0, rest, suffix else: - msg = f"unknown seg {sample}" + msg = f'unknown seg {sample}' print(msg) # FIXME TODO logging # raise ValueError(msg) # return int(f'{sam_ind}000') @@ -392,8 +392,8 @@ def __init__(self, queries): 'json-path-with-types', '#/path-metadata/data/#int/dataset_relative_path#derive-sample-id' ) - self.addr_jpspec = q.address_from_fadd_type_fadd("json-path-with-types", "#/local/tom-made-it-up/species") - self.addr_jpsaty = q.address_from_fadd_type_fadd("json-path-with-types", "#/local/tom-made-it-up/sample_type") + self.addr_jpspec = q.address_from_fadd_type_fadd('json-path-with-types', '#/local/tom-made-it-up/species') + self.addr_jpsaty = q.address_from_fadd_type_fadd('json-path-with-types', '#/local/tom-made-it-up/sample_type') # future version when we actually have the metadata files # addr_jpmod = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/manifest/#int/modality') @@ -402,7 +402,7 @@ def __init__(self, queries): # addr_jpsuid = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/subjects/#int/id_sub') # addr_jpsaid = address_from_fadd_type_fadd('json-path-with-types', '#/curation-export/samples/#int/id_sam') - self.addr_const_null = q.address_from_fadd_type_fadd("constant", None) + self.addr_const_null = q.address_from_fadd_type_fadd('constant', None) self.qd_nvlai1 = q.desc_quant_from_label('vagus level anatomical location distance index normalized v1') self.qd_nvlain1 = q.desc_quant_from_label('vagus level anatomical location distance index normalized v1 min') @@ -440,11 +440,11 @@ def __init__(self, queries): 'fascicle-cross-section': self.id_fascicle_cross_section, } - self.ct_mod = q.cterm_from_label("microct") # lol ct ct - self.ct_hack = q.cterm_from_label("hack-associate-some-value") + self.ct_mod = q.cterm_from_label('microct') # lol ct ct + self.ct_hack = q.cterm_from_label('hack-associate-some-value') self.luct = { - "ct-hack": self.ct_hack, - "microct": self.ct_mod, + 'ct-hack': self.ct_hack, + 'microct': self.ct_mod, } @@ -459,10 +459,10 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a add a kwarg to control it maybe? """ - ocdn = " ON CONFLICT DO NOTHING" if dev else "" + ocdn = ' ON CONFLICT DO NOTHING' if dev else '' if extract_fun is None and values_args is None: - raise TypeError("need one of extract_fun or values_args") + raise TypeError('need one of extract_fun or values_args') ( updated_transitive, @@ -523,10 +523,10 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a ) vt, params = makeParamsValues(values_objects) - session.execute(sql_text(f"INSERT INTO objects (id, id_type, id_file) VALUES {vt}{ocdn}"), params) + session.execute(sql_text(f'INSERT INTO objects (id, id_type, id_file) VALUES {vt}{ocdn}'), params) vt, params = makeParamsValues(values_dataset_object) - session.execute(sql_text(f"INSERT INTO dataset_object (dataset, object) VALUES {vt}{ocdn}"), params) + session.execute(sql_text(f'INSERT INTO dataset_object (dataset, object) VALUES {vt}{ocdn}'), params) vt, params = makeParamsValues(values_instances) session.execute( @@ -545,7 +545,7 @@ def ingest(dataset_uuid, extract_fun, session, commit=False, dev=False, values_a values_qv = make_values_quant(this_dataset_updated_uuid, i, luinst) vt, params = makeParamsValues(values_parents) - session.execute(sql_text(f"INSERT INTO instance_parent VALUES {vt}{ocdn}"), params) + session.execute(sql_text(f'INSERT INTO instance_parent VALUES {vt}{ocdn}'), params) vt, params = makeParamsValues(void) session.execute( @@ -601,7 +601,7 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): blob = json.load(f) else: - resp = requests.get(f"https://cassava.ucsd.edu/sparc/datasets/{dataset_uuid}/LATEST/path-metadata.json") + resp = requests.get(f'https://cassava.ucsd.edu/sparc/datasets/{dataset_uuid}/LATEST/path-metadata.json') try: blob = resp.json() @@ -609,8 +609,8 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): breakpoint() raise e - for j in blob["data"]: - j["type"] = "pathmeta" + for j in blob['data']: + j['type'] = 'pathmeta' ir = fromJson(blob) @@ -641,7 +641,7 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): ) / mdp1 # ensure there is overlap between section for purposes of testing # TODO norm_anat_index_min # TODO norm_anat_index_max - dd[e["dataset"], e["sample"]].append(e) + dd[e['dataset'], e['sample']].append(e) inst_obj_index = dict(dd) max_nai = max([e['norm_anat_index_v2'] for e in exts]) @@ -728,9 +728,9 @@ def extract_reva_ft(dataset_uuid, source_local=False, visualize=True): instances = {**subjects, **samples} values_objects = [ - (i, o["id_type"], o["id_file"] if "id_file" in o else None) + (i, o['id_type'], o['id_file'] if 'id_file' in o else None) for i, o in objects.items() - if o["id_type"] != "dataset" # already did it above + if o['id_type'] != 'dataset' # already did it above ] values_dataset_object = dataset_object @@ -1315,12 +1315,12 @@ def ingest_demo_jp2(session, source_local=True, do_insert=True, commit=False, de def ingest_reva_ft_all(session, source_local=False, do_insert=True, batch=False, commit=False, dev=False): dataset_uuids = ( - "aa43eda8-b29a-4c25-9840-ecbd57598afc", # f001 + 'aa43eda8-b29a-4c25-9840-ecbd57598afc', # f001 # the rest have uuid1 issues :/ all in the undefined folder it seems, might be able to fix with a reupload - "bc4cc558-727c-4691-ae6d-498b57a10085", # f002 # XXX has a uuid1 so breaking in prod right now have to push the new pipelines - "ec6ad74e-7b59-409b-8fc7-a304319b6faf", # f003 # also uuid1 issue - "a8b2bdc7-54df-46a3-810e-83cdf33cfc3a", # f004 - "04a5fed9-7ba6-4292-b1a6-9cab5c38895f", # f005 + 'bc4cc558-727c-4691-ae6d-498b57a10085', # f002 # XXX has a uuid1 so breaking in prod right now have to push the new pipelines + 'ec6ad74e-7b59-409b-8fc7-a304319b6faf', # f003 # also uuid1 issue + 'a8b2bdc7-54df-46a3-810e-83cdf33cfc3a', # f004 + '04a5fed9-7ba6-4292-b1a6-9cab5c38895f', # f005 ) batched = [] @@ -1378,5 +1378,5 @@ def main(source_local=False, commit=False, echo=True): engine.dispose() -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/quantdb/mysql_app/__init__.py b/quantdb/mysql_app/__init__.py deleted file mode 100644 index e73dd2a..0000000 --- a/quantdb/mysql_app/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from . import database - -__all__ = ['database'] diff --git a/quantdb/mysql_app/database.py b/quantdb/mysql_app/database.py deleted file mode 100644 index 411d706..0000000 --- a/quantdb/mysql_app/database.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from typing import Any - -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -from quantdb.config import Settings - -# TODO: get new setting env setup first -# SQLALCHEMY_DATABASE_URL = Settings().MYSQL_URL - -# engine = create_engine(SQLALCHEMY_DATABASE_URL) -# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -# Base: Any = declarative_base()