From fe37996f7b625a4e70f64b2141095f6120b003ac Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 2 Apr 2026 00:51:01 +0000 Subject: [PATCH] heathaet --- config/__pycache__/__init__.cpython-311.pyc | Bin 159 -> 159 bytes config/__pycache__/settings.cpython-311.pyc | Bin 5552 -> 5552 bytes config/__pycache__/urls.cpython-311.pyc | Bin 1557 -> 1557 bytes config/__pycache__/wsgi.cpython-311.pyc | Bin 679 -> 679 bytes cookies.txt | 4 + core/__pycache__/__init__.cpython-311.pyc | Bin 157 -> 157 bytes core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 212 bytes core/__pycache__/apps.cpython-311.pyc | Bin 524 -> 524 bytes .../context_processors.cpython-311.pyc | Bin 763 -> 1252 bytes core/__pycache__/forms.cpython-311.pyc | Bin 2116 -> 2116 bytes core/__pycache__/models.cpython-311.pyc | Bin 3623 -> 3623 bytes core/__pycache__/urls.cpython-311.pyc | Bin 1650 -> 1650 bytes core/__pycache__/views.cpython-311.pyc | Bin 12975 -> 27300 bytes core/context_processors.py | 12 +- .../__pycache__/0001_initial.cpython-311.pyc | Bin 2645 -> 2645 bytes ...ns_lottery_analysis_window.cpython-311.pyc | Bin 1087 -> 1087 bytes .../0003_alter_lottery_name.cpython-311.pyc | Bin 1154 -> 1154 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 168 -> 168 bytes core/templates/core/index.html | 21 +- core/views.py | 365 ++++++++++++++---- restore_app.py | 158 ++++---- 21 files changed, 402 insertions(+), 158 deletions(-) create mode 100644 cookies.txt diff --git a/config/__pycache__/__init__.cpython-311.pyc b/config/__pycache__/__init__.cpython-311.pyc index 227665244c2e6ad5797fde90fffe0e8572c8be16..190cbc2bde267e28f504bc5685f5250ccea3157d 100644 GIT binary patch delta 19 ZcmbQwIG>SwIWI340}#lsJv)(m3IHp&1mFMw delta 19 ZcmbQwIG>SwIWI340}zzIUN(_?3IHx)1y29~ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 281cd8fb9bd4d846c736b8cdf175b711827bf658..510ba17444390a83fd0d33f27115d56beb92ac36 100644 GIT binary patch delta 20 acmdm>y+NCMIWI340}#lsJ-d;6sVD$Byak*9 delta 20 acmdm>y+NCMIWI340}zzIUbc~YsVD$E)dmXy diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 27d885390f556a5f5655f87dfe55f25915546cea..57e12898a4121260f26eeaaddc5ad12c51bfd59b 100644 GIT binary patch delta 20 acmbQrGnI#XIWI340}#lsJ-d;ciwyuU83cX+ delta 20 acmbQrGnI#XIWI340}zzIUbc~&iwyuXG6d}a diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index 05ab9ff364f57586bb536e060f7e31bc196739fe..c718f3a8f95fd6ff30e1e90388f7a2f50a4d446d 100644 GIT binary patch delta 20 acmZ3^x}24JIWI340}#lsJ-d;64if+}=>;MH delta 20 acmZ3^x}24JIWI340}zzIUbc~Y4if-20tJx( diff --git a/cookies.txt b/cookies.txt new file mode 100644 index 0000000..c31d989 --- /dev/null +++ b/cookies.txt @@ -0,0 +1,4 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index 9ecccf5c20bb12ff46c2e0672134fbcc723cacc9..aae06704f48ac7fec2ab0f820bd03206c17302b1 100644 GIT binary patch delta 19 ZcmbQsIG2%oIWI340}#lsJv)(m5&$c01lj-q delta 19 ZcmbQsIG2%oIWI340}zzIUN(_?5&$k21xWw^ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 0071a6c54752ced92fff28211cc1a772a86aa8b7..1f878c92656894aaa26ad1fef3fa89429cde25ce 100644 GIT binary patch delta 19 Zcmcb@c!iOBIWI340}#lsJv)*6BmgsD1%Chl delta 19 Zcmcb@c!iOBIWI340}zzIUN({YBmg!F1?~U< diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 2f054bdba8ebbe4d9fabedca071ad41322e3aedd..60bbdc2ddd36e55c858b977c3f72dbf64a5bcb4d 100644 GIT binary patch delta 20 acmeBS>0#ks&dbZi00i=D&u-*qWC8#!jRa2s delta 20 acmeBS>0#ks&dbZi00iZ)mu=)`WC8#%rUbqK diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 553b7b9e77934ec634c96d8ab2a4bad245fb8442..876926e0360f528cb4cee7bc46df877e02d564b2 100644 GIT binary patch literal 1252 zcma)5&1=*^6rXIi>2AAPiv_!u+7S=Bpu6I+iltvj)qYU*U=fxyGj3;1GV9Dl+v1_1 z|3ek_pirw)Jb4lEB0URTSx^QFf_U(@QoZ!#OKSR|2u_mUdvE5=@BQ*JdA4s~3xZL7 z`<}f^AoL{+;sJ3qHco(9LG{!q zLL{yTUZ##ZB`efnH<^Q}e}@U~d(>C>jxCry?NW&q$7+URMZJn^D~cWND>|!i$>r0w z>(1qMW@2*u;_%?r;#mLa@O;~4t|)AlcwiDw2(Ayt%R|Fg1}8@*u8xe41-C%(1uAU9 z1!h3#vJ#Y~m{9DmzVWEwAkY>zc4Ice@qn-;b3uWJXP8v0uLN^A7SxHhQXnGC2`ND~ zmC9DDFQtMMnb+;Hh>_9$3&X{0lb1EpEjIC(U$g@?le2W@cSDN4?NNO&jtG^GU=_u< zuiN8E=Gi4$41}vwOco;=opAyoKuIw?vAToMip%H1W=0br$E)PhI>Dv3N>o9!b#j_> zS2s&4Wyo&Av#b1qYe{5Phm5sCJ+R7cyU&({1rf{9YD^fwCDMc^_Z_{j^ZFtD1hCUpq5c z>l~_g4z2ebt*4Fa|Sd zGQR|A@JnU{QBc4Jq?v*E^E8Hub*_v|6W>Vjm9WEP85pvdC$})F^0CZe1{%Ox70#sq z1d}&0`Wk{+w}by3RP`BFQbWzs+imd%p1?8nKOFS2PFY&&pU~omj z-~%Ts58DR@2*Jws8RRET<|0s_74d=ykQFN#iUdJyejxFS!zMRBr8Fniu1J^@$Oi>e VvBBh7EK+hGSU4GhW?>Rw-2kboVoLx3 diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index c04103584a8342bdd6e909e05bcbb5581043e8d3..521b7cb1b21a8018b21c6b8be5fff7e62ee9c249 100644 GIT binary patch delta 20 acmX>ia72K6IWI340}#lsJ-dia72K6IWI340}zzIUbd0jk^=xc90h~` diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index db9c55ccf066a48527860fa2a6fe82cc29ab5b4c..fcced8fcb83380ec7cbb6497784250d7d576a8d8 100644 GIT binary patch delta 20 acmZ23vs{LIIWI340}#lsJ-d-xh7SNWF$CNI delta 20 acmZ23vs{LIIWI340}zzIUbc~2h7SNZN(D;* diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index e74af67b00025b63a60526c37c9834b1b017b37b..a25b93366ec2d70a86e566610d9eecd0402f2621 100644 GIT binary patch delta 20 acmeyw^NELhIWI340}#lsJ-d-Roecm!p#_xy delta 20 acmeyw^NELhIWI340}zzIUbc}toecm%x&{OQ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 4f8400b26d5c5ad86ee175d0f7f46ccb2552825c..5f9fd1389222681e686fd66615a1d3aabf422dfd 100644 GIT binary patch literal 27300 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 Gq5lg~Zu>6) literal 12975 zcmcgSX>1(Vc{6)+XLpB7E|;Wutw@QNc}SwH!=yx0hjmaQtw>26+RkRh8IeovLCp+x z5X*!}l!SKJhH9OPapZc_Dh?IP2vIaC|Fl)%6wt3=hM0oW0SFW@4AdwJFi;E_{*!*+ zo4sa9IaC7laro_+%jT`TSu%kmRVx9xP8PPcZ@jT-OAZw&bVvD6)zbniMvPKq>eqt#62S(l6Sw~fD!B67^hg~SfVX0L_7mc#Ev zu3kF38UOFXSKUXqCk5Mz06n%nv{qJY2k|Xz_upe8#G@naZAJXr^Snub@v**9& zbDX89R>2tgT18mdQkJwc|23aWS<|lkTRvxQpR!IMg-A--1ZT=NL3kDm5;SLpvK>&y z7&Ry=?aAf}e>s~O^90ZediG}PaQ1t4a}AoBzc1Bgzf)8S_%mgZ8uBG(JUQn*%X_en z@8epM1bpY3>VfRbAO3VKnuvs=iRfrF5(|wc;~@bUCY}s&5i!JzQbhXj15t{O!W+kj z&J1)t0OpAq4EDLFlLHP5C#K?;d12Z*)IIPy-O*;(ZHY*n*R5CiTY5<>B1&Pbrh7=% zs4VL4c;sfbB|DlF`0gn1|IMy(DINnpx8u;F?&BsRiLqpsJ=;nJB)RT11Vq%WkyuPX zs_Tv`(HJna?n(+EM&Zl1bo-U4AWFI;GC2vNrI#X`BVu7LDo(~Cw}f(R#}rIKcV@A( zLyf2;R6-2_$tK`>z|5Qie{BlLqPu_wybz6u0%#{nC`C{KKt%QoVfwtYdpsHEyQeOL zq)m16H~G;iDJgW{ND5cQ$;c?*O=f*P%HI&XCU5CBtSz)abDWR(zW`>bkJ;e-x7OIk zRkl%Kcd6{I+0&ZGH+xbmui2nnc7}jC=X#)C4K%L>I#&ao%VTn&QwbbZ14q{ar&a@} z9{ZHQSJl8**8;Dv23}VJVKoq*E74ruA9~*OXn~rwK*wsJLkV=LfiA6l$6I~czFxWd zrNwHsx=XI^dQ_qHos?_OFP>3rJJ)J^R%?5fk1Mr@)Y?OG?V-o}v>iLu9UU8tBk10s z0Q`<}lrX#i6YJQ zW|dT+2_|y7Ck+^FPASX7B5B@Qzi>qoJ!9n@fUUK7^cmasG?7pZ^>VtX5HGVPFzF}f zj9o(h&ZLQ)c1qDdp*Yt&MH-c+q?&A9&^bl(oB<6q;mdxXal#v$dvi%Uvw3LgFjJf> zxk^3?AE8Z6IXH&%eAiN)Yp{tXB~nxFBb6#K_cr4OOT$RJ^EEhc%E@`Bp}@=`V2cK4 zJe)7(PNN>!lu{nfX>P%>)3E;2Ae}Sb6q62YY61l*Ps(eeD(VE1I4@Uf?hkRgvWd#` z=b-Okrjh9fJqfPtVY#^%8fdmPE>d^CIeHu^plr(ZMYJ{3()7_3 z<>`Zd<~{QP;Eb|uaz+gMc%$k%CD8NElyBP{5}XEW&-su~X3A6S)7qIRStLp3)#WNl zUx8FPSB1G$fU7242y>dLNR@59@-Y7jZU(3g473H*jQwAh-rYPRMrt`=6XjPr1H zU&J>vK@+dl8*QG2*JdiE=ddNetGYsS4YysXU@`Z> zTtbC&;Yw2WR3*2QYrN++(+j+$rm6cdx*N^;#i>bN0RL!mN{Vnv0llG<;3Dv$!-u;L z?+$f_E=CihLNXDZhR?*2+PkM~&z?PyO^i+nVp8lP{Tz?PMyFyCXg|#-46lkO{alWS ze1v#iaclz)`PhfQNPt@wibo_q{^3v23&UX}F$vr=UgYE8*Ni8PZrf<$&Y1jn;1>#Y z+mk&2?o!}af~?HYDf*YTLQ9$|^04n%<^vb02NZaE)@VX{V9_mIdvrP>qAd-Dgl72p z+}#(8iW1V}==83PKNyZCt|Y^e$!J$!EIAs9iAN!mt%dHXh?;wrlG!uHv$*%sHOT$Q zx-f9Z@s=Z3F0?{Dp$q|95djsCX=Z4B$;`e2nas?ZXK0QB&Z8%w%t&eA|ILXjnr|_O z!2DZa?JcGebF}P$4l^VAXXrmd$Fhr*@EkM{u$9mSppb|HD!l3L?NG0Pkd4?&JpdqN z*&*XujQpr);o6-8Zym@LsqNFg8TzOa~=DDb39!Sgjy+y=GC8rMQ5L-7*i zUeNJ2*iBmQSm*8Y?kt6o%nz(&cEWzh3&`{$I(;C#gl7$MZ)=v=nYbGO%rekxJd1l* z{6=nhVBtcp7&2s5(ck{z4}TaE&|Cb3Lx8jV1YLR4tgw{K3c33(?%n9TuY+7?bV0ZV z1dzTdA`fST&ea!-#4mG^qtlIF#56`7WG)fMpQX}~CHg)$OA$mb0S}<>f_oX<_46kD za#TkS%TV2oROhHXo**F%;7SWP8w1rWUyc5We6mli*e%J+DVLu96VcaO1ro9a=ItBe3Ge!TWfQy8q@V*@eI*#oO23n$*S{}MH z3}nLO8TjP^ExeRNY5+UOX*|1dp1`IVRw?h3$&>ak3@q?(oi^S85-NX!eEz4RE1ZSK z1_l2NWKR|#TX$9_&(hw-NZNjH+;{`HnXhwX#d-dS&IqCl`T7mZb?TRn6b(XHQmFje zGRhD2p6-ey5>v4l&t+JA_E|OHdBhuaz-PePb*GFcJ+R_m8DFVesauR7T&Xk2jK*== zP8^{&H}OMA_Bv%SgVx)9bf#x&b%1tcel90^~-99lvWH4=7U z4rwf4Kuve>H^E;KbsG*q+zyD#MiN{yuG>VO=imc;H^M?MV*_i9PYAek*6mPl41!(| zWfIWW0rd(E!GD65Nddx82%(1(9*DvV;L>4SmndLPKz~LE0cfihc41>CtAHSeMuJPa z3*%H`n7gdI$;8R4?#)vIRYeJe!w98C-I5ha!`;u`$J$xk=0bE8g`U^3*QACjg zTts3n1ymZkcPy5?Oh`*A`N$A3`$QNA*6Ap*uX?GmmiaDp_Z5hag^9L;uo@2`w(N_P zl%PZMMYsTC2{+O*+*_~@_>nhv2 zTq(1y3VT>(53jMuR@q~ZjwtK}mAwEVH{!oMY6U+=@eiv0!P&D~PwzkV|3kmhb3*Mo zAqNH*PTxKMSLg2xzcs7|y5&Iko~>46_Vr}&#yfAa=qDLFYZT5ichb!tQ=Tr zQM->pfw2Ti^$E55gyK4>x=zZjlb`x2#%G@Nrva*>*07xETkxyY^r-*?eQKa@_B>2= zc1UyjHz=$97`+~-duKd7t<>*R>-Q;v9yQQYIO{h~%ni(4(<*`+6zxS)AY7oe^1$Md zT;2}PBYJ6QWv|k4g$@4{G`zH?RaHmcsn4ayGV8E}r)7|()h z?#SGc#a1oYv=(ex4Yq)#+^YmT)nMncQwbhi=}{_<&bhVVuC-w6YOr<5|H1KP_k-bw z!%DDE4fX*~29M1#TIJ64aiy|lX>g?p&`^R$q0Cja=4x1VH7wD~gL3Facof%w>Kc&A z^Ix{Wnyq@(RxQ`>gJ+>jS8mPu|Wa& z9mRN@zefPCcIRf}huRPwyC)ogKkwX0azYoGguXThr%+LP(`krxXu6(6<3$YbCnPuv z38S9}YC0*9BMAX_18p|Lh!CrR5ejlP@W4WJHmOnQ(&&M$dJ}FzyG!sdb^@5C);CA^ z*BL*?%6nHP9=FOzE-K6=mANF_E^Ve1?luUeehs^XKaWzm6T18VKPPk$+qP{TMSl+_ z^*;O?Nq80N6|7>meA5}DowLI3-#(Fx*KZvWFFtQ8-u+`FJB?oNrj)Wmq{zY9$7nd8 zW3rzy8pk>A6-Du2!|^jIzhT0Co|AK>EL-qi$&eA`@I#L#UlqM7vQ>NyG8qrB4L!g# zyl+lsvZa9`HYbiUhq%CpY$qGJ!%){abrYhKx_ufB(_&AFSLF6acu*LV#I1roVdzf8 zm^t2X&;>MF@ZIvW|gT?m^ziIgQLO{R;$_dZvWf;N==7a z(=m5WBa5@rr}hr7^?rG^_sjBEzNYk!sJ$bwDoXvUYW=Ho!YRgfj{^&-DeWwph;!fZ1D7%;aJpy?3vY&nG$(;J_T|Tptl6%f6%z2eL zFWb&<)?_vy0MTT)2WkP zRfX@v$8q?7f)WOlH&pIEh3Qe59@*CO1sM8>u1!%B(9%d8eHFK|lyDVFbAq?^&4}Wg z3J^m?dzVQQNC%SADE^xgoOBkQSZ+s$im+YeY#AJ@IXhsP)2N)I4@Qwt`(zTFd%+y? z=LQ!k35k?Ruo|vSt5Gtf+g={Ncu8j_h3IwIPlRFL0`Z|AKqJ_T;?1K72wY}8jnY^W?j;y^0o=MH&P(G-PIwb~6Hv7XsC^6eZYV;!Q-rH6 zu-y}oaE9Dw3^PUnOoo{$7(}=OU$0_~Zv&X6G^g+OHy4Ae&IZ}p0KWNrY|U4{>Z@Oz zQGA`MuXE12&IDDaevR3^%Iscll9}BKb3kPdtT9JcnIj6*uQL7Ues(|VdECBw{E~e9 zlGd?j?$rG7;yK0Bwq9N%*LEu9U21uk?CIJD;~_f}(ZIbch79wJx3!ixTLL(^D`2JK z;I|QSY=NF3ey{*PaBU(sC5{_Z{2mk(ijXHqxa5>1yoF!!A0-VaXJc*;8$PLGt5Iz= zvaM#FDPMR=VQN$c-!=;svObyHuOYLYxfSCtvZmP;9eDxkK7(+X=>~#{7Z5TD*dDHs zZRtGPhlIa?T7;3NtBF!7V(kJC(n*MHa4}(2JhnGjIp)>?AhL$?S-?6rd?ozzL(CENNP4r05eO`yT!B!daO&=?=K-T)XQs%v?kOBm zxCh@;Sm&<+Y>&@;4G?@eEBg*AzJsdoAOtf^TC}PCe&1SC&uUYTy#J-g7Nu!WZ5o_& zs;-dcV(}Kz8@_qpoNt}2l&jhlwq0f0WmkJfHTEn=A63fFpHY~zDsxu0JtbO7v`8ah z{4CDH6^M}*IuF8kp#$N2IC(VV#<7de6jT%b7RpJjIjq_Wz;tH`d5fU(Z6Y;==YxOj z#*$f)72A2ZBLw#qC$Mjl;Es;{9cv114iWoZaQi1ODXAh~;puxW+?|XSDNndrV#)gn zTT2R8eE-H?=qqq;=)s-Em~(G=3#S8iTu<2vrW}QLX^L+SrR;D&!@@Ce*V3~Y_xaA0 z6^xsANVp3KOuHnA4|k7>*V|}t)iG!cy9w#mBtKmjk)+7zcpM6PL$L3GQ~5Z5r18pB zBEiSP)AlZC)Hq#!+-TEz0xl0G#VCm}JBG-1M0Z?-%ZgK?abk@cMRyHk;_td+0OHZt z`8H2M5E_}pxP54a&UMEP0WK22)kWhsd7~go{dKI0*R*si1fH`yk)L2G$}z@}jk~}C zDlHOHHBLTp4D;rLOWsJtk`XR!OilO&)IcT{e+vLy;7vlj&ekmLklFnTyI*DZLm0IA zYmdf$6O)_2W<0Z}WpATmYg+e~&W|j*6z@*eyL0v>&F238@VAE-URk^;2U-MhWIG~ZpaFJC){j^UWUjJ_?mdnV=~4|d>3q<9e8DR3M22}h(^YE z@P8dJ7#z2v3Knq93XM8Az)|#sli5=X;g?VYbuNjC)+5=$T~{XHlt)UhSlRTyr%j0@-r{tbRM0tiTk1HU@( zk4OH!;?cFojsI5ro7%_Ml%5M}&jq>aOYkVnMU}ZI+b$By2q&OvTZ3L|+#K!_$6;p( zH*ZB^4-FwiW`Gk4ab}9DMHT z1GJ`M{88bbp$u)4_#J={uBK^?dQpD*qfz_htsafqFF*a!s0P{mXq0!B{ApCZY<@I~ znI(T3<(?&f8#XT;0Rb+6^@nMQ_vXNL+CqOBY7{`1j~;@O0vMv{{h$T1z~R7L4wPEx zTI?VLTvqyJXkGx_PI?eZ3Se2TFVd@{gbr*_IS{1j*P;F<=%DFKq
- 5 loterias - Mega-Sena, Quina, Dupla Sena, Lotomania, Lotofacil + {{ lottery_count }} loterias + {{ lottery_overview }}
Analise recencia @@ -81,7 +81,7 @@
{{ form.lottery_type }} -
Baseado no historico recente simulado ate o admin importar dados reais.
+
Baseado no histórico recente já restaurado no banco e nas configurações atuais de cada loteria.
{{ form.lottery_type.errors }}
@@ -258,17 +258,11 @@ document.addEventListener('DOMContentLoaded', function() { let selectedNumbers = new Set(); let currentMax = 60; - const lotteryConfigs = { - 'mega_sena': 60, - 'quina': 80, - 'dupla_sena': 50, - 'lotomania': 100, - 'lotofacil': 25 - }; + const lotteryConfigs = {{ lottery_configs_json|safe }}; function renderGrid() { const type = lotterySelect.value; - currentMax = lotteryConfigs[type] || 60; + currentMax = (lotteryConfigs[type] && lotteryConfigs[type].max_number) || 60; grid.innerHTML = ''; selectedNumbers.clear(); updateUI(); @@ -301,8 +295,9 @@ document.addEventListener('DOMContentLoaded', function() { renderGrid(); // Initial load btnLive.onclick = async () => { - if (selectedNumbers.size < 6) { - alert('Por favor, selecione pelo menos 6 números.'); + const minimumRequired = (lotteryConfigs[lotterySelect.value] && lotteryConfigs[lotterySelect.value].numbers_to_draw) || 6; + if (selectedNumbers.size < minimumRequired) { + alert(`Por favor, selecione pelo menos ${minimumRequired} números para esta loteria.`); return; } diff --git a/core/views.py b/core/views.py index 484c64f..c8ab532 100644 --- a/core/views.py +++ b/core/views.py @@ -1,72 +1,259 @@ -from django.shortcuts import render, get_object_or_404, redirect -from django.http import JsonResponse, HttpResponse -from .models import Lottery, DrawResult, AdminAccess from collections import Counter -import random -from django import get_version -import json import csv +import json +import math +import random + +from django import get_version +from django.http import HttpResponse, JsonResponse +from django.shortcuts import get_object_or_404, redirect, render + +from .forms import LotterySimulatorForm +from .models import AdminAccess, DrawResult, Lottery + + +def _format_int(value): + return f"{int(value):,}".replace(",", ".") + + +def _format_percent(value): + if value <= 0: + return "0%" + if value < 0.000001: + return "< 0,000001%" + return f"{value:.6f}%".replace(".", ",") + + +def _parse_annulled_numbers(lottery): + return [int(number.strip()) for number in lottery.annulled_numbers.split(",") if number.strip().isdigit()] + + +def _lottery_queryset(): + return Lottery.objects.all().order_by("id") + + +def _lottery_choices(): + return [(lottery.name, lottery.get_name_display()) for lottery in _lottery_queryset()] + + +def _get_recent_draws(lottery, draws_to_consider=0): + queryset = DrawResult.objects.filter(lottery=lottery).order_by("-draw_number") + if draws_to_consider: + queryset = queryset[:draws_to_consider] + return [[int(number) for number in draw.numbers.split(",") if number] for draw in queryset] + + +def _rank_numbers(lottery, draw_matrix): + total_draws = len(draw_matrix) + if not total_draws: + ordered = list(range(1, lottery.max_number + 1)) + return ordered, ordered[: lottery.numbers_to_draw], ordered[-lottery.numbers_to_draw :] + + counts = Counter(number for draw in draw_matrix for number in draw) + last_seen = {} + for index, draw in enumerate(draw_matrix): + for number in draw: + last_seen.setdefault(number, index) + + scored = [] + for number in range(1, lottery.max_number + 1): + frequency_score = counts.get(number, 0) / total_draws + delay_score = last_seen.get(number, total_draws) / total_draws + total_score = (frequency_score * 0.7) + (delay_score * 0.3) + scored.append((number, total_score)) + + scored.sort(key=lambda item: (-item[1], item[0])) + ranked_numbers = [number for number, _ in scored] + hot_numbers = ranked_numbers[: lottery.numbers_to_draw] + cold_numbers = [number for number, _ in sorted(scored, key=lambda item: (item[1], item[0]))[: lottery.numbers_to_draw]] + return ranked_numbers, hot_numbers, cold_numbers + + +def _generate_suggestions(lottery, ranked_numbers, games_requested, annulled_numbers): + if not ranked_numbers: + ranked_numbers = list(range(1, lottery.max_number + 1)) + + filtered_pool = [number for number in ranked_numbers if number not in annulled_numbers] + if len(filtered_pool) < lottery.numbers_to_draw: + filtered_pool = ranked_numbers[:] + + pool_size = min(len(filtered_pool), max(lottery.numbers_to_draw * 4, lottery.numbers_to_draw)) + candidate_pool = filtered_pool[:pool_size] + display_total = min(games_requested, 12) if games_requested < 1_000_000_000_000 else 6 + + suggestions = [] + seen = set() + attempt = 0 + while len(suggestions) < display_total and attempt < display_total * 10: + generator = random.Random(f"{lottery.name}:{games_requested}:{attempt}:{','.join(map(str, candidate_pool[:20]))}") + sample = sorted(generator.sample(candidate_pool, k=min(lottery.numbers_to_draw, len(candidate_pool)))) + key = tuple(sample) + if key not in seen and len(sample) == lottery.numbers_to_draw: + seen.add(key) + suggestions.append(sample) + attempt += 1 + + if not suggestions: + suggestions.append(sorted(candidate_pool[: lottery.numbers_to_draw])) + + return suggestions + + +def _build_lottery_cards(lotteries): + tagline_map = { + "mega_sena": "6 dezenas no volante principal", + "quina": "5 dezenas com universo amplo", + "dupla_sena": "duas chances por concurso", + "lotomania": "análise ampla para dezenas recorrentes", + "lotofacil": "equilíbrio para 15 dezenas", + "timemania": "histórico recente com foco estatístico", + "dia_de_sorte": "recorte curto com leitura de frequência", + "federal": "modelo estatístico interno por sequência", + "super_sete": "sequências compactas por coluna", + "maismilionaria": "base principal de 6 dezenas", + } + + cards = [] + for lottery in lotteries: + try: + combinations = math.comb(lottery.max_number, lottery.numbers_to_draw) + odds = f"1 em {_format_int(combinations)}" + except ValueError: + odds = "Configuração personalizada" + + cards.append({ + "label": lottery.get_name_display(), + "tagline": tagline_map.get(lottery.name, "Análise estatística por histórico real"), + "picks": lottery.numbers_to_draw, + "range_max": lottery.max_number, + "odds": odds, + }) + return cards + + +def _build_home_result(form): + if not form.is_valid(): + return None + + lottery = get_object_or_404(Lottery, name=form.cleaned_data["lottery_type"]) + draws_to_consider = int(form.cleaned_data["draws_to_consider"]) + games_requested = int(form.cleaned_data["games_to_generate"]) + draw_matrix = _get_recent_draws(lottery, draws_to_consider) + ranked_numbers, hot_numbers, cold_numbers = _rank_numbers(lottery, draw_matrix) + annulled_numbers = _parse_annulled_numbers(lottery) + reclaimed_numbers = [number for number in hot_numbers if number in annulled_numbers] + total_combinations = math.comb(lottery.max_number, lottery.numbers_to_draw) + probability = 100 / total_combinations if total_combinations else 0 + suggestions = _generate_suggestions(lottery, ranked_numbers, games_requested, annulled_numbers) + + trillion_labels = { + 1000000000000: "1 trilhão", + 10000000000000: "10 trilhões", + 30000000000000: "30 trilhões", + 60000000000000: "60 trilhões", + } + + return { + "lottery": lottery.get_name_display(), + "draws_used": len(draw_matrix), + "total_combinations": _format_int(total_combinations), + "odds": f"1 em {_format_int(total_combinations)}", + "percent": _format_percent(probability), + "hot_numbers": hot_numbers, + "cold_numbers": cold_numbers, + "annulled_numbers": annulled_numbers, + "reclaimed_numbers": reclaimed_numbers, + "suggestions": suggestions, + "is_trillion": games_requested >= 1000000000000, + "trillion_label": trillion_labels.get(games_requested, _format_int(games_requested)), + } + def home(request): - """Página inicial com resumo das estatísticas de IA.""" - loterias = Lottery.objects.all() + """Página inicial com resumo das estatísticas e simulador principal.""" + lotteries = list(_lottery_queryset()) + form = LotterySimulatorForm( + request.POST or None, + lottery_choices=[(lottery.name, lottery.get_name_display()) for lottery in lotteries], + ) + result = _build_home_result(form) if request.method == "POST" else None + stats = [] - for lottery in loterias: - last_draw = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number').first() + for lottery in lotteries: + last_draw = DrawResult.objects.filter(lottery=lottery).order_by("-draw_number").first() stats.append({ - 'name': lottery.get_name_display(), - 'key': lottery.name, - 'last_draw': last_draw.draw_number if last_draw else "N/A", - 'last_numbers': last_draw.numbers.split(',') if last_draw else [], - 'max_number': lottery.max_number + "name": lottery.get_name_display(), + "key": lottery.name, + "last_draw": last_draw.draw_number if last_draw else "N/A", + "last_numbers": last_draw.numbers.split(",") if last_draw else [], + "max_number": lottery.max_number, + "numbers_to_draw": lottery.numbers_to_draw, }) - return render(request, "core/index.html", {"stats": stats, "django_version": get_version()}) + + overview_names = [lottery.get_name_display() for lottery in lotteries[:4]] + lottery_configs = { + lottery.name: { + "max_number": lottery.max_number, + "numbers_to_draw": lottery.numbers_to_draw, + } + for lottery in lotteries + } + + context = { + "form": form, + "result": result, + "stats": stats, + "django_version": get_version(), + "lottery_cards": _build_lottery_cards(lotteries), + "lottery_count": len(lotteries), + "lottery_overview": ", ".join(overview_names) + (" e mais" if len(lotteries) > 4 else ""), + "lottery_configs_json": json.dumps(lottery_configs), + } + return render(request, "core/index.html", context) + def lottery_info_api(request, lottery_key): - """ - Supercomputador de Elite 99.9% - Sincronizado com 10.000 concursos. - Calcula dezenas de elite baseadas em convergência matemática de alto desempenho. - """ + """Retorna indicadores estatísticos da loteria selecionada para o gerador sequencial.""" lottery = get_object_or_404(Lottery, name=lottery_key) - annulled = [int(n) for n in lottery.annulled_numbers.split(',') if n] - - draws_db = DrawResult.objects.filter(lottery=lottery).order_by('draw_number') - real_draws = [[int(n) for n in d.numbers.split(',')] for d in draws_db] + annulled = _parse_annulled_numbers(lottery) + + draws_db = DrawResult.objects.filter(lottery=lottery).order_by("draw_number") + real_draws = [[int(n) for n in draw.numbers.split(",")] for draw in draws_db] last_real_num = draws_db.last().draw_number if draws_db.exists() else 0 - + random.seed(f"{lottery_key}_supercomputer") all_draws = list(real_draws) max_num = lottery.max_number n_draw = lottery.numbers_to_draw - + while len(all_draws) < 10000: - hist_flat = [n for d in all_draws[-50:] for n in d] + hist_flat = [number for draw in all_draws[-50:] for number in draw] counts = Counter(hist_flat) candidates = [] - for n in range(1, max_num + 1): - score = 100 - (counts.get(n, 0) * 2) - candidates.append((n, score + random.randint(1, 10))) - candidates.sort(key=lambda x: x[1], reverse=True) - all_draws.append(sorted([c[0] for c in candidates[:n_draw]])) + for number in range(1, max_num + 1): + score = 100 - (counts.get(number, 0) * 2) + candidates.append((number, score + random.randint(1, 10))) + candidates.sort(key=lambda item: item[1], reverse=True) + all_draws.append(sorted(candidate[0] for candidate in candidates[:n_draw])) - global_counts = Counter(n for d in all_draws for n in d) + global_counts = Counter(number for draw in all_draws for number in draw) last_seen_real = {} - if real_draws: - for i, d in enumerate(reversed(real_draws)): - for n in d: - if n not in last_seen_real: last_seen_real[n] = i - - elite_candidates = [] - for n in range(1, max_num + 1): - if n in annulled: continue - freq_score = (global_counts.get(n, 0) / 10000.0) * 100 - delay_score = last_seen_real.get(n, len(real_draws)) * 1.5 - total_score = (freq_score * 0.4) + (delay_score * 0.6) - elite_candidates.append({'num': n, 'score': total_score}) + for index, draw in enumerate(reversed(real_draws)): + for number in draw: + last_seen_real.setdefault(number, index) - elite_candidates.sort(key=lambda x: x['score'], reverse=True) - elite_greens = [c['num'] for c in elite_candidates[:20]] - next_prediction = sorted([c['num'] for c in elite_candidates[:n_draw]]) + elite_candidates = [] + for number in range(1, max_num + 1): + if number in annulled: + continue + freq_score = (global_counts.get(number, 0) / 10000.0) * 100 + delay_score = last_seen_real.get(number, len(real_draws)) * 1.5 + total_score = (freq_score * 0.4) + (delay_score * 0.6) + elite_candidates.append({"num": number, "score": total_score}) + + elite_candidates.sort(key=lambda item: item["score"], reverse=True) + elite_greens = [candidate["num"] for candidate in elite_candidates[:20]] + next_prediction = sorted(candidate["num"] for candidate in elite_candidates[:n_draw]) return JsonResponse({ "name": lottery.get_name_display(), @@ -74,78 +261,112 @@ def lottery_info_api(request, lottery_key): "numbers_to_draw": lottery.numbers_to_draw, "elite_greens": elite_greens, "annulled_numbers": annulled, - "reclaimed_numbers": [n for n in elite_greens if n in annulled][:5], + "reclaimed_numbers": [number for number in elite_greens if number in annulled][:5], "supercomputer_sync": 10000, "last_real_contest": last_real_num, "next_prediction": next_prediction, - "precision": "99.9%" + "precision": "99.9%", }) + def sequential_generator(request): - loterias = Lottery.objects.all() + loterias = _lottery_queryset() return render(request, "core/sequential_generator.html", {"loterias": loterias}) + def lottery_results(request): - loterias = Lottery.objects.all() + loterias = _lottery_queryset() results_data = [] for lottery in loterias: - all_draws = DrawResult.objects.filter(lottery=lottery).order_by('-draw_number') + all_draws = DrawResult.objects.filter(lottery=lottery).order_by("-draw_number") if all_draws.exists(): current_draw = all_draws.first() - current_numbers = [n.strip().zfill(2) for n in current_draw.numbers.split(',')] + current_numbers = [number.strip().zfill(2) for number in current_draw.numbers.split(",")] results_data.append({ - 'lottery': lottery, - 'last_draw': current_draw, - 'current_numbers': current_numbers, - 'prediction': ["??"] * lottery.numbers_to_draw + "lottery": lottery, + "last_draw": current_draw, + "current_numbers": current_numbers, + "prediction": ["??"] * lottery.numbers_to_draw, }) return render(request, "core/results_ia.html", {"results": results_data}) + def hits_report(request): return render(request, "core/hits_report.html") + def admin_login(request): if request.method == "POST": key = request.POST.get("private_key") if AdminAccess.objects.filter(private_key=key).exists() or key == "admin123": - request.session['admin_auth'] = True - return redirect('admin_dashboard') + request.session["admin_auth"] = True + return redirect("admin_dashboard") return render(request, "core/admin_login.html") + def admin_logout(request): request.session.flush() - return redirect('home') + return redirect("home") + def admin_dashboard(request): - if not request.session.get('admin_auth'): return redirect('admin_login') - loterias = Lottery.objects.all() + if not request.session.get("admin_auth"): + return redirect("admin_login") + loterias = _lottery_queryset() return render(request, "core/admin_dashboard.html", {"loterias": loterias}) + def edit_lottery(request, lottery_id): - if not request.session.get('admin_auth'): return redirect('admin_login') + if not request.session.get("admin_auth"): + return redirect("admin_login") lottery = get_object_or_404(Lottery, id=lottery_id) if request.method == "POST": lottery.annulled_numbers = request.POST.get("annulled_numbers", "") lottery.save() - return redirect('admin_dashboard') + return redirect("admin_dashboard") return render(request, "core/edit_lottery.html", {"lottery": lottery}) + def full_report(request): return render(request, "core/full_report.html") + def download_funnel(request, lottery_id): lottery = get_object_or_404(Lottery, id=lottery_id) - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = f'attachment; filename="funnel_{lottery.name}.csv"' + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = f'attachment; filename="funnel_{lottery.name}.csv"' writer = csv.writer(response) - writer.writerow(['Number', 'Status']) - annulled = lottery.annulled_numbers.split(',') - for n in range(1, lottery.max_number + 1): - writer.writerow([n, 'Annulled' if str(n) in annulled else 'Active']) + writer.writerow(["Number", "Status"]) + annulled = lottery.annulled_numbers.split(",") + for number in range(1, lottery.max_number + 1): + writer.writerow([number, "Annulled" if str(number) in annulled else "Active"]) return response + def ai_auto_predict(request, lottery_id=None): return JsonResponse({"status": "success", "message": "Neural re-calibration complete."}) + def live_math(request): - return JsonResponse({"status": "live", "data": random.sample(range(1, 61), 6)}) + if request.method != "POST": + return JsonResponse({"error": "Método não permitido."}, status=405) + + payload = json.loads(request.body or "{}") + lottery_key = payload.get("lottery") + numbers = [int(number) for number in payload.get("numbers", []) if str(number).isdigit()] + lottery = get_object_or_404(Lottery, name=lottery_key) + + draw_matrix = _get_recent_draws(lottery, 50) + ranked_numbers, hot_numbers, _ = _rank_numbers(lottery, draw_matrix) + top_window = set(ranked_numbers[: max(lottery.numbers_to_draw * 2, lottery.numbers_to_draw)]) + overlap_score = sum(1 for number in numbers if number in top_window) + hot_overlap = sum(1 for number in numbers if number in hot_numbers) + diversity_bonus = 5 if len(set(number % 2 for number in numbers)) > 1 else 0 + score = min(99, 35 + (overlap_score * 8) + (hot_overlap * 10) + diversity_bonus) + + return JsonResponse({ + "status": "success" if score >= 70 else "warning", + "score": score, + "numbers_checked": len(numbers), + "lottery": lottery.get_name_display(), + }) diff --git a/restore_app.py b/restore_app.py index 597b18a..ce553a2 100644 --- a/restore_app.py +++ b/restore_app.py @@ -1,87 +1,107 @@ import os -import django -import requests from datetime import datetime -# Setup Django environment -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +import django +import requests + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") django.setup() -from core.models import Lottery, DrawResult, AdminAccess +from core.models import AdminAccess, DrawResult, Lottery + + +LOTTERIES = [ + {"name": "mega_sena", "api_name": "megasena", "min": 1, "max": 60, "draw": 6}, + {"name": "quina", "api_name": "quina", "min": 1, "max": 80, "draw": 5}, + {"name": "dupla_sena", "api_name": "duplasena", "min": 1, "max": 50, "draw": 6}, + {"name": "lotomania", "api_name": "lotomania", "min": 0, "max": 99, "draw": 50}, + {"name": "lotofacil", "api_name": "lotofacil", "min": 1, "max": 25, "draw": 15}, + {"name": "timemania", "api_name": "timemania", "min": 1, "max": 80, "draw": 7}, + {"name": "dia_de_sorte", "api_name": "diadesorte", "min": 1, "max": 31, "draw": 7}, + {"name": "federal", "api_name": "federal", "min": 0, "max": 99999, "draw": 5}, + {"name": "super_sete", "api_name": "supersete", "min": 0, "max": 9, "draw": 7}, + {"name": "maismilionaria", "api_name": "maismilionaria", "min": 1, "max": 50, "draw": 6}, +] + def restore_lotteries(): - lotteries_data = [ - ('mega_sena', 1, 60, 6), - ('quina', 1, 80, 5), - ('dupla_sena', 1, 50, 6), - ('lotomania', 0, 99, 50), - ('lotofacil', 1, 25, 15), - ('timemania', 1, 80, 7), - ('dia_de_sorte', 1, 31, 7), - ('federal', 0, 99999, 5), - ('super_sete', 0, 9, 7), - ('maismilionaria', 1, 50, 6), - ] - - for name, min_n, max_n, to_draw in lotteries_data: - Lottery.objects.get_or_create( - name=name, + created = 0 + for item in LOTTERIES: + lottery, was_created = Lottery.objects.get_or_create( + name=item["name"], defaults={ - 'min_number': min_n, - 'max_number': max_n, - 'numbers_to_draw': to_draw - } + "min_number": item["min"], + "max_number": item["max"], + "numbers_to_draw": item["draw"], + }, ) - print("Loterias inicializadas.") + if not was_created: + changed = False + for field, value in { + "min_number": item["min"], + "max_number": item["max"], + "numbers_to_draw": item["draw"], + }.items(): + if getattr(lottery, field) != value: + setattr(lottery, field, value) + changed = True + if changed: + lottery.save(update_fields=["min_number", "max_number", "numbers_to_draw"]) + created += int(was_created) + print(f"Loterias prontas. Novas criadas: {created}") + def restore_admin(): - if not AdminAccess.objects.exists(): - admin = AdminAccess.objects.create(private_key='admin123') - print(f"Chave Admin criada: {admin.private_key}") - else: - print(f"Chave Admin existente: {AdminAccess.objects.first().private_key}") + admin, created = AdminAccess.objects.get_or_create(private_key="admin123") + print(f"Chave admin pronta: {admin.private_key} ({'nova' if created else 'existente'})") -def sync_all_latest(): - from core.views import sync_results - sync_results() - print("Sincronização dos últimos resultados concluída.") -def sync_specific(lottery_name, contest_number): - mapping = { - 'mega_sena': 'megasena', - 'lotofacil': 'lotofacil', - } - api_name = mapping.get(lottery_name) - if not api_name: return +def sync_history(draws_per_lottery=40): + session = requests.Session() + inserted = 0 + + for item in LOTTERIES: + lottery = Lottery.objects.get(name=item["name"]) + latest_url = f"https://loteriascaixa-api.herokuapp.com/api/{item['api_name']}/latest" + latest_response = session.get(latest_url, timeout=15) + latest_response.raise_for_status() + latest_data = latest_response.json() + latest_contest = int(latest_data["concurso"]) + + current_inserted = 0 + for contest in range(latest_contest, max(latest_contest - draws_per_lottery, 0), -1): + contest_url = f"https://loteriascaixa-api.herokuapp.com/api/{item['api_name']}/{contest}" + response = session.get(contest_url, timeout=15) + if response.status_code != 200: + continue - lottery = Lottery.objects.get(name=lottery_name) - try: - url = f"https://loteriascaixa-api.herokuapp.com/api/{api_name}/{contest_number}" - response = requests.get(url, timeout=5) - if response.status_code == 200: data = response.json() - draw_number = int(data.get('concurso')) - if not DrawResult.objects.filter(lottery=lottery, draw_number=draw_number).exists(): - date_str = data.get('data') - draw_date = datetime.strptime(date_str, "%d/%m/%Y").date() - numbers_list = data.get('dezenas', []) - numbers_str = ",".join([str(int(n)) for n in numbers_list]) - DrawResult.objects.create( - lottery=lottery, - draw_number=draw_number, - draw_date=draw_date, - numbers=numbers_str - ) - print(f"Sincronizado específico: {lottery_name} Concurso {draw_number}") - except Exception as e: - print(f"Erro ao sincronizar {lottery_name} {contest_number}: {e}") + dezenas = data.get("dezenas") or [] + if not dezenas: + continue -if __name__ == "__main__": + draw_date = datetime.strptime(data["data"], "%d/%m/%Y").date() + numbers_str = ",".join(str(int(number)) for number in dezenas) + + _, created = DrawResult.objects.get_or_create( + lottery=lottery, + draw_number=int(data["concurso"]), + defaults={"draw_date": draw_date, "numbers": numbers_str}, + ) + inserted += int(created) + current_inserted += int(created) + + print(f"{item['name']}: ultimo={latest_contest}, novos={current_inserted}") + + print(f"Total de resultados inseridos agora: {inserted}") + + +def main(): restore_lotteries() restore_admin() - sync_all_latest() - # Concursos solicitados na história - for contest in [2976, 2977]: - sync_specific('mega_sena', contest) - sync_specific('lotofacil', contest) - print("Restauração concluída!") + sync_history(draws_per_lottery=40) + print("Restauração concluída com sucesso.") + + +if __name__ == "__main__": + main()