From 4cbdb00e14c71c7c9954e4610ecc4ab7544773ef Mon Sep 17 00:00:00 2001 From: "Ishan S. Patel" Date: Thu, 4 Sep 2025 22:48:15 -0400 Subject: [PATCH] YACWC --- .track.py.swp | Bin 0 -> 12288 bytes __init__.py | 2 + __pycache__/__init__.cpython-310.pyc | Bin 0 -> 210 bytes __pycache__/__init__.cpython-311.pyc | Bin 0 -> 240 bytes __pycache__/__init__.cpython-312.pyc | Bin 0 -> 217 bytes __pycache__/__init__.cpython-313.pyc | Bin 0 -> 217 bytes __pycache__/data_structures.cpython-310.pyc | Bin 0 -> 1028 bytes __pycache__/data_structures.cpython-311.pyc | Bin 0 -> 1640 bytes __pycache__/data_structures.cpython-312.pyc | Bin 0 -> 1324 bytes __pycache__/data_structures.cpython-313.pyc | Bin 0 -> 1394 bytes __pycache__/db_conn.cpython-310.pyc | Bin 0 -> 8569 bytes __pycache__/db_conn.cpython-311.pyc | Bin 0 -> 15183 bytes __pycache__/db_conn.cpython-312.pyc | Bin 0 -> 12518 bytes __pycache__/db_conn.cpython-313.pyc | Bin 0 -> 12721 bytes __pycache__/kwq.cpython-310.pyc | Bin 0 -> 1318 bytes __pycache__/kwq.cpython-312.pyc | Bin 0 -> 1912 bytes __pycache__/kwq.cpython-313.pyc | Bin 0 -> 1903 bytes __pycache__/naming.cpython-311.pyc | Bin 0 -> 2548 bytes __pycache__/naming.cpython-312.pyc | Bin 0 -> 2195 bytes __pycache__/settings.cpython-310.pyc | Bin 0 -> 1687 bytes __pycache__/settings.cpython-312.pyc | Bin 0 -> 3007 bytes __pycache__/settings.cpython-313.pyc | Bin 0 -> 3069 bytes __pycache__/util.cpython-310.pyc | Bin 0 -> 2498 bytes __pycache__/util.cpython-311.pyc | Bin 0 -> 5199 bytes __pycache__/util.cpython-312.pyc | Bin 0 -> 5213 bytes __pycache__/util.cpython-313.pyc | Bin 0 -> 5272 bytes __pycache__/video_meta.cpython-312.pyc | Bin 0 -> 10177 bytes __pycache__/video_meta.cpython-313.pyc | Bin 0 -> 10334 bytes __pycache__/wq.cpython-310.pyc | Bin 0 -> 2137 bytes __pycache__/wq.cpython-312.pyc | Bin 0 -> 2869 bytes ... ispatel@live.com 2025-04-15-10-12-32).pyc | Bin 0 -> 2981 bytes __pycache__/wq.cpython-313.pyc | Bin 0 -> 2981 bytes baseObjects.py | 15 + data_structures.py | 13 + db_conn.py | 356 ++++++++++++++++++ distanceTracker.py | 103 +++++ frozen | 73 ++++ get_amts.py | 15 + kwq.py | 54 +++ kwq_test.py | 6 + naming.py | 54 +++ settings.py | 54 +++ takeSnapshot.py | 37 ++ test_dbconn.py | 1 + track.py | 81 ++++ trackSerial.py | 129 +++++++ ...t ispatel@live.com 2025-06-22-10-59-32).py | 155 ++++++++ trackSerial_fullduplex.py | 154 ++++++++ util.py | 112 ++++++ video_meta.py | 199 ++++++++++ wq.py | 96 +++++ 51 files changed, 1709 insertions(+) create mode 100644 .track.py.swp create mode 100755 __init__.py create mode 100644 __pycache__/__init__.cpython-310.pyc create mode 100644 __pycache__/__init__.cpython-311.pyc create mode 100644 __pycache__/__init__.cpython-312.pyc create mode 100644 __pycache__/__init__.cpython-313.pyc create mode 100644 __pycache__/data_structures.cpython-310.pyc create mode 100644 __pycache__/data_structures.cpython-311.pyc create mode 100644 __pycache__/data_structures.cpython-312.pyc create mode 100644 __pycache__/data_structures.cpython-313.pyc create mode 100644 __pycache__/db_conn.cpython-310.pyc create mode 100644 __pycache__/db_conn.cpython-311.pyc create mode 100644 __pycache__/db_conn.cpython-312.pyc create mode 100644 __pycache__/db_conn.cpython-313.pyc create mode 100644 __pycache__/kwq.cpython-310.pyc create mode 100644 __pycache__/kwq.cpython-312.pyc create mode 100644 __pycache__/kwq.cpython-313.pyc create mode 100644 __pycache__/naming.cpython-311.pyc create mode 100644 __pycache__/naming.cpython-312.pyc create mode 100644 __pycache__/settings.cpython-310.pyc create mode 100644 __pycache__/settings.cpython-312.pyc create mode 100644 __pycache__/settings.cpython-313.pyc create mode 100644 __pycache__/util.cpython-310.pyc create mode 100644 __pycache__/util.cpython-311.pyc create mode 100644 __pycache__/util.cpython-312.pyc create mode 100644 __pycache__/util.cpython-313.pyc create mode 100644 __pycache__/video_meta.cpython-312.pyc create mode 100644 __pycache__/video_meta.cpython-313.pyc create mode 100644 __pycache__/wq.cpython-310.pyc create mode 100644 __pycache__/wq.cpython-312.pyc create mode 100644 __pycache__/wq.cpython-313 (SFConflict ispatel@live.com 2025-04-15-10-12-32).pyc create mode 100644 __pycache__/wq.cpython-313.pyc create mode 100755 baseObjects.py create mode 100644 data_structures.py create mode 100755 db_conn.py create mode 100755 distanceTracker.py create mode 100644 frozen create mode 100755 get_amts.py create mode 100644 kwq.py create mode 100644 kwq_test.py create mode 100755 naming.py create mode 100644 settings.py create mode 100755 takeSnapshot.py create mode 100644 test_dbconn.py create mode 100755 track.py create mode 100755 trackSerial.py create mode 100755 trackSerial_fullduplex (SFConflict ispatel@live.com 2025-06-22-10-59-32).py create mode 100755 trackSerial_fullduplex.py create mode 100644 util.py create mode 100644 video_meta.py create mode 100644 wq.py diff --git a/.track.py.swp b/.track.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..2a457262658aecc1488ae33fda05076d411c2a69 GIT binary patch literal 12288 zcmYc?2=nw+u+TGPU|?VnU|{&M$33A}orR$zl7XQlBQ+^Cv8Wg%g%1}L=iyTV zmaBsqs9#W&pI(%hTdeP#pPQSX=bWFCs$Wu+n4GOwPzhE#N{oiUXb4a}1b7*YjSLNx zl@t|(g+i%r`lx}UAut*OqaiRF0;3@?8UmvsFd71*Aut*OgF6ID3K$vwGcYhPF)%O$ zFfcGMLh;~s^{DGcLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3Tqfy5LB zhLubV41bv+^Z%eRfb0AW45#=R7<%~`81nfU7|i$?82I@a7+&!)Fzn@HU|7M&z)-=* zz!1jAz+lD4z#z-Vz#zlN!0?fmf#EJM1H%$t28Lu_1_lRS1_pax1_nD`1_o(f1_m)+ z28KI43=H#l7#Lo0Gcdg5W?%^CW?(SjW?<0gW?<0cW?<0eW?*3BW?*>4#lWzei-BP# z7Xw2w7XyP17XyPf7X!mxP6mecoD2-DoD2*toD2-roD2+AoD2+=oD2++oD2*hoD2-C zoD2+qI2agCaxgHg_>C3{fl$42CQW3B<07nSD4C+C;ul@zNZYzCld&;|!(N@`9?qDF3JPEIDwH?|6yc_kY8X=%l&C7P(_ zWu`%FQBbs1FtCD$6xgjG_bEX1qPZk3zX+mDM*(80LS~*qRc3)kaY<2TUV5~Fm6cJf z4wPdE;%Fkwh6sba2Ue_CT#%DlqM@#X6kIrzSgC798^&70U5TNrq$slhY!XCGN@|)y zL1IyHssc#4@dZ`tu$)zQ!8ku>;nyAV^ z%9C^Qle5b+i&H^CRE*$ZQCgmnnimg>wc?V*+ydk{(}4t|f~`VgQn3b7>}%>MlxO6} zXQqI{36ycN%M**zi=)-cGxF7Abr=+oot>Fi3{wVjdx?5%G}t30iAg!Bv3iiit)Zz` zmY7qTTC9PZguyC{3t_&^P0Xv*Q1|o;b`1(q@bnAuSAeAJV!iy7{M^)%)FK59kbl7L zgF0VF0q%WG1&AdI>N?TY>L7*cRv@24f?WaZd@Hb(s2);K2PZLgE6i|Eha0YLm5B)G z+E_$N&P+qZtwLt8LSBA}f?s}KDk9N=Vhl;Mf~|rfJQ=0t6ysHC09OgJ2PubU7K3e3 z$S*?4xHiZ+39J{C5FxP;5B6p;MuJ2!z!q$*jsm>+QLt5TOUx-wh2|oL_f(fKxTdzaWe)eS^zjh;xwBB5D!`#}Pykwo(--l~bowMNZF< z!WU|JNosCEYEfcIX;CVqtOQjRunY_iU64PKor_(ELUL(QQ9MWvk#68Z3bqPqIr)ht z&;ksh0;7n56jn4TiLgWfC_7^mMU>=ykpH2f35t6}*n;vi?oz5eBef_sJ~PD@9L|WW zn_85r9;-=afr9K7Xqkc}1a?qSDxm@eNr$=>5=Tb?RQeR9s-u@SU?&q(cqk<1r6?eo zh*_{8WosmLSn@U^m{D_Z2too|CWhw;aHwNV3veZ%OaQ{5A{SR(Q=X9sYDPg!1D6-b z*_gyi1Y{Wq7p0bz7Ud~~6qTleb0boqz{@Z6AORIoAiW?Ab`2y&DcCAN7+{0olHh6# zlv+X68Kh>1%N2vY0j~W((vq~ZcpRf83ywsAp~5f&4nb)~IBG)RFCh=>I%0|jC{xXz4^ zhX!78p^gH?w_s1|D1d`SM@tJ5WSUmcS^>2((8y29N=*jU2}KIF3Pq_YnZx{g9Yeo=|7nX$R0jzUV3t)V7EW-dqwqMsqH VC_h&L76uAXDHtEp$^th+833701xWw^ literal 0 HcmV?d00001 diff --git a/__init__.py b/__init__.py new file mode 100755 index 0000000..71d1704 --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +from .data_structures import CircularDict +#from CommonCode import util diff --git a/__pycache__/__init__.cpython-310.pyc b/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..845686d9feb6d3c7972a28fbe9b76839891abba5 GIT binary patch literal 210 zcmd1j<>g{vU|`_ebu-DGfq~&Mh=Yuo7#J8F7#J9e1sE6@QW#Pga~N_NqZk<(Qka4n zG?`yAGB7Y`GT!2G&MZnU%}FeB$xJTs(`1U`Pf09Ej4v)JDorjaElMpe0vWWDp@^A* z0Ydz8)X&JzP1P^SNKHyjEGpIyPEAb9%t_UENiEJy&nwn<&QD3z52!53$j{Sv&d<%w k&jX9Z$7kkcmc+;F6;$5hu*uC&Da}c>1G%CYWP<<$0E2fmBLDyZ literal 0 HcmV?d00001 diff --git a/__pycache__/__init__.cpython-311.pyc b/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..131f6177a13fb3451d02f1fa28600a418ae3ae19 GIT binary patch literal 240 zcmZ3^%ge>Uz`($_>t>QW0|Ucj5C?{tpp4H#3=9m@8B!Qh7;_kM8KW2(8B&;n88n$+ zGBPkQXfod7an3ACF3m|Sa>-0C@zZ3w#h;Q`k{DlHQdF8;Qd*Q+T*S=4z_60xGswVS zj`|t-xvBak8L3IBiABZw!KsO9nK`NYE~&+t>3PNa&iN^+`T>?`U;qHN C$3UR~ literal 0 HcmV?d00001 diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..540fcb2ec133c562136e68a4f210b28edccb7de2 GIT binary patch literal 217 zcmX@j%ge>Uz`($_>t>QW0|Ucj5C?{tpp4G~3=9m@8B!Qh7;_kM8KW2(87i4HnO`z8 zFfeE`-r{l2EJ`lTNi1^7OfK=$WV*$ll30=$UtCgDnp{#^lv-TG%)r2~lHoJRxL=O? z8Tq-X`Xw2uNvVlN#rnaiiD{WRsroLd#hK}O#rn?qDXIDal_eSZdHT-zxw-jyV3GLv v%)HE!_;|g7%3mBdx%nxjIjMF<91IK$AXgNF7$2A!85!>}XcnUz`($_>t>QW0|Ucj5C?{tpp4G~3=9lY8G;#t8NC_27>gJc7}A+EnO`z8 zFfeE`-r{l2EJ`lTNi1^7OfK=$WV*$ll30=$UtCgDnp{#^lv-TG%)r2~lHoJRxLc0; z8Tq-X`Xw2uNvVlN#rnaiiD{WRsroLd#hK}O#rn?qDXIDal_eSZdHT-zxw-jyV3GLv v%)HE!_;|g7%3B;Zx%nxjIjMF<91IK$AXgNF7$2A!85!>}Xcng{vU|=}RFeC9gBLl-@5C<8vFfcGUFfcF_t1vJyq%fo~<}lDOes#SvPZTI7Bv&#N@h~tj{PNJx$j?pHFUd$vN=+;()(=iiOv}tk)ptoP z&P>lM)_2ZNN!1UiEXl~v(|69#&CSmPi-63DFD@x6O)e=dN-fqasJz7y9}l)79uyVD zYzzzxB8)hFuO1`Gcq!O!-&~0nGqJ^3=9lR3=9mc3=9my zpg@;kU|>jRs9}iZsAU8h1hHr$Qz26@Bvdq+ir5(#7;Z5a`CJ0=Bn^10=#+oLZvE0U#}}3+ z=0IeM_(1lFg9wmsA#MP(KyE04MIkumIT*MYd04<=ewr+|xRdj9a#E8^GV}9_i$I|S cPO)HVBxB%a-Quu;IKd7Sg~cF~1sFvb0dd;qVE_OC literal 0 HcmV?d00001 diff --git a/__pycache__/data_structures.cpython-311.pyc b/__pycache__/data_structures.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb0cb222de2b8733bac38ff8e6a719380f6084f2 GIT binary patch literal 1640 zcmZ3^%ge>Uz`$^rVMgM0Mh1q*APx+(LK&Zp7#J9)Go&!2Fy=7iGDb1xGDR^lGB7c? zGo&!JFr+Z2GA?6gU|7uzRnHK`oWc^!pvn3YB%sN3izBo+wa6tix#SjWN@`(gYBES1 zhMAy@&oW?JQW>HcQy8L{Qdm=%TNptmwJ=1nq%a0EXtLenan3ACF3m{<8|L>C{AZFf3zWU|0?3)-c4w6DO(l3gcT@f?uVCmt$AuiX!(! z2r=!zRWUMvW0}b>8SXp=1_n?P5N2Rt z`0T>Kz%Z3@Izu``4MVIA48OQ+ax#lclJj#5?5cFoqP94-B(o$nH$Gm^CMQ2RF{jv0522a|WRyAs z1H+F7h6@~85HusPL!?KtU$#qjLg7VD$t#?a5b+BfT17$(3=EpAMIe2**b)m0Qu9*4 zPAuYPU|_h#T%1~>$pUs@kpxJ9vFsLGNq&4$YHl$oaV01yD1cpji#t9Z?yxEeTu$Zy zIWGm`yc--mJ>1thq%Ltt%`m*kA$x^G_5z3O4RHxj(wv~!$#X+cbc*D3xk+*t1r@Id zDlT%)%#03$cTwH7p&QlEx z5cq+KnN|J+11iD6!m9m&0h0i`3T&L8Cd)1EVjV! zHV`-46)7_?Fn|(jaUcT&!v|(YM#cvW>KCw~4@{hl+8-D&i3t`TL84zk1e!e9fdFSI BVIKeh literal 0 HcmV?d00001 diff --git a/__pycache__/data_structures.cpython-312.pyc b/__pycache__/data_structures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52e80a27e171c8279f8925dc7594d0fd85fd5f89 GIT binary patch literal 1324 zcmX@j%ge>Uz`$^rVMgM0Mh1q*APx+(LK&a67#J9)Go&!2Fy=7iGDb1xGDR^lGB7c? zGo&!JFr+Z2GOlKZsEuN-WYJ`O3F2rn-Qox>PAziDOfI>_nvz;rnwks}hhZite*s;&W2- zG?{O)6sP8--Qvv5%PffpE9YfkU?>K8OhG~6mxq2ver~FMNk(c?YGP5besF4HT4qkF zzDsIxW_n(+zH@#`s(wIaNk)F2zH@$VZhjtE1Y}TraY<2Wa!F}XYO!8Hi*c(5Ju zRiemYss|GWxw6=Zfq|ic;fA8t1x2%oo_kz&yLGViaNZG2R5k^q*sbRgz=x0o3N@qxBij`$xsAa5Sz~-7P zZkVYE8sUqHOg-`}49QHjjEoGG49QH4V0Jk}5eEYUgC<)M4+8_kEw;phg4Dc}B2acL z0vUIUxj41t7IQ&C@3ft34r9dB^TU_z+x%nxjIUqJqe0*VPVh%*6 zNRWYnp$JqStYjz>0-=-u&`=F6oS0;vb*Jh1K}d60>4V}EhjKwM#0q{zU)07`MiA`A=+AD9^#8J{z#e_`Te O)Sh7Rm4TBHVg~@WISa7> literal 0 HcmV?d00001 diff --git a/__pycache__/data_structures.cpython-313.pyc b/__pycache__/data_structures.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b284bffc1b656dbbb36efbbe2d7731fdd0ec30e GIT binary patch literal 1394 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 U;HgsZC>|Rl3lj$u7du}D004bR0{{R3 literal 0 HcmV?d00001 diff --git a/__pycache__/db_conn.cpython-311.pyc b/__pycache__/db_conn.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd1aa54311ee151aae4d596ef5f852123ccfb662 GIT binary patch literal 15183 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 P10$m|;|B(U6gXA@Sq~jn literal 0 HcmV?d00001 diff --git a/__pycache__/db_conn.cpython-312.pyc b/__pycache__/db_conn.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..494cb909fb0aa5bd0b550300038586dd96b02aad GIT binary patch literal 12518 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 U%$Lf@X!|9M*@2PKnGx)20Bx3{k^lez literal 0 HcmV?d00001 diff --git a/__pycache__/db_conn.cpython-313.pyc b/__pycache__/db_conn.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a658b1c624f69e627af8c69fc457a9e6f454b88 GIT binary patch literal 12721 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 literal 0 HcmV?d00001 diff --git a/__pycache__/kwq.cpython-310.pyc b/__pycache__/kwq.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5434fb4f32dd1b4a90ef37ec3a015fed2f738172 GIT binary patch literal 1318 zcmd1j<>g{vU|>-3WX$koVPJR+;vi#I1_lNP1_p-WECvRK6owSW9EM!RC`LvQn<iX(+Rg`LsCZ`rbxz71{#ihBaMO8wEmPUGpW)^yedPXKjR+a{qM#&%(kTE07 zFB%LC45c!>?v#@w?=WMa0D}Ga^7MK@elBH z4!$K>mYI^8UmRbOA77lDUz8f3l3J3QT#}iecS{^91r|?1k`RVUlxLP?#OIZ!r>B+_ z-{OM`=H9ZgIrN z=OyN*#>d~{ijU9DPbtj-v3cU-3riDoATmX43=9lK>>z>zL~w!#P!fPU0&H+fW?nkf zLTO}2AjH9zu4E_@0vP}ze%a_}?Sr6v{?>j$SMre)@&>bs;CXQt;B>pSPC zr0NG$mSp7T={x7==H};tMY78a^$IGBKnaZ-l+Xki7#KJhSQrtIgApv742nNke1h1_ z3=9m;FfAnv3m9t{7BVhiN?}~c7|fu_Ey1Mx{F365qQrvu z;?$zD)S}{BeA%g$AWmjtPG(hV(Jg_p#GKMpB#~s08)0q+u|eStcC#1*149Wz31bOU z2~!GV4Py;c3R4PmFMBY9CX1ijEsoU0qMXds;u1~vB1w?T1VC1$q!y5h&1$L_jWKF0L%T#hMMu_C=tu1G}$C8Dt?S zwcO%D3Jlhq{KS;vA{CH6j)EdcAptfV;x3Tw5Ej^l95#^bVFyY_#WD;G3_Oe+j2w&t WjBLz&%p6>7j7&@rr?UO!V+R23YGH%` literal 0 HcmV?d00001 diff --git a/__pycache__/kwq.cpython-312.pyc b/__pycache__/kwq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cb26c51b63e4193f32da2aba6c253302e211061 GIT binary patch literal 1912 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 H0J{tT3HT?V literal 0 HcmV?d00001 diff --git a/__pycache__/naming.cpython-312.pyc b/__pycache__/naming.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35b73065a6cea1dfc88b25e3f806e392a45571d7 GIT binary patch literal 2195 zcmX@j%ge>Uz`)?#@0DP~%E0g##DQT}DC4sl0|Uc!1||k~h7^Vth7`tBhSkgvxhTdI zrWS@MrWEEDhA8Gr7ERVKfW*K!3nU6=A&44=8phR7-L*_LOu-DAjDDI-x0s7d zif*yT$LFP%$H!|j-(pVADZa&AmY8#kIX@|@7-W)yg2FF*{fzwFRQ-~S)TGqJqGJ8v z)Wo#RoK$_6)Z)zaykdRl{FGGvfXb4L{5*Z<{M_99Jg`V!Vs2($x?VwL5gP*oLzO6s zXY`=9f`YMFoPmL%f#D9HV1IUJ_6+42#h3Y%JD6|q3w3aP2KmBIljRmie0*MFZfbn| zEw1?Z-29Z%91xo)KEALtF$W?8wr3?n5jO(^LlG$Eia;bgU1et9I4kG& zf?RZq7o;67c8e#oC?3vxnZm%paEm8CBe6I>IU_MIJvHSPHxegAll>M;N@j8iI71YH z674Pal+?7u(wvf8tSKdx1*x}~GxJJru_Wc^=V&tD;!MlQ&o7G4E6u&dlAT&ve2clb zJP}qXfb-Wa5l{#~loh3>WERJR)D>wkFff2pLa`nrIA3$~cbhf1JmC^=aDFH$b3x8* zOYsiN&2<;$To2@4lncHr8QS2|A#;&M?1rKmly!wg?vA_?oFjv-FXy6M&}GSx2A2-$ zi!7oy6jY$BD=e~i4&lk7nH2_81JauZFf=0^MY5@MWv|AvM~)V9r71hByXsw zLRnW>6z?b@IPwo=mNGFJOg+=}j*xeA0>;rE825)e=gi_GM zi=+dPf*xAHFfhQ2B&HfhY=u5d-i0C7fq@~0iGiV(xt67dAqy1qpwxtfYgk~#R4r=_ zYXfr%V=Zf*LJdn6sK`W82V&N+W^saORGh*DGXX|}OsZk30T+5bPAm-IYKPhH7K>kg zUTPI z&2)>y22zLF6`3$FFn}VpSek)>;R7=xBjXniW=7!&&R-aq8HGRbNHGe0w&7;v2b&H6 Dg(K~Z literal 0 HcmV?d00001 diff --git a/__pycache__/settings.cpython-310.pyc b/__pycache__/settings.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e81c05dd60d7ea2a0b17105550fbff07c1f348e GIT binary patch literal 1687 zcmd1j<>g{vU|{$dz?iX-je+4Yh=Yt-7#J8F7#J9e8yFZEQW#Pga~Pr^G-DKF3PTE0 z4pT036f+}8j5&uTmo0rNrzMur-e1uP{DS*!~g89}maCF~0sYgua; zQy5Aai}XtvvN&oOQy5d2dYNk3YS`MO7gwiH z_gkE9`9-;jB_*jvx43fh)8o^?+*{mknK`M@SiHqkoLYjUEHNboCc_(CQk0sQ3*#~u zR~Fx5D=taNFD<#n;_2t+U&O?~z@W)_i!~k;S+_Vr(H38jSdvjB!N9<9i@mtAxFj_< zb>nlFYpHV!eXOTU_a> zCGimV@PJZ>8Uq6Z2O}3F472O}SZ#mK_M181=@axro-vM?2?FfcHnBr{MFV`gAr z04Fm!a5Af5Y-TKBtYKC02xib^^wVT10%F&`)P9D;)n+)sQCC>T=DU_`6;D2 zsqyi*c;e#=OB2C`M0|V^JIEZ6FCoFm;^gn+a!ay2u_QSoJ~1yPJ}t8%z96+IH?z1n zGe58RmQZnWeo<jQrA)_`K5e^wg5#TRf?` zNvSEt@tJw?DM^qZ2PHoU3l#W8+6)W~AY2S8$vGH=7(q~s5iG9B?C0jD$?WInrzu#( z4RWS9$h)vKt(OHUMZuZp7JD)%tWr~oK2ckOl7KaTa1nn3>$wLg3+!;6+c^D-ac^G*ZS(v$)xuiLm GMHm63zP={_ literal 0 HcmV?d00001 diff --git a/__pycache__/settings.cpython-312.pyc b/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a68dc1b628dde977489bce152b1d519ec164193 GIT binary patch literal 3007 zcmX@j%ge>Uz`!88VsplFb_RyWAPx+(LK&aeF)%PpXGmd4Va#EOg3ydnj9@-f6jKUA z3R4bqE=v>(BS?-phc%ZiiY=EtianPjiX)dZiW4l(lEanD9mNf1v*xho@?L(r{SW-Az7^C=8cv3i97^4K37*css*jF<`lrSAH2~{#`^1cLd{4^PF@p@|#y5lFg(U@8!F4Py$^8s^o|FsS9I;m8t2$ii4PNU5TR1DiWb zU`iRX#NbRg1Lh(|h8or^Nw_eaQNw|xf~hD4$<0!5MQ}z5JS}I*!3E(Am`SyqHJk+f z!b!j{3UFiL44C;i{lbZ)g1JbiL>ZW1=SGEtcH;lvGWjTkJXc>FJqy>9;u3Q%ijE)6-LnZgFHlvT5-x*5sVj#G+eF z`Ng-`(o;)P^U7{<6sMN>fU?{zPPhD`+{BWS)S_ElIr-`FX<+UxZnw;wRA@%O#Z#PG zf>6fl;_4LYev2zHB?YE{H@KuIH8B^)WiGBPzQtBtl9FFqa*M^&&&|IGlp8g(W~ngtUm=4N1EP(XlRj`|t-xvBak8L3IBiABZw!KsNL1NB`}i!;;n ziuIlIQ&RNBV{lmAAOkQ%m9@ZY|lMs&U1lX;sys_KYu6x4Av_ga!&-ruL~$%6i{5Eby+|crudGy+;wrS zi{e@q**WR+b7$s)Mfk3BNM7WSoS}1t zL;WE;?;S~n>ymmGCH2-6udiBJby?D?gYO21*mVx&iyX=eJg;!*fqC*5IpmkHFXvgv zbA?0q1_#U#y(=7=583(dNNHS_((2&9!6AN~L**ie%7Vx%9Qp`l`d2u#9&igb_B_^j988I+0=z!v0g8>}(-24+by6xtu%&3^H zcad9Th3G|Yy%i-FxsA8DZuj2keUaP#0*if-HUk4gGO}wy&I0j2zW|3wDg&rV5XHpA zkjk9OlERq6l**a~N@5^2NO%qNYLE*V7#O11;c8h3s^x&IWhJPV6RMUag^i$EE~r{| zBGhtMa%ghi;`Yf;ch1krF9J0on2SlqjrS{QCBs9(^qJYaeuDDpyL+V#Zj zi;3A+6ml-G$ljDxxS(jXBlJZ0g_y+aF=-cL(ymCRUtkftDXVfp)$D-ig}|r_N$J;< zaxNz2T#?Paz#?^1Nb-V|_J;Hm>=(jgu7@XH3{Siwl+@sJQ&8f9q}GP81L+q+qppX> zUkr`EBAC$V1#uh5MVichetw$5MWD7u5va;70<|A*!3tBoEJ)K2T#nyjPXt%j?^m*B41co850>N MsC{Js(cnM;04-asvj6}9 literal 0 HcmV?d00001 diff --git a/__pycache__/settings.cpython-313.pyc b/__pycache__/settings.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1be1650cd61b17476f39122389e094618f3cc32c GIT binary patch literal 3069 zcmey&%ge>Uz`!88VsplFb_RyWAPx+(LK&YoF)%PpWe8>nX7pw#g3ydbj9@-f5mPWj zFq1d47fTU~0!WV8o7Ic0h|P<=h~0~$h{KDsh!ZT%;?3p7UBnG$vwE|7@f7hWFa)y| z@dh&nvj?-sFa*g!?L(qWSb{mE7_u0P_=9RgG2?fBXOWi zm>va&Xbz|lLl8I>z<3Z^i6NK~7Rq^Sp=?P^3=E-cc`TtUNrDUvp{zl?5Ov68C<{!7 z0t1rU`H*h&o10uUAh158gIdnjX47z46SA($Ejt;7(@7|ayRY{`_z5y}xHijaY^ zLXlEOCUr6fWe#tP*?Qo*DF6;4b=ak&&!7>QD1h=!-}AUPxn zC=+IC9%m>gL4R=)@RtJAP$UXwK2CpeBB@}8`%8y`AzB$`3@r8JafNabbR%mJw$S53 zvV~QJL6gPr7Ke|&yN_#_tIsXQL`}w9EV=n9shUE!*mLsJ(=+qZZ*iulmiXkSr>7R( z;>dtx;^JGZ$vLTsMYovpi*K=|rSyHVrs|huq$Z^% z78UCUrzV07)OSfO&P>lM)_2ZNN!1UiEXl~v(|69#&CSmPixj7plw{_m7wZ*N-r`D6 zEs2M?u}F)7fq{pCfuUH4k%6Is;SR6JbzZqkymFU$6&l=buyD2eH~L>^k-W$vd4)y# zj)2f~&Pkjzv@Q#%HhA7(7r4kSb3;I~gY^MB&qa2L8ytN7{GI$WSg&x%JrNMUE}(cx zKyiuIWdU87;ydDU*TuCiiEFK}++loC+`fb728YOX4#kTciVHNaaOi+}vKKjI=cLci zotX<3;k(Wud67eMhRziZ^=It7cO(_AOX^*c)LT=$zG`LFWl5_Jz8f53*Ey6gawsqG zyuzUe=E+~=kYB>SoM$1=6%O4S956%lu5f5RW9PdgrEytGtAqarhxm04m5UrI3nH&@ z=p&TrU*XVtz%AI|`x%rWl97vC5F3;nKF5Kp%V=l?#lR2>FGiz5@*s^2p-iw$pv%C( z5XuZD89-z-$Q-bmAT}_CLV#4LFlaLRX|fc7>WU&z0bc}Cc#E|twK%mzljRmmNoqyO zEe=q{7H?!~c8dd4tASappjt1r7-Rz|;S_0t5~(0aFtaK(J~OW*wJ0qyIkm`$fq_8> z6!)Mqpn>5IH~$2VZo4@uGb(25UF6nSA$pNpZ$-&PZsRSk+r2k>U*xvGz+zvd&A`Br zjO<#F+d%x!m%t$s!vJbD6fp%e7BR=LC^5vaf{O7NwjeCkLokaaOA!ZL6)PT9oN!fa zcvNvgRj~%M<59&8RmFi<6;Ci{5pOz|Cig9FpZs*^{G9xv%&OFvhM>@7E=o68ScZ(w) zT$04c-{Ojo&&^LM%}I@qzr_4l)k3yEph6SFTSW?xjuxxgZOQ&QoAqS21f6X6$P60gUkU5rV)D4BkNMeL@m$^})k z1ELoKqb?++Ur)-pn3Qu-HunOH)J-AD3sTw}(oe8o2#>iQo_H}l@uE;tgU?Mti3^fi z8^R8xUkHu59vXi!H2$JsLbDe*nS!0A$?WInrzuC%vZ!_@SXW+Zbpmm=??=pklT?W~^ z42mDv82I8C8@N8OF(|9ADBY2IBK&;p$=ELpGK@lB92glH9T-2dF);FVq+VeV`NG1= Pn8-Lm?JEO_1_uHF{4uGZ literal 0 HcmV?d00001 diff --git a/__pycache__/util.cpython-310.pyc b/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07205e1176a5834599495c3e1f0076ca86f25fa1 GIT binary patch literal 2498 zcmd1j<>g{vU|{gMn4H4K#lY|w#6iZ)3=9ko3=9m#F$@e0DGVu$ISf${nlXwI%x8*X z0@KV<%qa{hOgSvMtWm6t3@Q96EGeulj8SYUY$@z53{mVU0x6s+TrG@I94Xu>JS_}S zoGH91d@T%7Tq%sf44Q&3LH7D-GT!1&Nli;E%_&LAOfLDQsh^Rbo2p-uk(!j6SX8VZ zmYI^8U#uUVnOB^XU#?%ASeBZipPN{eU7XAaG60HM7#JAX7#J9wL2mb8U|=X=s9{K9 zY-XxuEMaVBsAZ~QbYY0)s%5TWTEJAoynv;Kc_HIMMn;B0t`g=f)*2?TdM0s(6y|0o zkWYG<85vR-f*CZ~{BAK8Rk7-273b&OVl00NvS}sbEw;?Ol+?TuO}1Ms`30$Yw^(vA zi%V{?B^DH<=B3dBa*H*sAh9Il7IRu=$}Pr}TZ|Pe8H$7% z7#M!pqWL*EH8CwSCsp4iwKy|9uUOwXKP6Q^pt2+*KTqE|KQ}i&4=hqzl9{7dP){pG0HI&i83%SBtruXOo2iW6yxAfGXljp zV+unKLoGuIV+}(JQ!`VMa0ycib2B3&11QWEGC;zlgeipuBnA#2R=*;UF}FD4<3Wy& zkFU}WD$UEw%u83u%u~qA&(qD!D@iR%OfJbROI66tPf68LNUg{$$;?aF)6+vyjHxg$ zzXVCKCM(F6+{Dbh`1o7w8HvS-B_&0-SPP0W^Ga?p7grYFVgZ?~$r!~BR+N?o4qpf% z!oa|AOBf^{pP3e)mtPW}SpW`FeppCxFmf?+F!C_+FcrywqD2oaT5u;taNQ$vxaE_ za|z1=))KZX_8Nv1))cm0rWE#tOfb0=4k(KQ7B`xlepO6G+Tb{O335>tlMYuAC~Fjf zl0+4Yf`WoV6%$viCT9^RC`ed}Q&Y2Vv6Q6du6Tiif23DAp`c!Y&d3spBZfFU~B<%+G^_Iw*Y?frDNG zq8OYwlRaf@huim0Ni3rt;j4cDZa&;S)7ttbW12bwIn{dIJG1`H?br+BOWBi zo|0LVmzbM+i=((8CleITx0pexm%S*pASW?7^%g5wT$3$|BQvj{v?M+=FNz~2vnU=^ zyhO1Sm*(c)VgVb#oLW(Gi>;)%Br&%DJ*@dqZB_+^un+?S0}mq$BM%b?7z;2;F>){# zS)hlp7y|Wh`L;m7*ok&~{;n4XI_WVNPL`WT<7SVNPL^WT<7W zVRd1Mb+2VBVax)Ruxtw%W0-2$YdNYcN?2>y7qF#(QZ>^8_7v8Ij45p3@a3%GNMSEy zC~^UrU&C0#X~O{4%~8Wr!?utKBqGUB!(qdaCsM;%!&JstcJ z1xk2oxWRnk7^YgmTA>o&8i5)i35I6I6pn0$qW&7*8Uaa$1$;F^puAUDQ&>{NyMTWo zg9}5fPz+P8aIHv)GU%?U|=Xx z2N4<|LK8%2F)%RLfs%a{i(YPl$xBcM(G z3sMqGQZ>b*Sc^(hGK-?vL1`_qs4|K>za%5I2%KMwZ!xAtu_VVAl|->8Lqww3AdDhY zko9ID!kmGDA&NV2#zyNYcu^Xs8z{tkL z$Hc+N!N|cRz{mtn?_7)$j2ui{jAD!|4Qz}oj7(oR_&69@7&(|&7zG#wm;@O47?~QF z{t2*gFp4lT{bykTo2kiI1ga-B8H;2Y7#K9U!DV8R5(5K6krapk#|n3HeojtmGN|w? zhJ-q(!UfkQ;Gzti*TBXhIU7+xa@geNrUz`)>hF*${ei-F-Why%k+P{!vF1_p-d3@HpLj5!QZ5SlTH5zJ?bVgl35 zQOqd}DNH#mxvWvFj0`DUDJ(6FQEVx!EeuiYDcmXSEsRkdDI6^fQJg89EeugyDU87k znmjK-R`_W$-r`P4O-n4zDM`spF8QUYpOK%Ns$Y_knv|MYRIDGCnUb1ctRJ44SDcey zu3wy3mYSlUn^=@xoXiL^0E*ce7#Nrt7#Kdk0=u^aNv?(=g>e}Z1H)>VpBQQxOF(L& zT9+{}Fsz2M85nArY8YJ@Vl``-YnT?mb-<+=7#K=Gc0mQQSfC7s8s=q;3=FH`_M-9_ z8G5u(^s&PAF`>DYsfvMtA%z*$#VIUnSeG#~Fsz2Vn~@=fA(%mv&F>atQ5CCRR&jpb zEynVfAor|fyv3H8my()SqRDoPCBGmw?-ol=W^u_aw#0&h)V!2iEMV2FIr)hx#kW{e zN^=V|S#GhW6(p8q++t44Ou5CFa*MH|7-W`$f`Y;?TeLU{PEAb9%t_UENiEJy&nwn< z&QD3z52!53$j{Sv&d<%w&jX8;mSpDW6;$350a+JckXjU9lwTfSk{=J^7l|=2Fn~g` zIG=%mp@HEp7hgx|gy2s79{mRAyBs{7Y&~oZjyHG(J}@w{Du9U&#v2@*{Ty8!Q$?mo z&2XG3J4N;)hx`=|`3qq5l!c?kvmH;jk^&fUq!iTj z28l-$b6C*R4mhS+{fa=z@fJsXJSgzv)wN>YmwlS?wo zQWbLZQ&M#lQY$h`GV{{)^z={^V=BzcFF{hQ$qKS1H!(9WKK>SaMq+VdNlDQy)`FtU zypmhY#g)akSU@IgGTvebD@sc%2Bmw2B2fkghFiiQx%kYq_`Lj*_{;)uvM5quU|`^9 zU|=W?2FLad7P$*BbeEm4qkKZuMRuht>`EUPSXgcEa`0Z@v%bh-bA`j^0*B3A4(=X~ zPOcuV4lsO*DtiH}rbwKDf#H`PT0+HLcz`oZGAKY{DS!i3g3MzCm3Sz*k`bc>VX9$T z0P-_TH=IskLeIO547f@X)Rd6IT*bw}04{g1nv7n;fXxM^S-9OWMhZp>s$p7yRb>e$ zOb-J?7CbGN@FIzV@+g>F!;r$dh7C0bq_Cs%(Niuids8^Ds076T*d}BGHH;v+MU&I7 zim6B&oF86-;;@QIhpPyry$Ix=Di%=tI1i!4N8(M#i^;;w^&M2b8>F6mKSA~ zq~78vNv$X;FG?&Z0#$-Vd?014prWw&7He`&esQWM%Pp?_q^x*onaP?3Djti3K2HGDeOWna!Fm`l4@|i z!NT6|(d5w)*iqQ%+v3~cdxKx7zoM(6v#O_RhGA!Ig9l7S#6=ddD=cCcSj0dgo=u(| zsTWy9uCRz)U=e|e^k($r&rO+=wL)r#;{`dpi*j~XMK_^{ zA35=&%i=T}p2(4`WaWUiZXL^ zQ&Vm+++vJ>`Tzg_|C(I4nDUEnv4B$jEw;**P0OX71A zOOi9?FG?-QNlZ??#R?YJWV^+YnO9I+5}%oO ziz6koC?4E0VJR-n&Ar6}Hh?*`qU07^NpVSHZb309NI|(-0b056p;}$!%)r2)3d&fU zAQ_9Bx5vESuFGzQ#zk)BE8NPUhJ(-ze!&jTr|g_9w_2qH;+@WkJM6 z5xpxSdYzmdEFH;r1w|%=Oploqvp{P_&_z*$E20M1Ma?dWnr$e)C~AF0)Ov^06;b<( zf(};%9Xi-<@C#3H>#4cGA&r^GKnb5+KJB6{9rv%i{ z1M6mBC;>I(p=>m@yeW*ed?g6yFw}6Ns&HY5^#-R5{#pSPb=(;0%)sgdYlTXrp_VZ) zFw_Xt2!R?+sH#&qvcXDB3`OJkO zRvYXt;aZUz5f_G7PbwLl9xaz`$S!s&s#`=;aoeyaXAiDR_$o)QK)K2DMUH$`f<4i%dX@EkT48h_D6` zpd@*VB`ZHO?-pBeeo;wkN)aeK7x93!ai->#=B5@UmZaWdPE0B;0<{Hiv1XQ}<`&;# zD=kP#EJ@WAyTw{m0xtDI<#u9G+n1tzzc zbK^^Hae;XG@g=FnCAXOK<4bNaX5V5gyTw)(5AtL&D1|~Q5p)vVZYwfpU|;|frl6XH zp9$O_q%>He<~z4QEiG^gZEQj{+{p&p`CF(aUU22SVh4^2jdNXfeC^=B^@j` zgoGzJPEqM#e;_9Nfq{op6ijru-Ib7tc7E^Bv5)^11fMOo)7vd#ybuE@Gyl=QeF=`n%rhOpQS zjS2P>?C+{+ElgXIHo<*{+eH7n+PZ7Z*W0YJ*}<|S?V`5p6>V3L#1#Jp&KE^AE{JG6 z5SLvLeL>vdg1Et5J)@OrYtm+LFL1jkp?z21a83F8npHJBTn?yR)c3ri?>WJH0oxT3 z^%cw)MRb1r_`t--D|$mfWCF{NA3r`w@^FfNm0=K;oFP6@e~NyC_gxXG8P*p?)USxB zH~8KV5Nz;#Dj+@A<)VP%6#>Nt&l|ja4Q_WO6c=b;l+e2(p$F>SX@iMQo(}d2!cXNj zR>)qIH@_lp{(*ssQyWZl`geHEV7|*QG{Ja=@I=ebx}LfPOqckTF7PYe5RjaqJcaMN zfa)ay)dk5H1+=aRXk8G{`tgAkq??C9P-;5=Bz}nX9~hWejlo1GO9x|z;SDkI4-AZ) z#$cku?FNtV1jin)>pZfTcw}c}uTZ-pXLymv=n9X~1u*)+3{uO%z$4t?R}9Jo2eq6m zT~t_(s4%%`uxm0Ff$BX?#v(-q1_n)Ta0jAD9h8t{K?FDlawq5KUz`(Gg`(Jt^Hv_|C5C?{tpp4IH3=9m@8B!Qh7;_k+AT(nXBbd(=#RR6A zqnN=oOB72ALkd$4Yc5+98zVysR|<0rV-$M|OAA93M+$2TLlkEUcM4kzV-!~kdkaGp zcM3-fLljR6XA46VZzZE9&r6V{ewvK8xKmQo5=(PRQZkcEk{LmwP|VK2z`)GF!0`D3 z*jXiTxf+HP#???q*D{uX)PS|D29pd7wM;dPE)2113=Fl*HB4EoP+=sh1Y|atpTz;D zP>34l)lj=&G$TWg1`7j{E^uI?Xkw^gLUAEe6$1l93Ny@|DJ*MPS3_OO$WY0k$>w*9 zv8akwFRM5|?-pbEOBMzOhLw!B*fR4{Qu9hQ*>17q7o_IhV#&!YF1f{)SWu9fmvV~* zteQ0^KQX2F7E4NLZh`0u#G9h$^`NY@-#+L=u zR}@}jG5ib)(_|0|mj^v|><gJ0wUzi^Ql0|NtgCxeo}bOr)W zRw#id&=h#WV?<7DpumO&2b6{-bx=}5($9iOu$2t3U?`GcU|^`y4l2#d%gjqx$jnp7 z%g@uz%qvMPN=z=vEK60$%}+_yQAn-GEXmAE*VEHOQH-fDFTVsyu_kMgBm)COkrXKC zSPP0W^Ga?p7grYFVgVUgBn=8Yq`(yhiN{Wl0pgPI~=?h z_^hvR*xcdZ2BoN8t|wrTs~k3vpa(@YG+4mt5}aBR7(qoDQjTOqDl?dBn6f~sV7j1m z3KJr~GBRKw!d%4#Enl#hgeXD4=Dl3 z`Cu#rUBi&Vx`qvwD^l2DEJX6aZf6PyiV9G|M;Hob!Tb+Oc$%DkRZKVFI%8L>U zia_2i0{Q9|E2v;AzQvlHlV6;w$#RP;KPf97S^~0WfeO1Kkjrjy6yz6YmSpDV!3#V{ z{*{2J1(#OwdHIkspeQvlrAUu~fdQ1(iYvkC3{rH3T;`H$aK6F9-tN)p(Gl2D*y4MG zU#P#Lv!c6dhGA!Ig9l75;tGoxhz%;dQm?RxK-oPR{rR2wb5iDK&CFUMwZrj>oZXJH z%Y1HESlpp1I*?SidP9;eC{KWcl!1YP4OZ1nfh1d)I71dFSm8Vf16zew!vKr8Vx~-n zV1^QiQU(U>CV=7yt^&e9m{7!xNO}lSf@X3<%t0m*x@#Gc%w&YQwU&v1J3+|-*+2#c z285Y0OrQ){%TmG*Q&Pj61xgq&E|gATL{t!2!cajZ3Q_8&uz*T3xa(MJm}*!-BEg`t z^cF{aNl|8QZfeRchFgsBFaQ7l|6h~q7E^xlEf!Fkyv3GUky%_)e2X=+I3=^_mQZ?X zNqll~YDs)_w>sIf==sw^+gAnryc? zGV=;bOX4&0ZgHe!7R7^G0W8I(xw*Gkzy>g6g>|nVgA~xN9qWg6bm5U-O3nDIy=yh^- zuyiEf5fqsaGCgKu%mS?yL6=1hu8W#o6gAsWd|A|bhtow-`^$n39c(xFg(tXm)?DC_ z#>^6+N)c4De!d2-_X#8vkZPnT1u@YQ3S%u3qWuOd#cG)m$r5WzkTHc3REfir1WOHz z3q!0t14Auq4Qm#bB#&H&fDmPl_?9TNqmbXJtse@$d-YDL6hSaJGecQnOEcpQso3z1xjPL zSV0oSw;0oIv8Ba>QwVEXd}>8WF{mVlqzzc=kb7kWWO$;a8W>Ef%s(s%|_1#mk#AS-2B~U4K8G|q2)fXr)NSv*AkwcnV6_OMMhK>!k)cPIg`vlR zg`tKwOCF&D#)7487lv46sQtpVA~hl|46%|747H*)j5WewTNGhB5OfVwjVMSC$u4OY zh8|TGh8o6T24x0DhDe49j6D?<%#jS`jGCN~K2wnjs9-Sw5r!bb2t*h&FfiDG%BWu~ zdbtHAFG0p?3Kn^SrGuQWHcD6u5<7IR`!aS;!wJY&r)NzE<3#a3F7l30?eDRzsss03Vhf@;6S zqRLy``6U^tkbtMXWFEBx{v!sLNhLG?C$B8N(?2pA{JKXL_NX=)T$$nizEw0PjU6i%kQG8j}`GC_!S@+A59uwGZ z2#d|om}q}TO>1%51os(k6aDXK>#jFnX};cOrOghO9ch=fT_NHNoG*!JJP?;%5PebH z;EtZr>a-c$3*0VBXy4H{TwlJje0|NznjJ0&)Gq6LPVip9c2Puq1@lD_ogW`X8N}s2 za5C`8d{JQ#mYg9zS--*ij)>F@>&qhQ4Zb%71RFe`2uROyxh$aA;CX|Wufgq(gyI72 zYZ7{$JRR&4grCT3tdPAXZ{F$O;WdN#4!_U@;~ByeExYR$FkR+Xx*;GrLwO?Kbph3j z0;&s=uL@{=WMxpW_`u2_ssBNMK~QQs|3v<40*ak19gH1@x5dOe+-~p)PjKw?y3Qke zkw5Dl6#d-9qU>T##wtCc6@$luH2I3W zKq&-dZ;=Lwr2rzp*^E0mKPM+O8QkT8WFt^{2Ogs+0(EP^{Tpy=4_uLhQ`t%eaN7!; z_HJ?5qPnh literal 0 HcmV?d00001 diff --git a/__pycache__/util.cpython-313.pyc b/__pycache__/util.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8ed45b15c0f3b431c65ab421bcc7ee99a3b1536 GIT binary patch literal 5272 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 literal 0 HcmV?d00001 diff --git a/__pycache__/video_meta.cpython-312.pyc b/__pycache__/video_meta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2faf677e195351a9b6a6a4531af6c48aaa339ebd GIT binary patch literal 10177 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 literal 0 HcmV?d00001 diff --git a/__pycache__/wq.cpython-310.pyc b/__pycache__/wq.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5610b1fa83076d8bbdb61512daf5bd496349338c GIT binary patch literal 2137 zcmd1j<>g{vU|?v^*`I!ui-F-Wh=Yt-7#J8F7#J9e6Brm6QW#Pga~Pr^G-DKF3PTE0 z4pT036f+}4j3tW2ogsxeg{6fdg(a1_nK_Czm0hRoSa{jdP@vb zEIzfOJU=NbCAB0zEip4EHRYBBhD32`a&l^MF*cd(%z}c{lv_#|dQy_&OAB)H z6I0@0x)t%Lfm)4eOhGzfV-RYf#>ipVR8U%!o(gw~EDm`>+M%A6M`$n0Oi9fzj?c|c z$xN$+n~!8assgA5(g*`mbCXh2QZn<>;hK?xA0Y|ScniBWh@<>8MQ(A#$LA&HrpCwL z;)(}3sWb<~=82ClEKSUT$P|I{MG-3l149uTh+qd191IK$x0DfXf+mNe)Z)^dlH&N3 zB)Ho!;|EC*$c?Efcnm5)coMfk1?jMKsD{%;sIjR^KpoUw1dW3PWRU4 z5h4r>3@aImB6B~?G5 zvLquvPv1E|H#a{IEK**mS5R5R%)r0^3jJag1_lNW1{OyAkb@DdBbf=QL;Ya zE`*D&Wc1VIxW$!OoS9c#l9-pA3bykWYguAWY3eQJ;*z3Uj77Itiwa7MGj6eD73b&O zVofQ{EhyGxy2V>_NCuUTFu#G=aF2nsSFx2aEMTl*SjZU85X`^`hBXWem=-dyF+?&1GZZN@ zFfeE`7pa4+Vg(gA#kbf}Qj_yjQZ*TGG3OSiqq~Q|ghmnnu zhp|W>-A$mt2E`dTu=N-iK!J-I*vt!9KrXChtYJ)HC}k)TPGJaUC}FK(Y-WVHkgZ4w zlzKqs6e)vPsvrlm6cywbfNj)dD$)cAgUm;FF?(`;UU6w|st(AlJPZsB;1FcuU}RzB zU@9`i=3Y=12fNn_r+ZTvQy3RA*D^xfnZlUOR3r{|H(L#33ZoTg*ABdC0!F#Zs1-Q;hBnmV(sOY%^p(@G?g`QEEzN z@h!H3%;fBx)LSg6d8N5Upx_5bFCB4;KK}2}tSy literal 0 HcmV?d00001 diff --git a/__pycache__/wq.cpython-312.pyc b/__pycache__/wq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb909beff196a5aeb92d9d9e0476a09bd37c2a00 GIT binary patch literal 2869 zcmX@j%ge>Uz`(GHV^hXCE(V6jAPx+(LK&Yc7#J9)Go&!2Fy=5sL1@M(#uSDWrW~eR z<|t-Hh!{&03loDoLke>XLkdeO^J->@{wP)^hE#?u9taPaTn*(!v8S-MFhp^ru(dEm zai*}hFhp^saI`Q)aaS^Ga=rwq^3!Cz#p0S*nwt#b!7w9~@i_qO!c+#3tD=}v7^9d| zn4(xxn4?%zSfbccSfkie*rGU6*rPa8IHI^xIHR~zxT1JcxTAPec%t}Hc%%4J_@V?- z_@e|<1fqme1fzs2g*1h4v4!{tcsd8)63WeqFUu^>%+HH2$&W8i&M!*6C59;$pIT9J zOB^AdpOlr7S`wd@n3aYC^AdAYd|I1P6%V1QDQ|bxRrHCTMafN-ZwUDJhOmNrJl#Gk%a1 zf!vsyg2$i&geP$uRFDo!hiW)ogc_Tw1k^#@MbJ1%Ko$vsfw-%xeEz@l_R$^HV1Jk$gw`-?2{ zMM@x#fb36Z0{H`qLGA#Rf1mGwOQsT7NySjZPy$!C8p0XYs*U2)c%GHPjc#X0BxP)8x3t zm06sbS6q^qmz)ZYhFh#Fx+AXJBBEVPIe=c4J^*XkfU* zE;vDTj@T7;g&P8*)43;dcd&k8W8e^)pgza=3cLIr4$gk|PWB0`6Uwi0$UlMcrj$&t znpm|!{j#{$RSs=%oFzj&1NI;&@WIg)1CB0Eq=3%?xeBZZLDVq7f+CnfnSqg^h9L`- z0uTzoELioH%oNEG%uvA`$xzN%q{_g+pvhdM4e}!^s2nf8#g>wqoS%}a$#{!7w>Z5R z7oEfq_9C6cifZp!mSV%qnw-U%0=jvucKNcl`wpxgQ^x7+Ga* z@C$*&x@#_RNdGDZC6Oi(afaza+RWHJ-2nD9Qd%P|jl$Dq7AM%>CXyLng&z~V+tdz(NxQbEtpamvo#nPia>z~&#E#rfdQ1Eik%o37(OsFGBQ4A5V^}BdYgg!3yUD517kUz`)R+vp@YR7X!m%5C?`?p^VQJ3=9lY8G;#t8NC^bAT(nUV=zN7lQ)wW za}l!wM2w|~MS?+zA(&Z;A($nG*_0Wgzlb%4A&3XUMkbXQOrhK&_Fz^ih9Zt&HYtW8 z&R}*ah9a(D4k?Br?sP^?&X*unewvK8SX}c;bCW?l7-obrK1YCE7{dT^RS{D#V-a&O zQxQura}jGWOA%WzYY}@eTM$9NGxPJ}OY-B3 zlkH= zDXICz@wxdanQ4`9^O5XFRRFa>8eu?cZc=JWN@iXZZ;WH@z-?G-v$j?pHFUd$vN=+;()(=iiOv}tk)ptoP&P>lM)_2ZNN!1UiEXl~v z(|69#&CSmPim3=9ly3^ycoFR+N+kTt!)B6UN~`~r*g4SCB8 zEHY41%kKh<`VCE=3oL3kklDJv7g)4z=y+dX(Yyg+tGQobQNE$-dVxjhhLZgS7I~-% zO7@po~kFA_avaHBcI2FDOmQ$t=z&GGbt0 zkY-?DDE4AtU}#{t!!9^Mb&l9|c7==V3O58qr*lu_?qL1G#=s#oL4A(#b$0oS?DBUw zIQ!W<*(b1G<&b{@V^1lWUNxy|f%;`}t*ac`;J8eNdI;=EP*i}UF%BG!atzU6VGt1n zav6w$gq0XV8DIgT!l1~Y0E$jf+CWkRV#2D-VkUV86$V>od4@E`B2X&OWG>PJ`IHq@ z9TeYUOG!=6Pf68eyv3YboL&ra88j$(3lfWpQ{$nbRAkP;zyJ!OVvr#X3?G=7S!M3< z3-?!bRn1WDuD`$`_u~T-Bdg2}exd%V&Z_R3OB~X_ia`mbNmPPihL8?3c5nB9y^WOi z$W6WQ6dMF8IYD87gb{(3#~8{8a!EQvG(7Dh(wGVZB#_vOG+~*pNDIW$0fiY$Q9*tI zI3zTgiVQ%)W}qYjiY9mhO3u$KF3n9XvSMIh0BI_=z>|QUaB=smb*fDe?bdB@zQNAb zSbc+E_zAzvjNr@siWfK(F_Q}@s=>)+9i750m@$|U)}3j8+j042g^kpxn+2 z3-nONU`CK*5s6ik$*%|$RGREq1D7qQC^0WR^%iqZY94YJ-(o3C%qd36r7Q)hso6#L z3=9n5@OQ@-!rVOl7M&IoTxTfG2)xWK-{5kGovXv7xpIPIa~(9WLl!8`48P2;dVxa~ z9N?Nfw^)l(Q!+|3LEa=lA_Gyk|2=EB2ZL=3k|T- z!Lbi^Ah`Mgn+hplIBatBQ%ZAE?TTC&7#Kj=t=NHqf#Cx)BO~K;29diAqPH2izpw~0 zIxu#Gd|?35AJiCFxvsN_USbiw%p%^v_=$ysQRkBw3!}^@9u7vO&mtU*YG4}xnC;%I literal 0 HcmV?d00001 diff --git a/__pycache__/wq.cpython-313.pyc b/__pycache__/wq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20232564a0b5797b862cb1a41fb8cc89b63ce000 GIT binary patch literal 2981 zcmey&%ge>Uz`(GHV^hXCE(V6jAPx+(LK&Yc7#J9)G6XXOGkP-=L1@Mz#$bkECT}J$ z<|1YVh!{%|iv)ubLol-xLoiDWvnew~e-Ud8Ll6&yjZ7*rm_oTl?7^&33`HElY*GwG zoWbl;3`JbQ98wHL-06&(oG(GD{4^PFvAE`y<|c!9Fw6*Le2xISFopr-sv@Rf#v)9-6_ggGr@~z#i$k7}cBp6N5!%Z# zQ&RJb<8$*U#}}3+=0IimLE$0-B1A!i7>E!Dg^*}`d~r!)NoI0RTMYH@sg zkpu$+!)H+bzh$kTk)NBYUy_lUl$uyntRI}3n3kE7s_&9ooSB|itnZwklByq2S(1^T zr|+Dfo132p7AY^(E2u026>?w#l(&mB7#JAZ7;Z@FUSJWsA!~YpMe2r}`2`l~8}gPH zSY)82mfr;y^&6T#7g*G8AhUISFR*Cc(DA;&qImvEb>qj zlj@VwfPtzz_{epkUb`P}&5u5JUtcOpgLXIsvtz4AJlsKZp-u28^Y| z5Xxu@^%=6MD;fPXIc{-f7H8%amn7yTr-GyA7He5zPHE~b=Hil~TZ~1wSc?iui!*Mq zWEJP<-C|8C%`GU_WV*##l3$RSe2YCdwYWGjJ+&AVL<$N;YM?a4UQn8plUbZmWW>P0 zAj81GQ0&FPz|g>Ohh1=j>Kw7_>bQKPUoJ;-NE{Wje$dGg8Cfe>+JFu+2!wW zaQ3rzvQJ>W$|3&*#-36#y=qd`0`<${T30!=!Eu=k^$^&Tpr`;xV;ndds+ytPU4MZ??#BlvMpl^{{6hUzomJg6mpG(<6@wB=lc)s43?Ut6?B4DHdmAb3 zk(+wqDK-dHa)QDD2_ph6k1>=H_Bk!12z>>z;M{)=BJeAq}mm^FfcHHvRknO0|UbcW=2NF=L{lu8ANY0aDQPD zWOQKc2>HSQqCaRbuyS2z5xvABdYMJMf$F&0LdPdprqN}okI7}dZw001)9 B-ev#* literal 0 HcmV?d00001 diff --git a/baseObjects.py b/baseObjects.py new file mode 100755 index 0000000..417ef57 --- /dev/null +++ b/baseObjects.py @@ -0,0 +1,15 @@ +from datetime import datetime, timedelta + +class BaseSensorPost(object): + def __init__(self, sq, sensor_type): + self.last_values = [None,None] + self.last_measurement = None + self.last_insert = datetime.now() + self.sq = sq + self.heartbeat_interval = 15*60 + self.sensor = sensor_type + + def insert_heartbeat(self, time_init, sensor): + ins = self.sq['t']['heartbeats'].insert().values( + when = time_init, what = sensor) + self.sq['s'].execute(ins) diff --git a/data_structures.py b/data_structures.py new file mode 100644 index 0000000..63d8232 --- /dev/null +++ b/data_structures.py @@ -0,0 +1,13 @@ + +from collections import UserDict, deque +class CircularDict(UserDict): + def __init__(self,init_data = None, max_len = 50): + self.data = init_data if init_data is not None else dict() + self.fkkeys = deque(self.data.keys(), max_len) + self.max_len = max_len + def __setitem__(self, k,v): + self.fkkeys.append(k) + self.data[k] = v + to_rem = self.data.keys() - set(self.fkkeys) + [self.data.pop(k) for k in to_rem] + diff --git a/db_conn.py b/db_conn.py new file mode 100755 index 0000000..6fd6c9a --- /dev/null +++ b/db_conn.py @@ -0,0 +1,356 @@ + + +#tables_meta = ['motionlogs', 'floor2temperature', 'horizontalwheel', 'verticalwheel','heartbeats', 'activities','food_weight','weight','daily_counts','hourly_temperature','food_dispenser', 'food_forager', 'touch_surface_grid'] +import inspect +import clickhouse_driver as ch +from functools import partial +import json + + + +# %% + +c_to_ch_dtype_map = {'uint8': 'UInt8', 'uint16':'UInt16', 'float32': 'Float32','datetime':'DateTime', 'point':'Point','str':'String', 'float':'Float32','int':'Int16'} +class db_api(): + def __init__(self): + host = '192.168.1.242' + host = update_host(do_loopback, host) + self.conn = ch.connect('clickhouse://'+host); + self.client = ch.Client(host=host) + self.cursor = self.conn.cursor() + kff = ['altitude','humidity','pressure','temperature','weight','hydroponics_ph','hydroponics_ec','hydroponics_rtd'] + + for kv in kff: + func_name = 'insert_'+kv + func_set = partial(self.generic_when_where, first_key = kv) + setattr(self, func_name, func_set) + + + def get_query_time_filtered(self, table, time_start, time_end): + cquery = f"select * from {table} where when > '{str(time_start)}' and when < '{str(time_end)}' order by when asc" + return cquery + + + def expand_to_list(self, vals): #dict of lists -> list of dicts + max_len = 1 + for k,v in vals.items(): + if isinstance(v,list): + max_len = max(max_len, len(v)) + + output = list() + for i in range(max_len): + output.append(dict()) + + for k,v in vals.items(): + if isinstance(v, list): + for idx, val in enumerate(v): + output[idx][k] = val + else: + for f in output: + f[k] = v + + return output + + def get_insert_statement(self,table_name, keys): + kvars = ', '.join(keys) + return f'INSERT INTO {table_name} ({kvars}) values ' + + def insert(self, dd, table, schema = None): + if schema is not None: + table = schema + '.' + table + + if isinstance(dd, dict): + insert_this = self.expand_to_list( dd) + else: + insert_this = dd + + keys = insert_this[0].keys() + insert_statement = self.get_insert_statement(table,keys) + + self.cursor.executemany(insert_statement, insert_this) + + + def query(self, query, no_results = False, **kwargs): + self.cursor.execute(query, **kwargs) + if no_results: + return None + else: + return self.cursor.fetchall() + def generic_when_where(self, v_in , when = None, where = None, first_key = None): + keys = [first_key,'where','when'] + table = 'nuggets.'+first_key + cb = locals() + cb[first_key] = v_in + dd = {x:cb[x] for x in keys} + + self.insert(dd, table) + + + def get_table_create_statement(self, schema, table_name, dtypes, insert_when = False, nullable = set()): + + entries = list() + if insert_when: + entries.append('`when` DateTime64(3)') + + for field_name, data_type in dtypes.items(): + ch_dtype = c_to_ch_dtype_map[data_type] + if field_name in nullable: + ch_dtype = 'Nullable('+ch_dtype+')' + entries.append( f' `{field_name}` {ch_dtype}') + + + dtype_text = ',\n'.join(entries) + + create_header = f'create table {schema}.{table_name} ' + + + create_footer = f' \nENGINE = ReplacingMergeTree\nORDER BY when\nSETTINGS index_granularity = 8192' + create_statement = create_header + '\n(\n' + dtype_text + '\n)\n' + create_footer + + return create_statement + + def check_if_table_exists(self, schema, table_name): + query = f'''SELECT count(*) as num_tables + FROM information_schema.tables where table_schema == '{schema}' and table_name == '{table_name}' ''' + + return self.query(query)[0][0]== 1 + + + def get_table_contents(self, table, make_map = False, make_inv_map = False): + query = f'select * from {table}' + self.cursor.execute(query); + results = self.cursor.fetchall() + + if make_map: + cm = dict() + + for f in results: + cm[f[0]] = f[1] + return cm + + if make_inv_map: + cm = dict() + for f in results: + cm[f[1]] = f[0] + return cm + + + return results + + + + # def insert_temperature(self, temperature, when = None, where = None): + # keys = ['temperature','who','when'] + # table = 'nuggets.temperature' + # cb = locals() + # dd = {x:cb[x] for x in keys} + # self.insert(dd, table, keys) + + + # def insert_pressure(self, pressure, when = None, where = None): + # keys = ['temperature','who','when'] + # table = 'nuggets.pressure' + # cb = locals() + # dd = {x:cb[x] for x in keys} + # self.insert(dd, table, keys) + + # def insert_weight(self, weight, when=None, where=None): + # keys = ['weight','who','when'] + # table = 'nuggets.weight' + # cb = locals() + # dd = {x:cb[x] for x in keys} + # self.insert(dd, table, keys) +from sqlalchemy.dialects import postgresql +from sqlalchemy.engine import create_engine +from sqlalchemy.schema import MetaData +from sqlalchemy.orm import Session +import os +import json +import socket +if socket.gethostname() == 'tree': + do_loopback = True +else: + do_loopback = False + +def update_host(do_loopback, ip_addr): + if do_loopback and ip_addr=='192.168.1.242': +# print('Updating host to be 127.0.0.1') + return '127.0.0.1' + else: + return ip_addr + +def connect( user='postgres', password='', db='nuggets', host='192.168.1.242', port=5432, echo = False): + + host = update_host(do_loopback, host) + + + from sqlalchemy.engine import create_engine + from sqlalchemy.schema import MetaData + from sqlalchemy.orm import Session + import os + import json + + if False: + + user='postgres' + password='' + db='nuggets' + host='192.168.1.242' + port=5432 + echo = False + + url = 'postgresql://{}:{}@{}:{}/{}' + url = url.format(user, password, host, port, db) + + conn = create_engine(url, connect_args={"application_name":"python_commoncode"}, echo = echo, future=True) + if db == 'nuggets': + schemas =['public','health','videos'] + elif db == 'winslow': + schemas=['body','notes','sensors','video'] + + + + metadata = MetaData() + for schema in schemas: + metadata.reflect(conn, schema,views=True) + + + session = Session(conn) + + tables = {k.split('.')[-1]:v for k,v in metadata.tables.items()} + + from sqlalchemy import text + + def execute_sub(query_in, has_data = None): + + if has_data is not None: + with conn.connect() as cox: + result = cox.execute(query_in, has_data) + try: + cox.commit() + except: + pass + + if hasattr(query_in, 'compile'): + + + compiled = query_in.compile(dialect=postgresql.dialect()) + + with conn.connect() as cox: + result = cox.execute(query_in, compiled.params) + try: + cox.commit() + except: + pass + + + + else: + with conn.connect() as cox: + result = cox.execute(text(query_in)) + try: + cox.commit() + except: + pass + + return result + + setattr(conn, 'execute',execute_sub) + + +# with conn.connect() as ce: + if True: + ce = conn + pid = ce.execute('select pg_backend_pid()').fetchall()[0][0] + if not os.path.exists('/dev/shm/pg/'): + try: + os.mkdir('/dev/shm/pg') + except: + pass +# os.chmod('/dev/shm/pg',0o777) + +# fname = '/dev/shm/pg/'+str(pid) + + details = list() + for x in inspect.stack(): + details.append({'filename':x.filename,'function':x.function,'lineno':x.lineno}) + + # with open(fname,'w') as ff: + # json.dump(details, ff, indent=4, sort_keys = False) +# os.chmod(fname,0o777) +# %% + + return {'s':session, 't':tables, 'c':conn, 'm':metadata} + + + + + +def old_connect( user='postgres', password='', db='nuggets', host='192.168.1.242', port=5432): + + if False: + user = 'postgres' + password='' + db='winslow' + host='192.168.1.242' + port=5432 + import time + + + + from sqlalchemy import and_, func, Table, MetaData, create_engine, inspect + from sqlalchemy.orm import Session, load_only + from sqlalchemy.ext.automap import automap_base + from sqlalchemy.pool import NullPool + + url = 'postgresql://{}:{}@{}:{}/{}' + url = url.format(user, password, host, port, db) + + + conn = create_engine(url, client_encoding='utf8',poolclass=NullPool, future=True) + + + def get_tables_in_schema(schema_name): + + + output = conn.execute("SELECT table_name FROM information_schema.tables WHERE table_schema='"+schema_name+"'").fetchall() + return [x[0] for x in output] + + + schemas = [x.strip() for x in conn.execute('show search_path').fetchall()[0][0].split(',')] + + + tables_meta = list() + for schema in schemas: + tables_meta.extend(get_tables_in_schema(schema)) + + + materialized_tables = [x[0] for x in conn.execute('select matviewname from pg_matviews')] + tables_meta.extend(materialized_tables) + + meta = MetaData(bind=conn)#, reflect = True) + meta.reflect(conn) + + + base = automap_base() + base.prepare(conn, reflect=True) + + session = Session(conn) + tables = dict() + + + + for table in tables_meta: + try: + tables[table] = Table(table, meta, #MetaData(), + autoload=False, autoload_with=conn) + except: + print(table, 'broke') + pass + + + + return {'s':session, 't':tables, 'c':conn, 'm':meta, 'b':base} + + + diff --git a/distanceTracker.py b/distanceTracker.py new file mode 100755 index 0000000..f324d3f --- /dev/null +++ b/distanceTracker.py @@ -0,0 +1,103 @@ +import sys +import serial +import time +import math +import traceback +from subprocess import check_output, call +import syslog +from db_conn import connect +import dateutil.parser +from datetime import timedelta, datetime +sys.path.append('/home/thebears/Nextcloud/Designs/NuggetTracker/CommonCode/') +from track import Temperature, VerticalWheel, ActivityLogger, Weight, BaseSensorPost, HorizontalWheel +import os +from sqlalchemy import func +import syslog +import redis + + + + +do_break = False +while True: + try: + sq = connect() + do_break = True; + except: + syslog.syslog("Failed to connect, waiting 1 second and trying again") + time.sleep(1) + + if do_break: + break + + +curr_time = datetime.now() +last_date = datetime(curr_time.year, curr_time.month, curr_time.day, 18, 0, 0) + +if last_date > curr_time: + curr_time = datetime.now() - timedelta(days=1) + last_date = datetime(curr_time.year, curr_time.month, curr_time.day, 18, 0, 0) + +r = redis.StrictRedis(host='192.168.1.242', port=6379, db=1) + + +os.makedirs('/dev/shm/winslow/', exist_ok=True) + +call(['chmod','777','/dev/shm/winslow/']) + + +res = sq['s'].query(func.count(sq['t']['horizontalwheel'].c.transittime)).filter(sq['t']['horizontalwheel'].c.timestamp > last_date).all() +total_counts = res[0][0] + +r.set('nugget_run_counts', total_counts) + + + + +#1500 turns is 1/4 mile + +threshold_for_next_mealie = 1500 +flipped_stuff = list() + +bins_per_day = 18 + + +last_iter = min([int(math.floor(total_counts / threshold_for_next_mealie)),bins_per_day]) + +while True: + do_rotate = False + next_date = last_date + timedelta(days = 1) + + if datetime.now() > next_date: + if next_date not in flipped_stuff: + do_rotate = True + + if do_rotate is True: + curr_time = datetime.now() + last_date = datetime(curr_time.year, curr_time.month, curr_time.day, 18, 0, 0) + flipped_stuff.append(last_date) + syslog.syslog('Resetting time to '+str(last_date)) + last_iter = 0 + r.set('nugget_run_counts', 0) + + + + +# res = sq['s'].query(func.count(sq['t']['horizontalwheel'].c.transittime)).filter(sq['t']['horizontalwheel'].c.timestamp > last_date).all() +# total_counts = res[0][0] + + total_counts = int(r.get('nugget_run_counts')) + curr_iter = min([int(math.floor(total_counts / threshold_for_next_mealie)),bins_per_day]) + syslog.syslog('Total: '+str(total_counts)+' Current bin: '+str(curr_iter)+ ' Redis Count:' + str(total_counts)) + + if curr_iter != last_iter: + last_iter = curr_iter + open('/dev/shm/winslow/mealies2_open.txt','w').close() + call(['chmod','777','/dev/shm/winslow']) + syslog.syslog("OPENING MEALIES") + + + + + + time.sleep(5) diff --git a/frozen b/frozen new file mode 100644 index 0000000..23f6c5d --- /dev/null +++ b/frozen @@ -0,0 +1,73 @@ +annotated-types==0.7.0 +anyio==4.10.0 +asttokens==3.0.0 +bounded-pool-executor==0.0.3 +certifi==2025.8.3 +charset-normalizer==3.4.3 +click==8.2.1 +colored==2.3.1 +decorator==5.2.1 +dnspython==2.7.0 +email-validator==2.3.0 +executing==2.2.1 +fastapi==0.116.1 +fastapi-cli==0.0.10 +fastapi-cloud-cli==0.1.5 +fastapi-server-session==0.0.1 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +ipython==9.5.0 +ipython_pygments_lexers==1.1.1 +jedi==0.19.2 +Jinja2==3.1.6 +kafka-python==2.2.15 +lttb==0.3.2 +lxml==6.0.1 +markdown-it-py==4.0.0 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +mdurl==0.1.2 +## !! Could not determine repository location +-e /home/thebears/Seafile/Designs/Code/Python +numpy==1.26.4 +parso==0.8.5 +pexpect==4.9.0 +pqdm==0.2.0 +prompt_toolkit==3.0.52 +psutil==7.0.0 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pydantic==2.11.7 +pydantic_core==2.33.2 +Pygments==2.19.2 +pystemd==0.13.4 +python-dotenv==1.1.1 +python-multipart==0.0.20 +python-systemd==0.0.9 +PyYAML==6.0.2 +redis==6.4.0 +redis-cli==1.0.1 +requests==2.32.5 +rich==14.1.0 +rich-toolkit==0.15.0 +rignore==0.6.4 +sentry-sdk==2.35.2 +shellingham==1.5.4 +sniffio==1.3.1 +stack-data==0.6.3 +starlette==0.47.3 +systemd-python @ git+https://github.com/systemd/python-systemd.git@903142423452c4dd18110b7f8a953dabb2031e49 +tqdm==4.67.1 +traitlets==5.14.3 +typer==0.17.3 +typing-inspection==0.4.1 +typing_extensions==4.15.0 +urllib3==2.5.0 +uvicorn==0.35.0 +uvloop==0.21.0 +watchfiles==1.1.0 +wcwidth==0.2.13 +websockets==15.0.1 diff --git a/get_amts.py b/get_amts.py new file mode 100755 index 0000000..0d9d528 --- /dev/null +++ b/get_amts.py @@ -0,0 +1,15 @@ +from db_conn import connect +from sqlalchemy import func +sys.path.append('/home/thebears/Nextcloud/Designs/NuggetTracker/CommonCode/') + +sq = connect() + +curr_time = datetime.now() +last_date = datetime(curr_time.year, curr_time.month, curr_time.day, 18, 0, 0) + + +last_beat = {i:last_date for i in range(4)} +threshold_for_next_mealie = 25 +min_time = 10; + +res = sq['s'].query(func.count(sq['t']['horizontalwheel'].c.transittime)).filter(sq['t']['horizontalwheel'].c.timestamp > last_date).all() diff --git a/kwq.py b/kwq.py new file mode 100644 index 0000000..b748a2a --- /dev/null +++ b/kwq.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +from enum import Enum +import json +import sys +from kafka import KafkaProducer, KafkaConsumer + + +bootstrap_server = ["192.168.1.242:19092"] + + +class TOPICS(): + videos_to_score_detection = "videos_to_score_detection" + videos_scored_detection="videos_scored_detection" + videos_with_nuggets="videos_with_nuggets" + videos_no_json="videos_no_json" + videos_without_nuggets="videos_without_nuggets" + videos_embedding_in_db="videos_embed_in_db" + videos_embedding_in_db_fail = "videos_embed_in_db_fail" + + +serializer = lambda v: json.dumps(v).encode("utf-8") +deserializer = json.loads + +producer = KafkaProducer( + bootstrap_servers=bootstrap_server, + key_serializer=serializer, + value_serializer=serializer, + request_timeout_ms=15000, # 15s (keep small) + max_block_ms=10000, # 10s max blocking + metadata_max_age_ms=300000, + retry_backoff_ms=100, + linger_ms=5, + retries=3, + security_protocol='SASL_PLAINTEXT', # change to SASL_SSL if TLS is enabled + sasl_mechanism='SCRAM-SHA-256', + sasl_plain_username='superuser', + sasl_plain_password='marybear' +) + + +def create_consumer(group_id = None, client_id = None): + return KafkaConsumer( + bootstrap_servers=bootstrap_server, + key_deserializer=deserializer, + value_deserializer=deserializer, + enable_auto_commit = False, + group_id=group_id, + client_id = client_id, + auto_offset_reset = 'earliest', + security_protocol='SASL_PLAINTEXT', # change to SASL_SSL if TLS is enabled + sasl_mechanism='SCRAM-SHA-256', + sasl_plain_username='superuser', + sasl_plain_password='marybear' + ) diff --git a/kwq_test.py b/kwq_test.py new file mode 100644 index 0000000..f482a67 --- /dev/null +++ b/kwq_test.py @@ -0,0 +1,6 @@ +from CommonCode import kwq +topic = 'whatsup' +kwq.producer.send("hello",'world') +for x in range(100): + kwq.producer.send(topic, 'num_'+str(x)) + diff --git a/naming.py b/naming.py new file mode 100755 index 0000000..1b00376 --- /dev/null +++ b/naming.py @@ -0,0 +1,54 @@ + + + +class str_with_attr(str): + def __new__(cls, val): + obj = str.__new__(cls, val) + return obj + + +def get_floor_redis_keys(floor_num): + keys = dict() + + swa = str_with_attr + + keys['vis_status'] = swa( 'nugget_house_lighting_'+str(floor_num)+'_vis_status' ) + keys['vis_status'].default = 'off' + keys['vis_status'].dtype = str + + keys['ir_status'] = swa( 'nugget_house_lighting_'+str(floor_num)+'_ir_status' ) + keys['ir_status'].default = 'on' + keys['ir_status'].dtype = str + + keys['vis_intensity'] = swa( 'nugget_house_lighting_'+str(floor_num)+'_vis_intensity' ) + keys['vis_intensity'].default = 0 + keys['vis_intensity'].dtype = int + + keys['ir_intensity'] = swa( 'nugget_house_lighting_'+str(floor_num)+'_ir_intensity') + keys['ir_intensity'].default = 100 + keys['ir_intensity'].dtype = int + + + keys['has_changed'] = swa('nugget_house_lighting_'+str(floor_num)+'_has_changed') + keys['has_changed'].default = True + keys['has_changed'].dtype = bool + + return keys + + + +def get_redis_values(r, keys): + out = dict() + for k,v in keys.items(): + redis_val = r.get(v) + if redis_val is None: + redis_val = 'None' + else: + redis_val = v.dtype(redis_val.decode('UTF-8')) + + out[k] = redis_val + + return out + + + diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..f718d22 --- /dev/null +++ b/settings.py @@ -0,0 +1,54 @@ +import logging +import sys, os +from systemd.journal import JournalHandler +from colored import stylize, fore, Style, back +from functools import partial +from colored import set_tty_aware + + +log_format = '%(asctime)s|%(levelname)s|%(filename)s⮞%(funcName)s⮞%(lineno)d|%(message)s' + + +def get_logger(_name, file_path = None, stdout=False, systemd=False, level = logging.DEBUG): + logger = logging.getLogger() + logger.handlers.clear() + env_level = os.getenv("LOGLEVEL") + if env_level is not None: + level = env_level + + if level is not None: + logger.setLevel(level) + + formatter = logging.Formatter(log_format) + if file_path is not None: + file_handler = logging.FileHandler(file_path, mode='a') + file_handler.setFormatter(formatter) + file_handler.setLevel(logging.DEBUG) + logger.addHandler(file_handler) + + if stdout: + stdout_handler = logging.StreamHandler(sys.stdout) + stdout_handler.setFormatter(formatter) + stdout_handler.setLevel(logging.INFO) + logger.addHandler(stdout_handler) + + if systemd: + systemd_handler = JournalHandler() + systemd_handler.setFormatter(formatter) + logger.addHandler(systemd_handler) + + return logger + + + +def stylize_interface(text, fore_256=None, back_256=None, style=None): + return fore(fore_256)+back(back_256)+style+text+Style.reset + +set_tty_aware(False) + +class LogColorize(object): + watch_and_fix_permissions = partial(stylize_interface,fore_256='red',back_256='white', style=Style.BOLD) + score_obj_det_orin = partial(stylize_interface, fore_256='red', back_256='green', style=Style.BOLD) + remove_without_nuggets = partial(stylize_interface, fore_256='red', back_256='grey_78', style=Style.BOLD) + embeds_in_db =partial(stylize_interface, fore_256='green', back_256='black', style=Style.BOLD) + video_meta = partial(stylize_interface, fore_256='blue', back_256='black', style=Style.BOLD) diff --git a/takeSnapshot.py b/takeSnapshot.py new file mode 100755 index 0000000..3d064e4 --- /dev/null +++ b/takeSnapshot.py @@ -0,0 +1,37 @@ +import os +from datetime import datetime, timedelta +import cv2 +import time + + +while True: + + + cdate = datetime.now() + try: + base_path = cdate.strftime('/home/thebears/Videos/Winslow/%Y%m%d/snapshots/') + prestr = base_path + cdate.strftime('%Y%m%d_%H%M%S') + + if not os.path.exists(base_path): + os.makedirs(base_path) + + + video_obj = dict() + video_obj[1] = cv2.VideoCapture('http://localhost:6082/frame.mjpg') + video_obj[2] = cv2.VideoCapture('http://localhost:6083/frame.mjpg') + video_obj[3] = cv2.VideoCapture('http://localhost:6084/frame.mjpg') + video_obj[4] = cv2.VideoCapture('http://localhost:6085/frame.mjpg') + video_obj[5] = cv2.VideoCapture('http://localhost:6086/frame.mjpg') + + + for idx, obj in video_obj.items(): + filepath = prestr + '_' + str(idx) +'.jpg' + print(filepath) + (success, image) = obj.read() + cv2.imwrite(filepath, image) + obj.release() + except Exception as E: + print(E) + + + time.sleep(15) diff --git a/test_dbconn.py b/test_dbconn.py new file mode 100644 index 0000000..d2cc374 --- /dev/null +++ b/test_dbconn.py @@ -0,0 +1 @@ +from CommonCode.db_conn import connect, db_api diff --git a/track.py b/track.py new file mode 100755 index 0000000..13e472c --- /dev/null +++ b/track.py @@ -0,0 +1,81 @@ +from datetime import datetime, timedelta +import redis +r = redis.StrictRedis(host='192.168.1.242', port=6379, db=1) +class BaseSensorPost(object): + def __init__(self, sq, sensor_type, table,**kwargs): + self.last_values = [None,None] + self.last_measurement = None + self.last_insert = datetime.utcnow() + self.sq = sq + self.heartbeat_interval = 15*60 + self.sensor = sensor_type + self.table = table; + self.kwargs = kwargs + + def insert_heartbeat(self, time_init): + return True +# ins = self.sq['t'][self.table].insert().values( +# when_timestamp = time_init, what = self.sensor) +# self.sq['s'].execute(ins) + + +class Temperature(BaseSensorPost): + + def insert_temperature(self, time_init, temperature): + if time_init is not None and temperature is not None: + self.sq.executemany('INSERT INTO nuggets.temperature (when, temperature, where) values ',[{'when':time_init, 'temperature':temperature, 'where':self.kwargs['where']}]) + + # ins = self.sq['t'][self.table].insert().values(temperature = temperature, when_timestamp = time_init, where_id=self.kwargs['where']) +# self.sq['s'].execute(ins) + + + + def parse(self, time_init,string): + string = string.strip(','); + string = string.split(':')[1]; + curr_temp = float(string) + self.insert_temperature(time_init, curr_temp) +# self.insert_temperature(time_init,string) + +# if (time_init - self.last_insert).seconds > self.heartbeat_interval: +# self.insert_heartbeat(time_init) +# self.last_insert = time_init + + + + +class Odometer(BaseSensorPost): + + def insert_speeds(self, time_init, transittime = None, heartbeat = False): + + if transittime is None or transittime < 0: + isclockwise = 0 + else: + isclockwise = 1 + if time_init is not None: + self.sq.executemany('INSERT INTO nuggets.odometer (when, who, speed, clockwise) values ',[{'when':time_init, 'who':self.kwargs['who'], 'speed':abs(transittime), 'clockwise':isclockwise}]) + + # ins = self.sq['t'][self.table].insert().values( +# when_timestamp = time_init, speed = abs(transittime), who_id = self.kwargs['who'], +# clockwise = isclockwise ) +# self.sq['s'].execute(ins) + + + def parse(self, time_init,string): + string = string.strip(','); + string = string.split(':')[1]; + string = string.split(',') + for speed, offset in zip(string[0::2],string[1::2]): + speed = int(speed) + if speed != 0: + time_actual = time_init + timedelta(milliseconds = int(offset)) + self.insert_speeds(time_actual, transittime = speed) + r.incr('nugget_run_counts') + + if (time_init - self.last_insert).seconds > self.heartbeat_interval: + self.insert_heartbeat(time_init ) + self.last_insert = time_init + + self.last_measurement = time_init + +# %% diff --git a/trackSerial.py b/trackSerial.py new file mode 100755 index 0000000..0437f24 --- /dev/null +++ b/trackSerial.py @@ -0,0 +1,129 @@ +import sys +import serial +import time +import traceback +import syslog +from db_conn import connect +import dateutil.parser +from datetime import timedelta, datetime, timezone +sys.path.append('/home/thebears/Nextcloud/Designs/NuggetTracker/CommonCode/') +from track import Temperature, VerticalWheel, ActivityLogger, Weight, BaseSensorPost, HorizontalWheel, Paired +import os + +sq = connect() + +commit_interval = 60 +code_heartbeat = 10*60 + +def now(): + return datetime.now(timezone.utc) + +last_commit = now() +last_heartbeat = now() + +func_ref = {'T:':Temperature(sq, 'floor2temperature'), 'V:':VerticalWheel(sq, 'verticalwheel'), 'M:':ActivityLogger(sq,'activities'), + 'W':Weight(sq,'food_weight'), 'S:':HorizontalWheel(sq, 'horizontalwheel'), 'D':ActivityLogger(sq,'food_dispenser'), 'A':Paired(sq,'food_forager')}; + +code_start = BaseSensorPost(sq, 'started') +code_start.insert_heartbeat(now()) + + +heartbeat = BaseSensorPost(sq, 'code') + +ser = serial.Serial('/dev/winslowMONITOR', 9600, timeout=None) +#ser = serial.Serial('/dev/ttyACM0',57600,timeout=None) +while True: + try: + if os.path.exists('/dev/shm/mealies_open.txt'): + os.remove('/dev/shm/mealies_open.txt') + ser.write(b'n') + + if os.path.exists('/dev/shm/mealies_close.txt'): + os.remove('/dev/shm/mealies_close.txt') + ser.write(b'o') + + if os.path.exists('/dev/shm/mealies_next.txt'): + os.remove('/dev/shm/mealies_next.txt') + ser.write(b'p') + + + + + + if os.path.exists('/dev/shm/mealies2_open.txt'): + os.remove('/dev/shm/mealies2_open.txt') + ser.write(b'c') + + if os.path.exists('/dev/shm/mealies2_reset.txt'): + os.remove('/dev/shm/mealies2_reset.txt') + ser.write(b'd') + + if os.path.exists('/dev/shm/mealies2_open_all.txt'): + os.remove('/dev/shm/mealies2_open_all.txt') + ser.write(b'e') + + + + + if os.path.exists('/dev/shm/mealies_reinit.txt'): + os.remove('/dev/shm/mealies_reinit.txt') + ser.write(b'q') + + + if os.path.exists('/dev/shm/i2c_reinit.txt'): + os.remove('/dev/shm/i2c_reinit.txt') + ser.write(b'r') + + if os.path.exists('/dev/shm/winslow/mealies2_reset.txt'): + os.remove('/dev/shm/winslow/mealies2_reset.txt') + ser.write(b'd') + + except: + e = traceback.format_exc() + print(e) + syslog.syslog(syslog.LOG_ERR,e) + + + + + try: + + data = ser.readline() + string = data.decode('UTF-8') + print(string) + syslog.syslog(syslog.LOG_INFO,string) + parse_str = string.strip() + time_init = now() + for startchars, function in func_ref.items(): + if parse_str.startswith(startchars): + print(parse_str, function) + function.parse(time_init,parse_str) + + + sq['s'].commit() + curr_time = now() + if (curr_time - last_heartbeat).seconds > code_heartbeat: + heartbeat.insert_heartbeat(curr_time) + + + + except: + e = traceback.format_exc() + print(e) + syslog.syslog(syslog.LOG_ERR,e) + + + + + + + + + + + + + + + + diff --git a/trackSerial_fullduplex (SFConflict ispatel@live.com 2025-06-22-10-59-32).py b/trackSerial_fullduplex (SFConflict ispatel@live.com 2025-06-22-10-59-32).py new file mode 100755 index 0000000..26917d8 --- /dev/null +++ b/trackSerial_fullduplex (SFConflict ispatel@live.com 2025-06-22-10-59-32).py @@ -0,0 +1,155 @@ +import sys +import serial +import time +import traceback +#from CommonCode.db_conn import connect +import dateutil.parser +from datetime import timedelta, datetime +from pytz import timezone +from CommonCode.track import Temperature, Odometer, BaseSensorPost +import os +import threading +import redis + +#sq = connect() +r = redis.StrictRedis(host='192.168.1.242', port=6379, db=1) + +def now(): + return datetime.now() +# return datetime.now(timezone('US/Eastern')) + +commit_interval = 60 +code_heartbeat = 10*60 + +last_commit = now() +last_heartbeat = now() + +#func_ref = {'T:':Temperature(sq, 'floor2temperature'), 'V:':VerticalWheel(sq, 'verticalwheel'), 'M:':ActivityLogger(sq,'activities'), +# 'W':Weight(sq,'food_weight'), 'S:':HorizontalWheel(sq, 'horizontalwheel_sonic'), 'D':ActivityLogger(sq,'food_dispenser'), 'A':Paired(sq,'food_forager')}; + + +from clickhouse_driver import connect +from datetime import datetime +conn = connect('clickhouse://192.168.1.242'); +sq = conn.cursor(); + + +func_ref = {'T':Temperature(sq, 'floor_3_temperature','temperature',where=1),'S':Odometer(sq,'i2c_odometer','odometer',who=4)} +code_start = BaseSensorPost(sq, 'started','heartbeats') +code_start.insert_heartbeat(now()) + + +heartbeat = BaseSensorPost(sq, 'code','heartbeats') + +ser = serial.Serial('/dev/serial_i2c', 9600, timeout=None) + +def write(): + while True: + time.sleep(0.2) + try: + if os.path.exists('/dev/shm/mealies_open.txt'): + os.remove('/dev/shm/mealies_open.txt') + ser.write(b'n') + + if os.path.exists('/dev/shm/mealies_close.txt'): + os.remove('/dev/shm/mealies_close.txt') + ser.write(b'o') + + if os.path.exists('/dev/shm/mealies_next.txt'): + os.remove('/dev/shm/mealies_next.txt') + ser.write(b'p') + + + + + + if os.path.exists('/dev/shm/winslow/mealies2_open.txt'): + os.remove('/dev/shm/winslow/mealies2_open.txt') + ser.write(b'c') + + if os.path.exists('/dev/shm/mealies2_reset.txt'): + os.remove('/dev/shm/mealies2_reset.txt') + ser.write(b'd') + + if os.path.exists('/dev/shm/mealies2_open_all.txt'): + os.remove('/dev/shm/mealies2_open_all.txt') + ser.write(b'e') + + + + + if os.path.exists('/dev/shm/mealies_reinit.txt'): + os.remove('/dev/shm/mealies_reinit.txt') + ser.write(b'q') + + + if os.path.exists('/dev/shm/i2c_reinit.txt'): + os.remove('/dev/shm/i2c_reinit.txt') + ser.write(b'r') + + if os.path.exists('/dev/shm/winslow/mealies2_reset.txt'): + os.remove('/dev/shm/winslow/mealies2_reset.txt') + ser.write(b'd') + + except: + e = traceback.format_exc() + print(e) + + +#t1 = threading.Thread(target=write, args=()) +#t1.start() + + +while True: + + # try: + if True: + time.sleep(0.2) + data = ser.readline() + string = data.decode('UTF-8') + print(string) + parse_str = string.strip() + time_init = now() + for startchars, function in func_ref.items(): + if parse_str.startswith(startchars): + r.set('i2c_'+startchars, str(time.time()) + ':' + parse_str) + try: + function.parse(time_init,parse_str) + except IndexError: + print('Index error') + except Exception as ff: + raise Exception(ff) + + if (time_init - last_commit).seconds > commit_interval: +# sq['s'].commit() + last_commit = time_init + +# sq['s'].commit() + curr_time = now() + if (curr_time - last_heartbeat).seconds > code_heartbeat: + heartbeat.insert_heartbeat(curr_time) + last_heartbeat = curr_time + + last_heartbeat = curr_time + + + +# except: +# e = traceback.format_exc() +# print(e) + + + + + + + + + + + + + + + + diff --git a/trackSerial_fullduplex.py b/trackSerial_fullduplex.py new file mode 100755 index 0000000..30a56e1 --- /dev/null +++ b/trackSerial_fullduplex.py @@ -0,0 +1,154 @@ +import sys +import serial +import time +import traceback +import syslog +#from CommonCode.db_conn import connect +import dateutil.parser +from datetime import timedelta, datetime +from pytz import timezone +from CommonCode.track import Temperature, Odometer, BaseSensorPost +import os +import threading +import redis + +#sq = connect() +r = redis.StrictRedis(host='192.168.1.242', port=6379, db=1) + +def now(): + return datetime.now() +# return datetime.now(timezone('US/Eastern')) + +commit_interval = 60 +code_heartbeat = 10*60 + +last_commit = now() +last_heartbeat = now() + +#func_ref = {'T:':Temperature(sq, 'floor2temperature'), 'V:':VerticalWheel(sq, 'verticalwheel'), 'M:':ActivityLogger(sq,'activities'), +# 'W':Weight(sq,'food_weight'), 'S:':HorizontalWheel(sq, 'horizontalwheel_sonic'), 'D':ActivityLogger(sq,'food_dispenser'), 'A':Paired(sq,'food_forager')}; + + +from clickhouse_driver import connect +from datetime import datetime +conn = connect('clickhouse://192.168.1.242'); +sq = conn.cursor(); + + +func_ref = {'T':Temperature(sq, 'floor_3_temperature','temperature',where=1),'S':Odometer(sq,'i2c_odometer','odometer',who=4)} +code_start = BaseSensorPost(sq, 'started','heartbeats') +code_start.insert_heartbeat(now()) + + +heartbeat = BaseSensorPost(sq, 'code','heartbeats') + +ser = serial.Serial('/dev/serial_i2c', 9600, timeout=None) + +def write(): + while True: + time.sleep(0.2) + try: + if os.path.exists('/dev/shm/mealies_open.txt'): + os.remove('/dev/shm/mealies_open.txt') + ser.write(b'n') + + if os.path.exists('/dev/shm/mealies_close.txt'): + os.remove('/dev/shm/mealies_close.txt') + ser.write(b'o') + + if os.path.exists('/dev/shm/mealies_next.txt'): + os.remove('/dev/shm/mealies_next.txt') + ser.write(b'p') + + + + + + if os.path.exists('/dev/shm/winslow/mealies2_open.txt'): + os.remove('/dev/shm/winslow/mealies2_open.txt') + ser.write(b'c') + + if os.path.exists('/dev/shm/mealies2_reset.txt'): + os.remove('/dev/shm/mealies2_reset.txt') + ser.write(b'd') + + if os.path.exists('/dev/shm/mealies2_open_all.txt'): + os.remove('/dev/shm/mealies2_open_all.txt') + ser.write(b'e') + + + + + if os.path.exists('/dev/shm/mealies_reinit.txt'): + os.remove('/dev/shm/mealies_reinit.txt') + ser.write(b'q') + + + if os.path.exists('/dev/shm/i2c_reinit.txt'): + os.remove('/dev/shm/i2c_reinit.txt') + ser.write(b'r') + + if os.path.exists('/dev/shm/winslow/mealies2_reset.txt'): + os.remove('/dev/shm/winslow/mealies2_reset.txt') + ser.write(b'd') + + except: + e = traceback.format_exc() + print(e) + syslog.syslog(syslog.LOG_ERR,e) + + +t1 = threading.Thread(target=write, args=()) +t1.start() + + +while True: + + try: +# if True: + time.sleep(0.2) + data = ser.readline() + string = data.decode('UTF-8') + print(string) + syslog.syslog(syslog.LOG_INFO,string) + parse_str = string.strip() + time_init = now() + for startchars, function in func_ref.items(): + if parse_str.startswith(startchars): + r.set('i2c_'+startchars, str(time.time()) + ':' + parse_str) + function.parse(time_init,parse_str) + + if (time_init - last_commit).seconds > commit_interval: +# sq['s'].commit() + last_commit = time_init + +# sq['s'].commit() + curr_time = now() + if (curr_time - last_heartbeat).seconds > code_heartbeat: + heartbeat.insert_heartbeat(curr_time) + last_heartbeat = curr_time + + last_heartbeat = curr_time + + + + except: + e = traceback.format_exc() + print(e) + syslog.syslog(syslog.LOG_ERR,e) + + + + + + + + + + + + + + + + diff --git a/util.py b/util.py new file mode 100644 index 0000000..6f6f364 --- /dev/null +++ b/util.py @@ -0,0 +1,112 @@ + +import glob +import json +import textwrap +import sys +import os +from collections import defaultdict +def json_per_row_to_json(fpath = '/home/thebears/Videos/Winslow/saved/marks'): + fid = open(fpath,'r') + d = list() + for x in fid: + d.append(json.loads(x)) + + json.dump(d,open(fpath+'.json','w'),indent=4) + + +def is_ipython(): + import __main__ as main_ff + if hasattr(main_ff,'__file__'): + return False + else: + return True + +def exit_if_not_ipython(): + import __main__ as main_ff + if hasattr(main_ff,'__file__'): + print('Running in non-interactive mode, exiting...') + sys.exit() + else: + print('Running in interactive mode, not exiting...') + + + +def append_json_no_file_read(obj_append, jpath = '/home/thebears/Videos/Winslow/saved/marks.json'): + fid = open(jpath,'r+') + fid.seek(0,2) + position = fid.tell() -2 + fid.seek(position) + fid.write(",\n") + fid.write(textwrap.indent(json.dumps(obj_append, indent=4),' ')) + fid.write('\n]') + fid.close() + + + +def get_cset_match(input_in): + if not os.path.exists(input_in): + return {} + + if os.path.isdir(input_in): + return get_cset_match_dir(input_in) + else: + dir_list = os.path.dirname(input_in) + summ = get_cset_match_dir(dir_list) + name, ext = os.path.splitext(input_in) + tstamp = int(name.replace('_trimmed','').split('_')[-1]) + return summ[tstamp] + +def get_cset_for_file_matching(file_in): + name, ext = os.path.splitext(file_in) + cset = dict() + files = glob.glob(name+"*") + for f in files: + [f_name, f_ext ] =f.split(".",1) + cset['.'+f_ext] = f + + return cset + + +def get_cset_match_dir(rtdir): + summary = dict() + + other_files = dict() + + for f in os.walk(rtdir): + c_rt = f[0] + c_files = f[2] + + for c_file in c_files: + name, ext = os.path.splitext(c_file) + try: + tstamp = int(name.replace('_trimmed','').split('_')[-1]) + if ext in {'.mp4'}: + summary[tstamp] = dict() + summary[tstamp][ext] = os.path.join(c_rt, c_file) + else: + if tstamp not in other_files: + other_files[tstamp] = dict() + other_files[tstamp][ext] = os.path.join(c_rt, c_file) + except: + pass + + mp4_tstamps = sorted(summary) + other_tstamps = sorted(other_files) + idx_other = 0 + + merge_map = defaultdict(list) + merged = 0 + for idx_mp4, m_t in enumerate(mp4_tstamps): +# for idx_o_test in range(len(other_tstamps))) + for idx_o_test, o_t in enumerate(other_tstamps): + if abs(m_t - o_t) < 25: + merge_map[m_t].append(o_t) + + + + for k, v in merge_map.items(): + for v_join in v: + summary[k].update(other_files[v_join]) + + + return summary diff --git a/video_meta.py b/video_meta.py new file mode 100644 index 0000000..24a0dbb --- /dev/null +++ b/video_meta.py @@ -0,0 +1,199 @@ +import datetime as dt +import os +import numpy as np +import subprocess +import pickle +from CommonCode.settings import get_logger, LogColorize +import logging +import numpy +pfm = LogColorize.video_meta + +if not ('__file__' in vars() or '__file__' in globals()): + __file__ = '/home/thebears/Seafile/Designs/Code/Python/VideoProcessing/VideoMeta.py' + +logger = get_logger(__name__, stdout=True, systemd=False) + +orig_prefices = ("/srv/ftp", "/srv/ftp_tcc") +new_prefices = "/mnt/hdd_24tb_1/videos/ftp" + + +def get_info_from_ftp_filename(cpath): + date_str_format = "%Y%m%d%H%M%S" + bname = os.path.basename(cpath) + froot, ext = os.path.splitext(bname) + cam_name, index, time_str = froot.split("_") + time_stamp = dt.datetime.strptime(time_str, date_str_format) + file_info = { + "name": cam_name, + "index": index, + "timestamp": time_stamp, + "path": os.path.abspath(cpath), + } + return file_info + +def get_cache_loc(cpath): + fpathroot, ext = os.path.splitext(cpath) + return fpathroot + '.timestamp_cache' + + +class FTPVideo: + def __init__(self, cpath): + self.cpath = cpath + self.file_info = get_info_from_ftp_filename(cpath) + self._real_path = None + self._frame_info = None + self._embeds = None + self._embed_scores = None + + def __lt__(self, other): + comp_val = other; + if hasattr(other, 'timestamp'): + comp_val = other.timestamp + + return self.timestamp < comp_val + + @staticmethod + def vec_norm(vec_in): + return vec_in / np.linalg.norm(vec_in, axis=1)[:,None] + + @property + def embed_scores(self): + return self._embed_scores + + def attach_embedding_score(self, scores): + self._embed_scores = scores + + + @property + def embeddings(self): + embeds_path = os.path.splitext(self.real_path)[0] + '.oclip_embeds.npz' + if not os.path.exists(embeds_path): + return None + + if self._embeds is None: + npz_contents = np.load(embeds_path) + self._embeds = npz_contents + + npz_contents = self._embeds + + ret_dict = {}; + ret_dict['embeds'] = npz_contents['embeds'] + + ret_dict['frame_numbers'] = [int(x) for x in npz_contents['frame_numbers']] + ret_dict['frame_offsets'] = [self.frames_info[x]['offset'] for x in ret_dict['frame_numbers']] + ret_dict['frame_time'] = [self.frames_info[x]['time'] for x in ret_dict['frame_numbers']] + e_scores = self.embed_scores + if self.embed_scores is not None: + ret_dict['embed_scores'] = e_scores + + + return ret_dict + + def try_cache_read(self): + cache_loc = get_cache_loc(self.real_path) + if os.path.exists(cache_loc): + logger.info(pfm(f'ATTEMPT READING FROM CACHE: {cache_loc}')) + try: + with open(cache_loc,'rb') as ff: + data = pickle.load(ff) + logger.info(pfm(f'READ FROM CACHE: {cache_loc}')) + return data + except Exception as e: + logger.warn(pfm(f'READ FROM CACHE FAILED: {e} while reading {cache_loc}')) + + + return None + + def try_cache_write(self, data): + cache_loc = get_cache_loc(self.real_path) + logger.info(pfm(f'ATTEMPTING WRITING TO CACHE: {cache_loc}')) + try: + with open(cache_loc, 'wb') as ff: + pickle.dump(data, ff) + logger.info(pfm(f'WROTE TO CACHE: {cache_loc}')) + except Exception as e: + logger.warn(pfm(f'WRITE TO CACHE FAILED: {e} while writing {cache_loc}')) + + + @property + def frames_info(self): + if self._frame_info is None: + self._frame_info = self.try_cache_read(); + + if self._frame_info is not None: + return self._frame_info + + self._frame_info = self.get_frames_info() + self.try_cache_write(self._frame_info) + + return self._frame_info + + def get_frames_info(self): + if self._frame_info is not None: + return self._frame_info + + fpath = self.real_path + cmd = f"ffprobe -select_streams v:0 -show_entries packet=pts_time,flags -of csv {fpath}" + logger.info(pfm(f'RUNNING FFPROBE FOR {fpath}')) + + try: + out = subprocess.check_output(cmd.split(), stderr=subprocess.DEVNULL) + except Exception as e: + logger.warn(pfm(f'RUNNING FFPROBE FAILED FOR {e} on {fpath}')) + + logger.info(pfm(f'RAN FFPROBE SUCCESSFULLY FOR {fpath}')) + + timestamps = list() + for line in out.decode("UTF-8").split("\n"): + if "," not in line: + continue + + _, fr_s, fr_type = line.split(",") + fr_s = float(fr_s) + + timestamps.append( + { + "offset": fr_s, + "type": fr_type, + "time": dt.timedelta(seconds=fr_s) + self.timestamp, + } + ) + + self._frame_info = timestamps + return timestamps + + @property + def timestamp(self): + return self.file_info["timestamp"] + + @property + def camera_name(self): + return self.file_info["name"] + + @property + def path(self): + return self.cpath + + @property + def real_path(self): + if self._real_path is not None: + logger.debug(pfm(f'FOUND REAL PATH AS {self._real_path}')) + return self._real_path + + + + cand_path = self.path + if os.path.exists(self.path): + self._real_path = cand_path + return cand_path + for og_p in orig_prefices: + for new_p in new_prefices: + new_cand_path = cand_path.replace(og_p, new_p) + logger.debug(pfm(f'TRYING PATH AS {new_cand_path}')) + if os.path.exists(new_cand_path): + self._real_path = new_cand_path + logger.debug(pfm(f'FOUND REAL PATH AS {new_cand_path}')) + return new_cand_path + + + diff --git a/wq.py b/wq.py new file mode 100644 index 0000000..56a9d24 --- /dev/null +++ b/wq.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +import redis, pickle +from enum import Enum +import json + +class TOPICS(Enum): + ml_vision_to_score = 'ml_vision_to_score' + ml_vision_to_score_ext = 'ml_vision_to_score_ext' + ml_vision_objdet_failed = 'ml_vision_objdet_failed' + ml_vision_objdet_success = 'ml_vision_objdet_success' + ml_vision_objdet_skipped = 'ml_vision_objdet_skipped' + ml_vision_objdet_results_db_success = 'ml_vision_objdet_db_upload_success' + ml_vision_objdet_results_db_failed = 'ml_vision_objdet_db_upload_failed' + ml_vision_objdet_results_pg_success = 'ml_vision_objdet_pg_upload_success' + ml_vision_objdet_results_pg_failed = 'ml_vision_objdet_pg_upload_failed' + ml_vision_objdet_results_purge_success = 'ml_vision_objdet_purge_success' + ml_vision_objdet_results_purge_failed = 'ml_vision_objdet_purge_failed' + ml_vision_objdet_results_purge_skipped = 'ml_vision_objdet_purge_skipped' + ml_vision_videos_modify_success = 'ml_vision_videos_modify_success' + ml_vision_videos_modify_failed = 'ml_vision_videos_modify_failed' + ml_vision_embedding_success = 'ml_vision_embedding_success' + ml_vision_embedding_fail = 'ml_vision_embedding_fail' + ml_vision_embedding_skipped = 'ml_vision_embedding_skipped' + + +r = redis.StrictRedis() + +def publish(topic, message): + if isinstance(topic, TOPICS): + topic = topic.value + + + if isinstance(message, str): + r.rpush(topic, message) + else: + r.rpush(topic, json.dumps(message)) + + +def parse_message(msg): + try: + return json.loads(msg) + except: + return msg.decode() + +def consume(topic): + if isinstance(topic, TOPICS): + topic = topic.value + + msg = r.rpop(topic) + if msg is None: + return None + else: + return parse_message(msg) + +def peek(topic): + if isinstance(topic, TOPICS): + topic = topic.value + + vals = r.lrange(topic,0, 0) + if len(vals) == 0: + return None + else: + return parse_message(vals[0]) + +# from kafka import KafkaProducer, KafkaConsumer +# import pickle + +# producer = KafkaProducer(bootstrap_servers=["192.168.1.242:9092"]) +# consumers = dict() +# # %% + +# def publish(topic, message): +# producer.send(topic, value=pickle.dumps(message)) +# producer.flush() + + +# def get_consumer_by_topic(topic): +# if topic in consumers: +# return consumers[topic] + +# csumer = KafkaConsumer( +# bootstrap_servers="localhost:9092", +# group_id="wq", +# auto_offset_reset="earliest", +# enable_auto_commit=True, +# consumer_timeout_ms=1000, +# ) +# csumer.subscribe(topic) +# consumers[topic] = csumer + + +# def get_a_message(topic): +# csumer = get_consumer_by_topic(topic) +# return csumer + +