From 8bbca9ce0c65dfc120df69d2e8efc92a2abe6d75 Mon Sep 17 00:00:00 2001 From: subscorp <33960879+subscorp@users.noreply.github.com> Date: Tue, 16 Feb 2021 06:33:26 +0200 Subject: [PATCH 01/27] feature: add cursor settings --- app/database/models.py | 9 + app/dependencies.py | 1 + app/main.py | 3 +- app/media/cursors/Material_3d.cur | Bin 0 -> 4286 bytes app/media/cursors/Minecraft.cur | Bin 0 -> 9662 bytes app/media/cursors/Valentine_Heart.cur | Bin 0 -> 4286 bytes app/media/cursors/Wand.cur | Bin 0 -> 4286 bytes app/media/cursors/alternate_select.cur | Bin 0 -> 10462 bytes app/media/cursors/arrow.cur | Bin 0 -> 4286 bytes app/media/cursors/blue_cursor.cur | Bin 0 -> 13942 bytes app/media/cursors/blue_finger.cur | Bin 0 -> 4286 bytes app/media/cursors/cloud.cur | Bin 0 -> 9662 bytes app/media/cursors/credits.txt | 11 ++ app/media/cursors/finger.cur | Bin 0 -> 4286 bytes app/media/cursors/green_cursor.cur | Bin 0 -> 13942 bytes app/media/cursors/green_finger.cur | Bin 0 -> 4286 bytes app/media/cursors/green_lightsaber.cur | Bin 0 -> 4286 bytes app/media/cursors/ice.cur | Bin 0 -> 4286 bytes app/media/cursors/lime_cursor.cur | Bin 0 -> 13942 bytes app/media/cursors/p1.cur | Bin 0 -> 4286 bytes app/media/cursors/pink_cursor.cur | Bin 0 -> 13942 bytes app/media/cursors/pointer.cur | Bin 0 -> 4286 bytes app/media/cursors/red_cursor.cur | Bin 0 -> 13942 bytes app/media/cursors/red_finger.cur | Bin 0 -> 4286 bytes app/media/cursors/xp_arrow.cur | Bin 0 -> 4286 bytes app/media/cursors/xp_pencil.cur | Bin 0 -> 4286 bytes app/media/cursors/yellow_cursor.cur | Bin 0 -> 13942 bytes app/media/cursors/yellow_finger.cur | Bin 0 -> 4286 bytes app/routers/cursor.py | 170 +++++++++++++++++++ app/static/cursor.js | 41 +++++ app/templates/base.html | 4 + app/templates/cursor_settings.html | 27 +++ app/templates/home.html | 1 - app/templates/partials/base.html | 1 + app/templates/partials/index/navigation.html | 3 + requirements.txt | 3 +- tests/client_fixture.py | 14 ++ tests/test_cursor.py | 52 ++++++ 38 files changed, 337 insertions(+), 3 deletions(-) create mode 100644 app/media/cursors/Material_3d.cur create mode 100644 app/media/cursors/Minecraft.cur create mode 100644 app/media/cursors/Valentine_Heart.cur create mode 100644 app/media/cursors/Wand.cur create mode 100644 app/media/cursors/alternate_select.cur create mode 100644 app/media/cursors/arrow.cur create mode 100644 app/media/cursors/blue_cursor.cur create mode 100644 app/media/cursors/blue_finger.cur create mode 100644 app/media/cursors/cloud.cur create mode 100644 app/media/cursors/credits.txt create mode 100644 app/media/cursors/finger.cur create mode 100644 app/media/cursors/green_cursor.cur create mode 100644 app/media/cursors/green_finger.cur create mode 100644 app/media/cursors/green_lightsaber.cur create mode 100644 app/media/cursors/ice.cur create mode 100644 app/media/cursors/lime_cursor.cur create mode 100644 app/media/cursors/p1.cur create mode 100644 app/media/cursors/pink_cursor.cur create mode 100644 app/media/cursors/pointer.cur create mode 100644 app/media/cursors/red_cursor.cur create mode 100644 app/media/cursors/red_finger.cur create mode 100644 app/media/cursors/xp_arrow.cur create mode 100644 app/media/cursors/xp_pencil.cur create mode 100644 app/media/cursors/yellow_cursor.cur create mode 100644 app/media/cursors/yellow_finger.cur create mode 100644 app/routers/cursor.py create mode 100644 app/static/cursor.js create mode 100644 app/templates/cursor_settings.html create mode 100644 tests/test_cursor.py diff --git a/app/database/models.py b/app/database/models.py index 94f702d8..aadc85de 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -194,3 +194,12 @@ def __repr__(self): f'{self.start_day_in_month}/{self.start_month}-' f'{self.end_day_in_month}/{self.end_month}>' ) + + +class CursorSettings(Base): + __tablename__ = "cursor_settings" + + id = Column(Integer, primary_key=True, index=True) + user_id = Column(Integer, ForeignKey("users.id")) + primary_cursor = Column(String, nullable=False) + secondary_cursor = Column(String, nullable=False) diff --git a/app/dependencies.py b/app/dependencies.py index 8158f3ad..bd0a1c26 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -10,6 +10,7 @@ MEDIA_PATH = os.path.join(APP_PATH, config.MEDIA_DIRECTORY) STATIC_PATH = os.path.join(APP_PATH, "static") TEMPLATES_PATH = os.path.join(APP_PATH, "templates") +CURSORS_PATH = os.path.join(APP_PATH, "media/cursors/") templates = Jinja2Templates(directory=TEMPLATES_PATH) templates.env.add_extension('jinja2.ext.i18n') diff --git a/app/main.py b/app/main.py index d800d11e..c4394a52 100644 --- a/app/main.py +++ b/app/main.py @@ -32,7 +32,7 @@ def create_tables(engine, psql_environment): set_ui_language() from app.routers import ( # noqa: E402 - agenda, calendar, categories, currency, dayview, email, + agenda, calendar, categories, currency, cursor, dayview, email, event, invitation, profile, search, telegram, whatsapp ) @@ -43,6 +43,7 @@ def create_tables(engine, psql_environment): calendar.router, categories.router, currency.router, + cursor.router, dayview.router, email.router, event.router, diff --git a/app/media/cursors/Material_3d.cur b/app/media/cursors/Material_3d.cur new file mode 100644 index 0000000000000000000000000000000000000000..00f3abe05b2d9dad20837f39a5ef56c577bcf065 GIT binary patch literal 4286 zcmeHKOGsm97`>_5Ql#LjLKQnJ5z;P;R8XoSxTr3aiYqAv9jK%3Tq)6oitjfns1=`4 z#y5x+X%Ml9l_J&$rb;yxAJzE4XqwpE)ARk)4%1E-14+8d2j|}Kzs~=C_x$JHXxdwv zL3{E<ri3=@mQ2$4;M z5dMx}64Yd7X4*PCJ6-+#{d!|#qboHv)h6wP?9HF?EGsMPXkualX0sW6eSPTa>OyO4 zD^gNYej)J}-jc_}#27+DL-PyX(oz@4d_#B@v9YmVWn^SncXoE%+uPgjf`WoyxtDLb=yz3BRd>QYHa3QVfdTLt zfb|F4QBhH$bIgQSK@2EEbD9IXT(QKGfCKVQ_E|-QC@2 zZf;iG<>lolE-wC^(|f(;Pe@3x=(-NK+l~ACdt6;z;q2@T$H&JQ9v((qTpZHU(ok7h ziR$WVl$DjCq@)CiiHSdxTh4`7VKf>o_$y=&cXxNH2EuQ(TCujahN7Y(P$x)FPnW!r zo15z(shN1!D{@W_4-bFIFWZnk+}zx#{C9VEvAVj7`T2RJJ3bRcM@Q>nVPQL*`iZyP zGCw3Fk62n-f@n~le@jaX3JVMM$jHbuPOT z7EMh}%7f%gm`tV{&TsGs|M~ejHa0deH#evFC4b=-|LN)JQGQ5Y_=DWDzUF^;c!14j zQ`(=MomKqeyB!@JN(Yj=Xo6?b?(6);3&aEV_V%#6yo{--Ddj)n{Q}`;-a2(?qjtq7 zKlWw*lDnLJsl8}#a&l7DTKr#pxTdD&nqII*&I-b7{d0|%`g?qQRJqfSRqaK4)6>&` zpFbzxyIUMTNPJ58DipueUETpFCnxIMudJ*<_?f>aFE8&fC@4s3{Uzb+?+%N{<58Yt zx7%@ec!+9>z{QP_?J#mWtpA&{x#`)`~JkPI*=Y)J?J|*P)=YI)W{hNgW f(6txPcz+G-f6->*{oi0;5b+N2Az>oO$|n2?g7h`@ literal 0 HcmV?d00001 diff --git a/app/media/cursors/Minecraft.cur b/app/media/cursors/Minecraft.cur new file mode 100644 index 0000000000000000000000000000000000000000..2293c9572a62a5592cb05255479a194dee4f8d94 GIT binary patch literal 9662 zcmeHLu};G<5WOO#N?lkg1QL~4D2yHGj7SH%z|6=5jC=uMMR7EVqmK)7H^|%#`u1s3-#+q3&i&_4#nBy(6-SdacDV?D zSCM*uch34TYL5R?_KU%KS*(9_;qTXOG#V)m|H$xO)choRT+|p-YpV7Pb2k{y&_7c6 z`}IN1(==5anD-wc--|V-JG@)02TpGvYxN9sHz@q=dUBikPp4DGQC5HS46kDh_8wlB zp1B^!dADtrL7vRrAm&5PApApA%8x;v_g()aNfZZOSN^JP20c@>7VFmFdE|pUnY%&E z<9KWfe^g_=HtHOrKWdJ3vg);1=+y{+``q0G{jq+cm(VjsbtZJas~(5`5&l)HTkd!` z7w8|HmtFZh5UHv2o6 zlFhp~Yuo$^ek(;cI`1OIzb)NGc}$LMz5w4T&iY+^Yip+ZcrNO*XL$!_`s^m3L!#{b Qvpi;eb$$iaj~(ygA7^w0=Kufz literal 0 HcmV?d00001 diff --git a/app/media/cursors/Valentine_Heart.cur b/app/media/cursors/Valentine_Heart.cur new file mode 100644 index 0000000000000000000000000000000000000000..f6c4414d77ce4b2f6d413ef5b59e09d26e5cb6b1 GIT binary patch literal 4286 zcmeHL?NgLh6u+qDV7x55unVm03$W}iu&W5D2=Z>2HE1uCI5~_m(g;OEIStMTkd6QLWd6VSDB-=7XG0AG0W@?h~gHw((jinTl?CG4dJey{+D>;7R&K&OZ+rC_g+AVCBjeWbV3A+2+<)ELMenWq2Z9oM#EYBDa2bix7ZdHSXAJFDF75{-#%yf z1B>W;AjnwvIpH%0`mCtP@u=^gFW8eY2|Ll$`IkZ0C2?RLrKO|t1xNP&A%q4uK&;+= zS*y0sM1|X3XrPHW0x=qg#KU`(`|xOM`ymM3IW}SIqwLcWq0KIpya`nDW>AJSLAatB zR;t=Szp_K(z*>}*oqyy$pzd>BilJo;zF1q0renztd1=tppM5vgKg}i zR<=Q$zT;1c0{|@b0n+1AQ}bDR1|hkRI-NcU{IKEfUhfh8fd;7B!u`?*Vt6)OqTI)Es;gjL~4O#3u$d zF-G@5yrB;gjmOaXxuZ0S`M=L+Q&CZ?#v6Wu zvCBpH8&Xf<8hnazKjxSg(*TeWuKT$kk`j-@`eY|;Tt5O?8%EL2;GP+U^tGq>JK{lm zH_-n*#^1KB!R!s+YVN*C_!;X)I4;GfcfusS;|-)oFBo*)tUYne$v6x7Szo}W>~XgI ztn-k&aSYZboy7HMhXDU~8Nax=nSM3CS7ZID21rNh$tVV4lm3KBx>6lb-N)&BaQ+j> zPCv`IoAbT`vVzku8)0SehWjBm4zMBWR0Zk#bU=Z(*hrfDgkaqcEO1EZrp z;98`+hR&8S8Qw=&YbmRSG1qfY_{4Yc!ZSZX@pF^dPw+l^vfyhl=sow67igaLKX`hh(&p&+;UViB}c)fB8-l&>_H>;2PTx4s#tT<&B^YufL9gqKh968f!lk+-nz9}S5@WkudE!j(tCVd*Du!i?&%~; zAEYJ?KxTT+42{p3sHt%-!{>!GDTb#nFL%)W=xNo}!+clcfX8R<+2ivt9-K~C*55xC zZnK?;boM$I|Jr;QqW@0}{N3!oo68As>s5ejDuBsg0I>vI!ms9*5YA~KYOV>PC7Z;5 bJIvW~?C&tiXWCC_@w*aO`Zi)+x&!ZTlKfFc literal 0 HcmV?d00001 diff --git a/app/media/cursors/Wand.cur b/app/media/cursors/Wand.cur new file mode 100644 index 0000000000000000000000000000000000000000..0ec23876df6d636e99a6ac2c2d8358dd1828b6de GIT binary patch literal 4286 zcmeI!ze^io9LMp;2uj7GQwMC<7 zJJcT#O18#AXEBx{YNLfCPxA2ay649|7h@8p&!kf3;aV9r<`uOu zk|I-tRz8hcqOEuJW}r6%?q{G_EDo2;<)Lb|I#8?C67I$SLO!2Q20`$aa-2*T3WYav z4;nsJ()ax=S>_(vj#jT>E06`CowXfylWQ?KQDiiE}&jWuKqN{7o%8 z^w01v$R*j=v_t;}^ABl|1($nGyza|Bd37eOZIfT5PL_ySw0FkZPM@_`{WK5KJVNv9 P=Qdk>}YUfmItD;&< zb>Y}4O{vM`UmyPeN={VE0b###6^k%`ksorwuT`Jw%>4nV z3T2=$|;iq?8>CMI(41AK8bdf}J=#N8cn^X6WiI(58?6)UC`ELbp}?_H^$On|`bHMFNVFsuL?O`}h*fP0BjT*rQ z4H|eIJ9hL23>e^b?b_A&opjLeRzW!AgUpcp3{n+Z!Hckv`JAs=vu3D!_wM%EYp>bN znKNza(xvv%M<3anZ@y_mhYq!fhzMhjA?%i1q)3tE5+zCm^_w({TJO`ST` zR<2xW>(;Ha<;$1b(@#I`p+^$W*aQEBN}!yV8KPWK7Mg-$cq#UXu3WitN{0>|Y}BYx z_Rc%+*y`1*ZRgINcHqDPhh8sQw8)-(@<|I14>#7_`t<4J?%ld|t9||T*LLpQIlF%S zy2ZuC*|u%lq`OzVcyZ;!mCym?3z;SI&0TEiHV686BA<#7cR6RLx$LxF=K4;;>D(Wu3Widsi~=7#`dBzB+0b0Y%64_O?;C}cEC`NWDHe0rA=?xn;%;_QD*tc(=oj!fqu3fuk zDJdyVAALT4{CJx&V}`9?zut}>J!9>{(}zv17;DoH=uB^XAQV z{P=OZapOjYEpFbt>GXf$g%?6)%a;9)vEPQwsu>LR15y4~O>P1`VNR}GxlUHARxOq1 zd(uO5{OYT(+}!0SvWI-^z4zX;apT6>+_`g|@8~mq@B7rHOP3T!9=1&+k14+UfqIkes~o>xgVVmKcdDU%q^Dj~+cd%`q}E(xRfGT+#V5{@Z-T1VhYufiJ%@6Y)?7W4a)5MM zyLPS9Q}$A>Rewvb9!uWy>t0snTBdo6C`Pnzt`FT{8uK_tjHEJOPxZs^NfZa4eDX;~ zPon-rJ&E$Je5_fsW;xh^;FaS z-s-}I3r-K!a@Bj~W$7T@I(6!l!fy`o+kuQx5$;rc^Qa7wAM4r8p*M4x$6oeqixw?H zt5&UYaqQdUgAYD%wR|3Zy>yf=+CNhF64}FS=J(3AciI*_(usOT?U~gxM!*d8ILaP9 zSv{@%NpXJS#0j@&dj0j+ZNY*CP7m$*=wm|kgBSSST+p5-tLq+*_)(D${07v|{SC&E zJGa-WRVzVzs*@*Ax_YkMr9H0nP|vIQ&>n-_c7wB(FZi8ut=2=ndN)MRl`m*trJivZ ze8E{q{41}#5>n1m&Q)%mHEWjhEA{oWXU}%~Z*oEmJ^y5W^Dx}WT+8Eb5k3E2XbCzG zSVi2%cI(zHv}w~O7bEHslg;J|@ayLN4za~CbHkzZ7X7Vsi0Wxdb0Z{I%TpR0`?J=*&A?W;4!5NC16v6;?V?t{CP zYkBl7^8FZ5+|H(FJVD=-s=cggpz`qJk3a6Qk5}E>gdXZ;)YoJu@q0$qGWCp;$wde1 z*RP*AeE4uz1362vdiCmg*y$4AtM1i>>|_h3<8r3oGj`zpFwV|5az-7;d9|nh16wFA zQm{o7-wy`)VRndbrtt&u_f@U=R6XB%&Py-0ZrwVlvtOO1Q&%pb-@APGDCqv|C^~!4 znOQUF0}I%*pWuu=mHKX+o$7xF2Z@W9_)a}Yb``~l>T&~6j7)+kYF#oI^N;8KD)*am literal 0 HcmV?d00001 diff --git a/app/media/cursors/arrow.cur b/app/media/cursors/arrow.cur new file mode 100644 index 0000000000000000000000000000000000000000..39dae41a0daa402c84c3cefb135259ef186876ed GIT binary patch literal 4286 zcmeH|A#amW6o&7ZfKb7bCO|ULB^4u?KvYbOq$Cv+6Nn1L#6-o!1fmkrB?trpF@c=G zkWBCsSi&FaPuM^Bc%E`kbJuPe?YGR(lRTWWbMHOxz31M3r1U7QrLC=$o~9@1$3{xe zQ%bLZRC)`H@^4C~jIHdy@4z}8Xuz|~LpDV1CTzoN*nx+#(C#4k4*Jl00P*PeCR%$i zsIn2ehea*w#r=7g=4LJCsT*e=7TQDd`?HJg_}iLI@ZLO&7@I5iP|iDG>;rrWZ9RX# z2iGoQ4JNy$5AI)#%{iupc8B;7K9{;#`-r^bO<2U7--~+0=P-mW*k|uVZ(ojMUg^mX zAhgS#k-pP5%UZ^)7=gX)f&1`nPw%;$Pu_kktIg8XD(e}O^KOr`|A@bK&oQ6;0eDaL zaM5$KWMZwNo_##@Z@hc>7GtF+Z$CV<_u?B_R@-&kSspfn`&s*f_*a~l*tc)5cc7E!oDhq7 z-(TGx%zbyV&QEH8h0r=E_VtOp--Qk|ppMPyQ|vcfz!~@!)S7;CiO<1z;{S2>uZ}h2 zT(^0OtM8o;V-E3s{SF#Ya}kfeztKIYdw$=KcW52oQ|lHc-^bV^bJT7_-TSA+KUuj? zzdi0(ZSyNzbzs$jnGVEnHTx_47%m}IS8q}^ehC|pWDVBgadnwmFoLQ&#s8&?pQjpk IsduCO0hz*LumAu6 literal 0 HcmV?d00001 diff --git a/app/media/cursors/blue_cursor.cur b/app/media/cursors/blue_cursor.cur new file mode 100644 index 0000000000000000000000000000000000000000..2735a65429f49c2ec1256a62ff9619b5a15309d7 GIT binary patch literal 13942 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 X>G@W9RkGeCv_Pw@doARxF46u1?3Eu6 literal 0 HcmV?d00001 diff --git a/app/media/cursors/cloud.cur b/app/media/cursors/cloud.cur new file mode 100644 index 0000000000000000000000000000000000000000..198e87cc889c9f4f70f4ebae864c9f4a6d3bb0de GIT binary patch literal 9662 zcmeHM3s6+&6<(H?F%`!&VrW3q5|C7}5>2AgM#r%eGo^tvB@>w>wsmBtHHi)@XcW*v z2N{E~gQdQp8Un`hvZSzsgrz(pFa$BY!!wH@3(Nbluq+E-&$+Pe&JLKzZql@~XXe|p z|9|iO|L=Tz&VT=VS0oaPtVA0(ibS>|d(jOyk;qXb5_u4)OY}Q}mFNlj6p2pKF^gFg zuqa?rz@mUf0gD0_1uP0!6!?EqVAUf_Y$=}2gaASy;rE1{gyn?qtu3{;UT0+`x=I8` z2v`8q)F&tWhG0ebUKnwM0UP5zm zPWTXBAh;6PtV#E*4IVByyni=NOJ2drgMJ7-@M}o+?Z&=Wx5H=WMnm3)-|x}&YlKn0 zr4MVd)pMk8CJnD47|zY#I|=fiKKUa^!(YdZ^KT>bVh}PfNsx9S2+7j7arsOjV#40Q zy9fLb;OlLCJ-4d}TrXy|wLe~EPr6=u$j*B9*$qE|r-!rg+_N+xe(~&62z}=j+`JNk z;`Gy~$bOG-4!5(SQJ5Bm+>}$uObA7iR08SA{g4Fu!F%&l#$$1fo6R#Xr-2?>W_RJ2KVOHxo&OU) zN(e&MmBT_lT#ou9A`S=O$bsJ?BKS>QJbMT?E}cMLniQ4!*U<9m-_cqBF_cY(=q7YE z7D7>1faa<^)RyO;xHW&Zp58FKV*rQ8lWQjqjmYqiJt+TkB z8-ucs-h(VT0@tL$h(B{s&@toc8RXx*fb#t7sIQQrqu~>%6rW+JyAi`Z%^2!wMSph- zl=qs@(N>S9`YPP1_yqa4{(+>wUPR!&H}x^-%*KDIZDv%6KRQZcp{%%oo~o72RvllMW2!0uv>G_A^2wplJd|nxO8j$3Ryd zR9&B=zej=LekI0+DObivNpB6Nr>8M9Gb2FHzpt;4a;HtuJ~uZPiHV5_4GqPvUAy%9 zvmYg_HzfwowU58xy~zz2l>t-49W?|6p_ec?2dxU)@m@@fa()kCQa$!>-mE!upPii* zz_CnBOn~)oZ*Ld)%VaWve|UH}{QUe3`SbS*Q}{pQxrX-ylrK5N^)8`gqyWU7he-a4pBg#2D5>x+(mfA9Gk?Bev?TE6p&T-fV5oF|LV*h6c#x zawH`sAvib~?(XjV8)TTmpRaxSWj|jxH&+ZO+s(##p@BG`Dk>_FnwpBp$Vhm5d-Ga) z&Q$)szAx?nndf@+^>i*Y?N{p4ub~e*nb#(+2d;(K*jTu@xG?|Srto)L<7i9ID$2Z^ zG+_3d=Reo%-U=**Xr2Vn6P)? znqACPDisnE5+D|f`T1z8Dct$mA1<@oCwV83rZZmi7h|w+Kj#ngS1OfIC=`O{dsaH_ z+j@I@g>|U9x*EriA7|b9d7qz4O}3Q|cCYaL=l$szO*;81pYQ3&}4T zgx-au|0OwA8F{_NJRTRCq|nT6a}AB1B_Bh2jCvuFJ1 z(WAO8TedKd`viV3$QJJG?5yMG5o6>1a$H;-9334w&%=eY|2rQpH0`Sdeuuf8U_&r8 zey5d1(0X`yz~A2=Zf)QE#KK{R8KOmA@2rtfiZd=TvfJK3CS%JB=j~{&5z6(FR zW^SHB>p3s75_|;DJ-5j?L7q2s?1zoKV!o~Sh4XFp+v$AvYmEGIYAdLJAetxNVe+#MEG5TsYTlC*+6()C0UhyhaR2}S literal 0 HcmV?d00001 diff --git a/app/media/cursors/credits.txt b/app/media/cursors/credits.txt new file mode 100644 index 00000000..cc968479 --- /dev/null +++ b/app/media/cursors/credits.txt @@ -0,0 +1,11 @@ +http://www.rw-designer.com/cursor-set/material-amber +http://www.rw-designer.com/cursor-set/windows-xp-style +http://www.rw-designer.com/cursor-set/material-3d +http://www.rw-designer.com/cursor-set/cloudflix +http://www.rw-designer.com/cursor-set/primogem +http://www.rw-designer.com/cursor-set/paper-plane +http://www.rw-designer.com/cursor-set/yodalighsaber +http://www.rw-designer.com/cursor-set/valentine-heart +http://www.rw-designer.com/cursor-set/icy-ice +http://www.rw-designer.com/cursor-set/wii-hand-1 +http://www.rw-designer.com/cursor-set/minecraft-sets diff --git a/app/media/cursors/finger.cur b/app/media/cursors/finger.cur new file mode 100644 index 0000000000000000000000000000000000000000..e2a214d0bd533990d9bd2f025732237924b04a15 GIT binary patch literal 4286 zcmeHJSx{3~7{0cB?X)jl9y+5>)#-HF8LM>{TnZ{gKtvWp*(B_QA^|E(Si+J3N!ZI8 z6cj{K6oH`>H4UIv2N~B|&^FeoTSp7BDIj0}bAa0ML6F#)cE&sN=Op*uKi|K6|96fc zye7;O7Az2iH-$HZnzsevT|p2Qk4G(_W&cK|(G}g@F0w*waM_JDcK%o;l3=B+4_0jP z!tzZX_}p5IrB-6Bw)2CnZ!+9t_qM&PIAq0VZf$UhKxk$MqH-@mrtL;})>%lEod`}b zATX&7e)1N0@2Q74q6(Yc6R~)m6C8upz2?RcTY2T;Mz<)$7WE*Z>IRbPZXn^{HN+qI z88Q1VA-en=s1FD)Xb06EK8ba34J(7auLhs2wa4~QZR=cPNI!bFZd-^NUl(0La`kN} z8}A{l;SN&jf2TN1NT}&WT-6oqsW=Z=$r(ssEIo_B%w~>H zb%(W!9E+B0gq?r7>xo?dF>N?%`uEo9FHi5L8zMV zBlXB_&I!haAax7AjN8Zh4lCz4Ec|Re&4Xg+Z1J^i!|$;iBuR}>>id!V!#MIgfP&6R zD>?;~p8@iWSOWcXBbj{o7}b+Fl$G;930%FB1QcqKwvJAm91 z6CAgYFqu_!0lj0uD1gQu@ER~)upV%HT{oYBXY-#ezT(@P7eta2=!_%CZheA+lazBi zpguARb;Br1P804m;Ob+b;VR)1Cnod1L-^MbJ%#TPUj-*=Ht+w!B^y}&ZZp+C>5$1J z@=HZQlL`8ZgiF{tlz)bP7=h`nP}L4m|LH#Ug2j<<9E0ZjLC!%o2W%Eu|5^P#<@@3E zRW3HUCUX8!|DTIbSoS;UeO<>e^cR5AQxi}(jUcV2AL7Ugu$rXTP(S|W;P_PT{YXE0 zkI!KI{-3x8F#dTZRAWbYKDLMz_-L6m>;sfD@;|(=9lH|ikyhP@)C2b+D>#qMp799F zGjcsouX)J1$oQCj;xXqw(<3(jObc1njPbEHHWy^ZuJX8Itm(|kj zW276I*cOrn-_&|m({sdeG22h3@%ijg-`U(TY^HyarCnTmf>c!d$Xd8ZRl-eH3Wr@9 zEd9a_JEKdjX2PF-PJFf}PKjv!fZe0G#3G4;*PqQl!)MyVG?-yaGdd8cXhU#TD}3dL zVdt9(S7{k`gcM`BwFhi`lE$Y0doC+!58bH0+&4sFVnClSJy}07*@Q%?#B5#I`rvjGS zL;0Cty>mF+qDp%Fl?`hHGMd)UCEsU#i1Qpj`Ym+_47ogIQQpsgQ7#YxgY(#J5>aV(p}pHWxLje%;u(j#^s%8h@bvhxAyb1!1Q zIfivkQda*p=KlX|{G-ac21P#c$ZxuajDzQ3;ouMFh{A!{_`ZyHNCV5lat%+m`lVpA zS3Ia5W71rMS#v#?xM+X2p>vkaTzp^o{I@DVN4Tfgqj^A|Acsk?fI*m#1TsN*i`fL( o_>?`OwU5j+wX*Nymh>+3F_6FEb-*y6%;Kq;3kUR@7XSbN literal 0 HcmV?d00001 diff --git a/app/media/cursors/green_cursor.cur b/app/media/cursors/green_cursor.cur new file mode 100644 index 0000000000000000000000000000000000000000..64ef753acbac0862bb2da1c621b6b4fe41244a31 GIT binary patch literal 13942 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 literal 0 HcmV?d00001 diff --git a/app/media/cursors/ice.cur b/app/media/cursors/ice.cur new file mode 100644 index 0000000000000000000000000000000000000000..d155accd9c79c9b0f5c71f47b71041a81a288aff GIT binary patch literal 4286 zcmchZT}YEr7{`wv*hQ3mNGMt^x+$H0I9fd0u?vw0!q|tzP)=ocEmP`Tw5hcqU^k zm*p^#Ci?6+U8o9SGGNl%*1J|iGod1i5dc|?(bE%jNEmmr9J1b_1O)*yZY96?L?nG4_ z*(9uCs6S6RhprB$)MJH>x$URzKX6R11K&}|9t&+1iI0aBQ7t%PEtqTNnf_Ed=B-`M z7vdA^!}H^ODjwyLpr2zMwBGC6{L2a(bCHj1tO4Jtj_V!kIC8LeWRpf7S1J5#^iF)_ zPz}V#5!b1A;#2QEm$xM`-|B1Q8(RHVf8_$#y0JJvw_$dei|72d1((|O2ezfkX3C-s42`n$ZRHvfZ<_bhgB!Fxx& z)4ii}lDS#oLk*K7r?^`0_LMw)6E5GkNRT)619H6NB4%Yx|L2J^orTdiXMXJZ$IQru|p6eD&^Y@nv@G z#qQZA&w)0*-g@FP@sUm6wfCVt$?Bbz$Mkp_iBaRr?15*sKiRIjEk=Cx#-{V%yc`uqDbtcF=FO8A=EM literal 0 HcmV?d00001 diff --git a/app/media/cursors/lime_cursor.cur b/app/media/cursors/lime_cursor.cur new file mode 100644 index 0000000000000000000000000000000000000000..5a9f2584db2f84e974178ad2c1c0c5bede6cc3df GIT binary patch literal 13942 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 W;rS3srM-gEXs_ut41e|CZTnx*hyHy4 literal 0 HcmV?d00001 diff --git a/app/media/cursors/p1.cur b/app/media/cursors/p1.cur new file mode 100644 index 0000000000000000000000000000000000000000..6ae3d5e71ebb2ce4023753d8b7c1d7f2a5fcf9f5 GIT binary patch literal 4286 zcmeH}T})eL7{|{X3si7bFO12IqGB>H6i~P9POrQ$CW~1MbQp}8Cd+ioWSWRY#mq4S zWq^nnjoDo0WSe$#hHkLofMp#;7%&zXL!}?I1zR|U($h9e{QJCbd(I9_KH7QV1#j~2 zIX&;k?|Gj0c|QckDbZY9 zg?QyP#Ri%0?rl=IJm!~#Q5kV`@&C@#wP4z%F%=LL%I zqn3QE%~-?om92|JOO)lHJO}K_b8>PJf&T6A+YIRbS`XQON82%2_t2b_$7K6RkFi!B zd&Wn9l>X zc^EmIujB{YEAEg#+UIn`H4{Q)I&z!BlM!0`M`F1q$DQWC%SbUGG5%=V7UsWtpIpe* zfBPP_6MH-;fyG)aX*hz}-@@=&I?#V;2dwYkn)m;3{-eLs9yFgXU5{X@M1p_UEIfx= z`QA73V<~#}z8=dxBiZs^EFZ=!N#}pP&oAP-I5USIp_os;&DbHht37ZZA^!OqW&iE0 zqqV=6_$Jc3I{a}gKiU0<^P7Ak=STbz1TA6wRVHGhbPAq|@fe@kaP}*O?><+BE8lEW z_Mbc1f0_R>f9gYtbGgrqsg{b*&LCGyB^;J=#Rgl0F46rzx2mcdPMUkh_qb=_KRP*= zspgwcDE{*^ke!1hd()?|eX%u}^Oy52#2)UTc%KsCZBWHK&K@iug6=sTmzt}7|MT=G z3G-7@Qa0{19e`y*#ME&SuDUUdn(SO!jW$XPRhD|q@^5R?)1vmXCNMdgO-fuUit~^n z2<$$&WXaCXhUH8%D&H`na+?V`kLQ@FQ+-@_FEIc+=Xl$vveuc)cj>_NbA2Aa(=O4M zS8FsHWMyTsyJBu!iQHS%h;(_3#~!|CAG=qpD;IjlGD<4iA^g1x!h<9srZQnQWVH#Y ZV6qEZF8M!u%5B(F?kjAfF=#wC?mwrY;5h&Q literal 0 HcmV?d00001 diff --git a/app/media/cursors/pink_cursor.cur b/app/media/cursors/pink_cursor.cur new file mode 100644 index 0000000000000000000000000000000000000000..2214eab4f69aceabbf959c0d2ed5dbbd14304e85 GIT binary patch literal 13942 zcmeHNX>1kO6&{BqZqrn0)b>ZDN+v(rG%Z`$gE2NXiygqkfb9o1UNPP82j}5?tAl?H~6hKQc?Y^PiH)H z?>*l+cbPlqTuplne}e~WO78!bru{(Ev=JjTZ4mzUe_zv1uTZ8E@bgulu^!ra9ykN&?MvJ%p|T)ivMiht=8OZ& z?gaY80riIhp8-mDDx_t4utZM{Qqlts6-HaUxrWXcfY0r^(bsdQOqAInFUy?JKuUFyz^nI#)*i=d18sH;*o)s-k| zW9)Zq(|wd zeg*u0cVwq*F>&CS7}oT|Xr_&<}cF za7^xKs#NuAmo8HNH~5^ouK?e6r612Z=RVs|Qz$1&4m;Ovlq%hv zj|(HGoA(0TAH3vBx6Sq;xmZJfzOqH`O!|*o{q#X;Y=*p!zT%ohtSrnn>%I-$F8f^P zg>s3_`5AIP`;>d#@WGAI*QKpI->!ReeW|3+o1xD$%B#Rwy?u8QW$QLqmrLhF4{V42 zlli~3>)zT>CfSzRM%|~-UVuBD#^)%%w!Qcl4~}*&7uDcuU ztO1@hw(ZGv+MMvZ^O^Adk4^KPB5i z`m^->$U48&yf@7KyMcG)RN)b2_eQM6g~_qH&7Y!fCh&M~?Rt6-_5F_}{;EUirw^zb zpx%!%-)PCt$}|vE?$B(O|_w8=4ZvN!IG+cQqaIL)h*t#Rx=+OWXrx4vG3Gh@%c?v49` zh!6>%FiKvTIHo^sI0s$*)(2>-r8n-D@L&lWH$uhe9)b-X#d_TJ4S3H3?C&Jqj@W6z zGHK*c2^l?1!p1%n8`ikv=I%MT1^{2^oOKz4Jkexu&PI&9e%WGaEmQB z7f3X0>xcR9M|b>OJqK6rWqzk~)@3|$Yn9o_vJy99uw)S4a|x96c0kZ z=KzOs!<-NEDthjPJjr_#aw6Lr%S|>|`LpcqRaHLO0R8uXr+n!F)Y}EveZLB^VB`28 zFXoOIv;7J3D$L)wvVr{&gFS_(?+07{oBIi`*HF}9{+_lg{v5~mxm(9Nxi%GKsoc-n zvOFg?-0uV0%en2zoKD8x&!+o~kG}tn&b=D*)XbfEv&!rgsmV!`hWt#AZQz`Cw=Nm- zYP4aCxBO8j5KwDZ_dKgxd%xDKd% zC&$gcmInD`;YBNNL+-=OU)d0N-DyAY{lIsez6R<0rC6+|)Mo&Al`u@zaKX}Gnw*~#f`Cwd^8u5PE-c(`d&OV3;8mGQ5op3*m``_@EKkD$?4&;YxZdx^UhGui+rDv! z4PC>!Bx{~Uoh34I=XFI!qBHK7pubPqA=KZ@JSOwFUrxQ{GyjVdK34l6Gxrv}OQ54S zFlP6hPfj;fLK{b0apvNkl0!ao?-|}LN6Pj%j2YU&GhICU(+mH7xaaj;HPE(SqMb9F z>Wbyl`5ivdIG1HUtKhItFxk>QqFc^opH%Cub-vchI=pLnz7Or`;9lS@-~}(}cwH3j zXC7a6(8}L@-oy4@QhNlO4Ywn1$g!#ecAxW`pa|df2Hw2pdBsq#$@owKdY;|6WcAJN zA>9x5fr0 z@^0~AwIAg-;Ch@XEXH>fc#e}3K9T5SW!k`gIKK2P;e0q+wNLgnH!44`!CF{} zcPP)0+IaIE+?&7x;GxV*U(5lQX932o4q)wm5Ap)Qb?+Fk9moS-1|Ergs4qSksN6gO7hZWJN_qMO7f?qJ(R&V qNjvdB4sR)mQ&vj)+hl-ER$C>C;duvIrM!vOC_9yhVXyk#m;VLlj6G%m literal 0 HcmV?d00001 diff --git a/app/media/cursors/pointer.cur b/app/media/cursors/pointer.cur new file mode 100644 index 0000000000000000000000000000000000000000..408e76062faa19dc15018efeafec7e1428c82c9d GIT binary patch literal 4286 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 literal 0 HcmV?d00001 diff --git a/app/media/cursors/red_finger.cur b/app/media/cursors/red_finger.cur new file mode 100644 index 0000000000000000000000000000000000000000..2403a2cad91858be09d70b95b2f0735912f4053a GIT binary patch literal 4286 zcmeH}e@K;A7{_0=e*>W=?GL$fuX?@LTn(&$VkAWnE{Vzw_D62)W{@qgsEDyOZ9j-6 z6EYd%j|%UWEJKGfAx)j5EeoAVY3YatwTgjP=e)XZ_xYMjQ)kH(X6pf8=>C#-(*F#H-S@TVs%(1d5Kh-xk zW1JCd&iTyMIcsl+);8$uG|}1$SFV^gW$)^OUAtia{(tz64wF~$^U<^Vs`xv7(0d(z z`q9K-FI>F}gFnOdYjC?q_#2R!34Pr#AiVNbeKhoke}VhI!o7PiJPdd5z|atUEc;y5 z=I$UodwB~7f$g*VvmOYlP6{#mFv+X3_N}er%pj$p1G!`CdkTy2MLaiJs#WiMJf3`Z?D&S-+S)mK>c73HMf(5Md2l+r zKS)jO9tqXox${k9Z^2Xie!npuA0L;x=*D-d_F^e z-@cpjZa84@8#a{dDn8O?)lW%jnAIN}8u23M?~u*b@@`sMPo3P~$&qTS zc&om{QK!G2(-c(poSd(8(aB9r{5CTo;nOb@6U$KrI6hQ@y}=j;QY9*k F-rs058I1q{ literal 0 HcmV?d00001 diff --git a/app/media/cursors/xp_arrow.cur b/app/media/cursors/xp_arrow.cur new file mode 100644 index 0000000000000000000000000000000000000000..7d8450784ed2287afea417571ce8054d6cb891ee GIT binary patch literal 4286 zcmeH{Jx;?w5QQgDKnE3~vrCRZXJ3N5(2!#a*ZU?#@=yu>= z?7$H%(WIZicyNAtjOymnJH%9Q-K*g>%z@xROt|q}ck+2|F`qWZ)-Hc7Tz4Pc#^~<* zg${Lh^6AxPcdL!Ns|(NZ4yc|+F}gec;7M#rr;bRvul2}fYKoWyXLE=Vmp4K-o~~GzrCfq68GT0aVgl=)cC2`;$FNz o9yH@S$}E~37QjBUeFBID%ubVxzXj7=@A2Lyv&HZ)$e4op2AwLX0ssI2 literal 0 HcmV?d00001 diff --git a/app/media/cursors/xp_pencil.cur b/app/media/cursors/xp_pencil.cur new file mode 100644 index 0000000000000000000000000000000000000000..4927650bd439beda8719c7aa84e5eb3eabd3c205 GIT binary patch literal 4286 zcmeH{u}Z^G6o!v>b8-@gLbEA!cQrV==K*|!E=~?+^96JkoFu-2w0VL$WpJv4AjHMN zp@aB){%cQ(L8*|YUCxEC_coXEfA_zkBAc=yQ6xIHZeGmdp^7Y zZ@?Sy2D|}pz#H%eyn!`00L_O)F(gjsJYF22LZa(_i=IF7nTz5s@SF{*9$dHM!t)es{>Oic MKH$Ttf>fE}3theZ>;M1& literal 0 HcmV?d00001 diff --git a/app/media/cursors/yellow_cursor.cur b/app/media/cursors/yellow_cursor.cur new file mode 100644 index 0000000000000000000000000000000000000000..a29b748edc5de4c1d9cef6a013d1a9ab1d8c75c9 GIT binary patch literal 13942 zcmeHNX>3);8NCik+@`70sO^tPl}vuLX(Wgt5O(m!zQ!!ZV6%9)F~-jr+u#Ln*w}c* zl(1t1b^=P93POlT0+>XI6tlFHq)l2OC#sq@DJ`I?p<(%Y&b&95>u0~!Mk6GscgQ@Rx#R(x?~x(ZF%x_1HvO z_Fa2>qt2tJg-g+jIN4m7C9jm^N@KoF@>7- zjvvn-2Rd2jTi35Kxwn<(NkLkil-p8dQ?6?b><`Z4A_nu5gZsa*PkP`_WZKHC6qCED zC|gnzlf;&iB>8E}-D+qouyq+13nx#I1be+x!(n&ae10&CmlAyG zo(#EoSEe8Dl|AI~}FzSvfhBeky{bIGkW1fwl%smZEO?%c3WadSK_nLWjM zEx`4`P->CW6Y!R@xqwJxMvR<$}+ z3XgPn9tlS+aFUS+8^*Pcv)HMI9TYop(8=U%j)jOY>+#D+_7S7h(cTr9~@VK>XZ?2Q( zPA$tn+#!Q6-sjPLR=fJ`kEOMKgPI?z*Q`+QCh(E}3NXbRuAb5$Z=~(WZ)Fhp*|#(_ zcX-FwQho3p<@a{X#W`^cwaU zQkH1mKOxUb;Je;(cuJR-7&DX_GEoothtEsRzBlBR4TVyjohF=ir@$UY|F6FW`fH%S z2EJ`IfcMO!z+(@C-_Xf*`$(V?I1T(A;CCMJ58y0N2ZR9sH}6ks0b+pP0sJ;o>l%Ky za33!rvdey=dWJNA0Ahg$vi9({y}{2t)(e2vrEDB@FP%A2me02DMY`~TY2OE)^_H)% z(t(q8&OIfyuLEo9r{*jmo&*kCHS25ct#B`N(bQ01ec*oF z1NQ+OKLYqoX4bd9UOF>E=3_543i~8}`oMM=*q3!}EemB+JI81@?Xfe$B{FP`+5_&T z51hC9d`zQE_L*}%Y3IH@_sO_V^KzJdAG?=6EVkBLZ?B=5d(Ut6qUpqKkV zUFON0_-NTvneWhTkC||vc;BXCbC28ZhdqX{cVmI1M9ucs2gXGG+7Cj$XMisKhA|(; zRdnoyI;ndHVj_Dg3!Hqg>u1{io7Vf~1NiR%Px#XU$hQwL>wXjVg0207x)?j+p6xFX zS7H3doeyk>D6A>GeLpzj-&{}lJcl9=1RK_uif6)sdZ!a3Kjcl$j@@&!~Hg( zzZ~1%jOjS6{T$qv{jB@rT6;0Zsk3&*&5F|Fr6eOsDpsd??E}ZOr+LW|SECQReASOU zV*xeSXD^FVF&wLY#&xpd7Rj1~SQT&a)d$Y4uHMsJ`{{af*hoM0A7x&~9vBC(YG;f% zF?yaPtjw15l{UY8s9KZO!&+mEaP8-$o#z9*j&0LVq~{qD#tft;cV!ptP22X!q+ef= zq33RyaffkdZb(e)X<2K_@VYNGAV$u3pmnXmvl8TV!g@dIzKLb9-{@F0V-tmlV+^}^ z$JDK32(;YS5BuPnPP+L&&xL&77yU^89pD;Zk9T5jZ>_J8 zmGAyp>84JrZhfue5@Cpctyly} z{ia+pkAKpmX02UY?L+K=)7{2&V?ma*RIfMt;_BtmSn~~1XIz|gYd`k;s@ijo;=}$WOxwJM^n)>)6SvU9oxi)co)~kysAO1nP z2di-|%lWK=JN?6OUa08sIW^|(n*x@&GU-kK8x`q19+Y;si?UgeBo}lHn8fS`Qc5e zc>R>4&P&x<#=_L4R^6=cc3`R>GJ2H-YvwrH>u2vd`!MRlZAm)$nbhD6T5Cgv+Sh5q znZi7LM?t5?OTB_j%Y)0M2`-fW1H#FdBF$;-UW3L4O|aYQTP42#1Q)&ntUL zaBvBSnQ)Mawi73iXxojalr?ZF3IF3@(*Rl=Yzm;o;Ur9ZSG4og4rKMrpxi&Iw0 f20Cnz!xq~ulHqwLN~OJy(r9m~8iv2>ci;XGo@WRk literal 0 HcmV?d00001 diff --git a/app/media/cursors/yellow_finger.cur b/app/media/cursors/yellow_finger.cur new file mode 100644 index 0000000000000000000000000000000000000000..bf88a3836ea5941b5b22e2a5b4c1c462fed78ce4 GIT binary patch literal 4286 zcmeH~e@xV69LJvn3l*kJ`?0zHaILoH)^xR&t<}tsjX9TXk<`lC41+m-m{4-Z$qlU` zMPdHHtyU@i0g?$JKNK+HmmGH-*~M{?fWN>Wsr-h!6Ykyn`QB+9oK6pMYxCLO_xU`Z z=kq+T=lS0A{XXBX5Pm`>7A_PbPzaF|EW~59&rl4BWfWWaD};`156Tp-fW~BC01V$3Dy2nAbIWTuWhm{DX?R4f#cRHyp9F$q(G<}U-CMLufg~C1pWj{qp=o@^Kln z0leEVhR0WIFnS&5Zi9jIRRLk@ypTP5#5Dks^+0qT*L(qP=^QtMHj~Pr|8*NMXBY4V=b|O=q3ytK zE3k`gS?;`aQ!hkFVx-5K?y#bYFI$^hVnuE{aKNo>+8ip+tnt+GDVcRJ{ z=E(g0RBq=eMEJM3L!?JqK*DeMv8EM;XMtZ&14k%nWxe>kOJx^;XLEp8e<6PdaFq3e zPUcZX%sG0I{5QY!jlZcJhxJxeT?VSJP#39giVUUBSRMU3+Iimtv$-D(*MKV7&b%t} z4L!c|XOdrPw4&weC`#+iFkc!%Yaeim3~R4T?L9y<>uDxn)*hfm#+f74PTw*x1%5&W z^nY5>dgd~MlPXbJQ;%~aK-VyE-Yw~$r3P$3NHNeo1YB{q$((;ieCPY8n8|NLch@BZ zXd6*oT?>aDuyGC^mYfmBFb3E;4=FjH5#|oreCNx}`1I;n*lae1uh;XzA~EDlv`!~5 zUSM68VaL??BBeR`$(0|&Pi=(3U~mn}U$fba!NEa#W8^wvu~^`6IF$DO{(f|HbSVCm z`SK!Ynk+oa?Bn@?8}d-n&`O3AqoZR8dOHnmZG0fHWS1H7#LKG{f7s6Tzm@6J{U^~k z%Vzx#Q1+T14MBwr$h&8*TZjR%tqcLTA@Hdtt z>+ms!H!v^&@o6pQYbvpH{pXmKRC`^l&tE6BCwqAQWciBp=r@rcTdJ%vESm2b@#XTI z%M(e4WU=Y@LfR!BeeXqOB0r{hNPKW0Tx@H%bFI;0?gG~t7xbv=Uf?E;F_9lz+%qX> zIs=pWz0--CH1-3&=lvI3eBZCX(a4u9cTvUMcmB>pMP&-Tudfdxx#`-ZJu-~}`MveX z)+WToZ^WWcvdRBrcp7ouht^s3Jck@&%U@mI6aJsk8+!_;RZvt(D templates.TemplateResponse: + """A route to the cursor settings. + + Args: + request (Request): the http request. + session (Session): the database. + + Returns: + templates.TemplateResponse: renders the cursor_settings.html page + with the relevant information. + """ + cursors = (["default"] + + [path.stem for path in Path(CURSORS_PATH).glob("**/*.cur")]) + + return templates.TemplateResponse("cursor_settings.html", { + "request": request, + "cursors": cursors, + }) + + +@router.post("/settings") +async def get_cursor_choices( + session: Session = Depends(get_db), + user: User = Depends(get_placeholder_user), + primary_cursor: str = Form(...), + secondary_cursor: str = Form(...), + ) -> RedirectResponse: + """The form in which the user choses primary and secondary + cursors. + + Args: + session (Session, optional): the database. + user (User, optional): [description]. temp user. + primary_cursor (str, optional): name of the primary cursor. + the primary cursor. + secondary_cursor (str, optional): name of the secondary cursor. + + Returns: + RedirectResponse: redirects to the homepage. + """ + user = get_user(session, "cursor_user", user) + cursor_choices = ({ + "primary_cursor": primary_cursor, + "secondary_cursor": secondary_cursor}) + save_cursor_settings( + session, user, cursor_choices) + + return RedirectResponse("/", status_code=HTTP_302_FOUND) + + +@router.get("/load_cursor") +async def load_cursor(session: Session = Depends(get_db),) -> RedirectResponse: + """loads cursors according to cursor settings. + + Args: + session (Session): the database. + + Returns: + RedirectResponse: redirect the user to the homepage. + """ + primary_cursor, secondary_cursor = get_cursor_settings(session) + + return json.dumps( + { + "primary_cursor": primary_cursor, + "secondary_cursor": secondary_cursor, + }) + + +def get_cursor_settings( + session: Session, user_id: int = 1 +) -> Tuple[Optional[List[str]], Optional[int], Optional[str], Optional[int]]: + """Retrieves cursor settings from the database. + + Args: + session (Session): the database. + user_id (int, optional): the users' id. + + Returns: + Tuple[str, Optional[List[str]], Optional[int], + str, Optional[str], Optional[int]]: the cursor settings. + """ + primary_cursor, secondary_cursor = None, None + cursor_settings = session.query( + CursorSettings).filter_by(user_id=user_id).first() + if cursor_settings: + primary_cursor = cursor_settings.primary_cursor + secondary_cursor = cursor_settings.secondary_cursor + + return primary_cursor, secondary_cursor + + +def save_cursor_settings( + session: Session, user: User, cursor_choices: List[str]): + """Saves cursor choices in the db. + + Args: + session (Session): the database. + user (User): current user. + cursor_choices (List[str]): primary and secondary cursors. + """ + cursor_settings = session.query(CursorSettings).filter_by( + user_id=user.id).first() + if cursor_settings: + session.query(CursorSettings).filter_by( + user_id=cursor_settings.user_id).update(cursor_choices) + session.commit() + else: + cursor_settings = CursorSettings(user_id=user.id, **cursor_choices) + session.add(cursor_settings) + session.commit() + + +def get_user(session: Session, user_name: str, new_user: User) -> User: + """returns a user with user_name, or new user if he doesn't exist. + + Args: + session (Session): the database. + user_name (str): name of the user to look for in the db. + new_user (User): new user to insert in case user_name isn't found. + + Returns: + User: the user. + """ + user = session.query(User).filter_by(username=user_name).first() + if not user: + session.add(new_user) + session.commit() + user = session.query(User).filter_by( + username=new_user.username).first() + return user diff --git a/app/static/cursor.js b/app/static/cursor.js new file mode 100644 index 00000000..f71b1c57 --- /dev/null +++ b/app/static/cursor.js @@ -0,0 +1,41 @@ +CURSORS_PATH = "../media/cursors/"; +window.addEventListener("load", get_cursor_choices); + +/** + * @summary This function gets cursor choices from the db. + */ +function get_cursor_choices() { + let request = new XMLHttpRequest(); + request.open("GET", "/cursor/load_cursor", true); + request.onload = change_cursor; + request.send(); +} + +/** + * @summary This function changes the primary cursor and the secondary + * cursor according to users' choices. + */ +function change_cursor() { + let cursor_settings = JSON.parse(JSON.parse(this.response)); + let primary_cursor = cursor_settings["primary_cursor"]; + let primary_cursor_val = + "url(" + CURSORS_PATH + primary_cursor + ")" + ", auto"; + let secondary_cursor = cursor_settings["secondary_cursor"]; + let secondary_cursor_val = + "url(" + CURSORS_PATH + secondary_cursor + ")" + ", auto"; + if (primary_cursor != "default.cur") { + primary_val = primary_cursor_val; + } else { + primary_val = ""; + } + document.body.style.cursor = primary_val; + let links = document.querySelectorAll("a, button, input, select, label"); + if (secondary_cursor != "default.cur") { + secondary_val = secondary_cursor_val; + } else { + secondary_val = ""; + } + links.forEach((element) => { + element.style.cursor = secondary_val; + }); +} diff --git a/app/templates/base.html b/app/templates/base.html index ce47969d..85bd59b0 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -39,6 +39,9 @@ place will change later according to the web design --> {{ gettext("Agenda") }} + @@ -55,6 +58,7 @@ + \ No newline at end of file diff --git a/app/templates/cursor_settings.html b/app/templates/cursor_settings.html new file mode 100644 index 00000000..6abefee9 --- /dev/null +++ b/app/templates/cursor_settings.html @@ -0,0 +1,27 @@ +{% extends "partials/index/index_base.html" %} +{% block content %} +
+

Cursor Settings

+
+
+ +
+
+ +
+ +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/home.html b/app/templates/home.html index 8c5ff091..01b499cb 100644 --- a/app/templates/home.html +++ b/app/templates/home.html @@ -7,7 +7,6 @@
-
{% if quote %} diff --git a/app/templates/partials/base.html b/app/templates/partials/base.html index 65614656..b4ec2289 100644 --- a/app/templates/partials/base.html +++ b/app/templates/partials/base.html @@ -28,6 +28,7 @@ integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw==" crossorigin="anonymous"> + {% endblock head %} {% block title %}Pylendar{% if self.page_name() %} - {% endif %}{% block page_name %}{% endblock %}{% endblock %} diff --git a/app/templates/partials/index/navigation.html b/app/templates/partials/index/navigation.html index 1e0b7d4b..fed23df0 100644 --- a/app/templates/partials/index/navigation.html +++ b/app/templates/partials/index/navigation.html @@ -24,6 +24,9 @@ place will change later according to the web design --> Agenda + diff --git a/requirements.txt b/requirements.txt index 10fc7494..db6181e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ asyncio==3.4.3 atomicwrites==1.4.0 atpublic==2.1.2 attrs==20.3.0 +autopep8==1.5.5 Babel==2.9.0 beautifulsoup4==4.9.3 blinker==1.4 @@ -61,7 +62,7 @@ pydantic==1.7.3 pyflakes==2.2.0 pyparsing==2.4.7 pytest==6.2.1 -pytest-asyncio<0.14.0 +pytest-asyncio==0.12.0 pytest-cov==2.10.1 pytest-forked==1.3.0 pytest-httpx==0.10.1 diff --git a/tests/client_fixture.py b/tests/client_fixture.py index 14a68528..f6ab1425 100644 --- a/tests/client_fixture.py +++ b/tests/client_fixture.py @@ -59,6 +59,20 @@ def profile_test_client(): Base.metadata.drop_all(bind=test_engine) +@pytest.fixture(scope="session") +def settings_test_client(): + Base.metadata.create_all(bind=test_engine) + app.dependency_overrides[profile.get_db] = get_test_db + app.dependency_overrides[ + profile.get_placeholder_user] = get_test_placeholder_user + + with TestClient(app) as client: + yield client + + app.dependency_overrides = {} + Base.metadata.drop_all(bind=test_engine) + + def get_test_placeholder_user(): return User( username='fake_user', diff --git a/tests/test_cursor.py b/tests/test_cursor.py new file mode 100644 index 00000000..6b1289d7 --- /dev/null +++ b/tests/test_cursor.py @@ -0,0 +1,52 @@ +from app.routers.cursor import get_cursor_settings, get_user, router + +CURSOR_SETTINGS_URL = router.url_path_for("cursor_settings") +GET_CHOICES_URL = router.url_path_for("get_cursor_choices") +LOAD_CURSOR_URL = router.url_path_for("load_cursor") +OK = 200 + + +def test_get_cursor_settings(settings_test_client): + response = settings_test_client.get( + url=CURSOR_SETTINGS_URL) + assert response.ok + assert b"Cursor Settings" in response.content + + +def test_cursor_choices(session, settings_test_client): + data = { + "primary_cursor": "arrow", + "secondary_cursor": "p1", + } + first_response = settings_test_client.post( + url=GET_CHOICES_URL, data=data) + primary1, secondary1 = get_cursor_settings(session, user_id=1) + + data["secondary_cursor"] = "default" + second_response = settings_test_client.post( + url=GET_CHOICES_URL, data=data) + primary2, secondary2 = get_cursor_settings(session, user_id=1) + + assert first_response.ok and second_response.ok + assert primary1 == "arrow" and secondary1 == "p1" + assert primary2 == "arrow" and secondary2 == "default" + + +def test_load_cursor(session, settings_test_client): + data = { + "primary_cursor": "cloud", + "secondary_cursor": "ice", + } + response = settings_test_client.post( + url=GET_CHOICES_URL, data=data) + response = settings_test_client.get( + url=LOAD_CURSOR_URL) + + primary_cursor, secondary_cursor = get_cursor_settings(session, user_id=1) + assert response.ok + assert primary_cursor == "cloud" and secondary_cursor == "ice" + + +def test_get_user(session, user): + new_user = get_user(session, "does_not_exist", user) + assert new_user.username == user.username From 914a808d81c26d485ec47c68aab5253e9fd430ce Mon Sep 17 00:00:00 2001 From: subscorp <33960879+subscorp@users.noreply.github.com> Date: Tue, 16 Feb 2021 07:23:19 +0200 Subject: [PATCH 02/27] fix: fixed pytest issue --- app/templates/calendar_monthly_view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/calendar_monthly_view.html b/app/templates/calendar_monthly_view.html index 5a1d3cf2..0c7df8c3 100644 --- a/app/templates/calendar_monthly_view.html +++ b/app/templates/calendar_monthly_view.html @@ -1,4 +1,4 @@ -{% extends "partials/calendar/calendar_base.html" %} +{% extends "base.html" %} {% block content %}
From 831244049f6cfcfeabdb8d7e3f1092a78996950c Mon Sep 17 00:00:00 2001 From: Ori Date: Wed, 17 Feb 2021 05:44:49 +0200 Subject: [PATCH 03/27] update: changed CursorSettings table to UserSettings --- app/database/models.py | 13 ++++++++----- app/routers/cursor.py | 10 +++++----- requirements.txt | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/database/models.py b/app/database/models.py index 2fa947b0..3c39cede 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -333,14 +333,17 @@ def __repr__(self): ) -# Will later change to UserSettings -class CursorSettings(Base): - __tablename__ = "cursor_settings" +class UserSettings(Base): + __tablename__ = "user_settings" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id")) - primary_cursor = Column(String, nullable=False) - secondary_cursor = Column(String, nullable=False) + music_on = Column(Boolean, default=False, nullable=False) + music_vol = Column(Integer, default=None) + sfx_on = Column(Boolean, default=False, nullable=False) + sfx_vol = Column(Integer, default=None) + primary_cursor = Column(String, default="default", nullable=False) + secondary_cursor = Column(String, default="default", nullable=False) # insert language data diff --git a/app/routers/cursor.py b/app/routers/cursor.py index 8acd5ff3..4d3aeef1 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -3,7 +3,7 @@ from typing import List, Optional, Tuple from app.dependencies import get_db -from app.database.models import CursorSettings, User +from app.database.models import UserSettings, User from app.dependencies import CURSORS_PATH, templates from fastapi import APIRouter, Depends, Form, Request from sqlalchemy.orm.session import Session @@ -118,7 +118,7 @@ def get_cursor_settings( """ primary_cursor, secondary_cursor = None, None cursor_settings = session.query( - CursorSettings).filter_by(user_id=user_id).first() + UserSettings).filter_by(user_id=user_id).first() if cursor_settings: primary_cursor = cursor_settings.primary_cursor secondary_cursor = cursor_settings.secondary_cursor @@ -135,14 +135,14 @@ def save_cursor_settings( user (User): current user. cursor_choices (List[str]): primary and secondary cursors. """ - cursor_settings = session.query(CursorSettings).filter_by( + cursor_settings = session.query(UserSettings).filter_by( user_id=user.id).first() if cursor_settings: - session.query(CursorSettings).filter_by( + session.query(UserSettings).filter_by( user_id=cursor_settings.user_id).update(cursor_choices) session.commit() else: - cursor_settings = CursorSettings(user_id=user.id, **cursor_choices) + cursor_settings = UserSettings(user_id=user.id, **cursor_choices) session.add(cursor_settings) session.commit() diff --git a/requirements.txt b/requirements.txt index 6beffa9d..def65284 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,9 +2,9 @@ aiofiles==0.6.0 aioredis==1.3.1 aiosmtpd==1.2.2 aiosmtplib==1.1.4 +apiclient==1.0.4 aiosqlite==0.16.1 anyio==2.0.2 -apiclient==1.0.4 apipkg==1.5 appdirs==1.4.4 arrow==0.17.0 @@ -77,9 +77,9 @@ multidict==5.1.0 mypy==0.790 mypy-extensions==0.4.3 nltk==3.5 -nodeenv==1.5.0 oauth2client==4.1.3 oauthlib==3.1.0 +nodeenv==1.5.0 outcome==1.1.0 packaging==20.8 passlib==1.7.4 From 53de34afd2087aed0f7b1cd808bd855ce1c54ae9 Mon Sep 17 00:00:00 2001 From: Ori Date: Wed, 17 Feb 2021 07:01:57 +0200 Subject: [PATCH 04/27] fix: fixed a small bug --- app/static/cursor.js | 2 +- app/templates/calendar_monthly_view.html | 2 +- app/templates/home.html | 15 +-------------- app/templates/partials/base.html | 4 +--- .../partials/calendar/calendar_base.html | 2 +- 5 files changed, 5 insertions(+), 20 deletions(-) diff --git a/app/static/cursor.js b/app/static/cursor.js index f71b1c57..8abb1a7d 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -1,4 +1,4 @@ -CURSORS_PATH = "../media/cursors/"; +CURSORS_PATH = "../../media/cursors/"; window.addEventListener("load", get_cursor_choices); /** diff --git a/app/templates/calendar_monthly_view.html b/app/templates/calendar_monthly_view.html index 0c7df8c3..5a1d3cf2 100644 --- a/app/templates/calendar_monthly_view.html +++ b/app/templates/calendar_monthly_view.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "partials/calendar/calendar_base.html" %} {% block content %}
diff --git a/app/templates/home.html b/app/templates/home.html index 9405d7e7..ff6b484d 100644 --- a/app/templates/home.html +++ b/app/templates/home.html @@ -1,4 +1,4 @@ -{% extends "partials/index/index_base.html" %} +{% extends "base" %} {% block content %} @@ -6,19 +6,6 @@
- - -
- {% if quote %} - {% if not quote.author %} -

"{{ quote.text }}"

- {% else %} -

"{{ quote.text }}"   \ {{ quote.author }}

- {% endif %} - {% endif %} -
- -
{% if quote %} {% if not quote.author%} diff --git a/app/templates/partials/base.html b/app/templates/partials/base.html index fc5536ac..daae12ff 100644 --- a/app/templates/partials/base.html +++ b/app/templates/partials/base.html @@ -29,9 +29,7 @@ crossorigin="anonymous"> - {% endblock head %} - {% block title %}Pylendar{% if self.page_name() %} - {% endif %}{% block page_name %}{% endblock %}{% - endblock %} + {% endblock head %} {% block title %} Pylendar{% if self.page_name() %} - {% endif %}{% block page_name %}{% endblock %} diff --git a/app/templates/partials/calendar/calendar_base.html b/app/templates/partials/calendar/calendar_base.html index 1355b92e..2029622f 100644 --- a/app/templates/partials/calendar/calendar_base.html +++ b/app/templates/partials/calendar/calendar_base.html @@ -13,7 +13,7 @@
{% include 'partials/calendar/feature_settings/example.html' %}
-
+
a {% block content %} {% endblock content %}
From 6dd0219051ba69328a489f1cda37dc50115037a3 Mon Sep 17 00:00:00 2001 From: Ori Date: Wed, 17 Feb 2021 07:16:16 +0200 Subject: [PATCH 05/27] fix: another small fix --- app/static/cursor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/static/cursor.js b/app/static/cursor.js index 8abb1a7d..a568b490 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -1,4 +1,4 @@ -CURSORS_PATH = "../../media/cursors/"; +CURSORS_PATH = "/media/cursors/"; window.addEventListener("load", get_cursor_choices); /** From fc72d95ca339aa2ca63c2c2a5dbd545dce9bab12 Mon Sep 17 00:00:00 2001 From: Ori Date: Thu, 18 Feb 2021 06:50:54 +0200 Subject: [PATCH 06/27] fix: fixed issues according to comments --- app/routers/cursor.py | 22 ++++--------------- .../partials/calendar/calendar_base.html | 2 +- tests/client_fixture.py | 15 +++---------- 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/app/routers/cursor.py b/app/routers/cursor.py index 4d3aeef1..5be5b31c 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -2,9 +2,9 @@ from pathlib import Path from typing import List, Optional, Tuple -from app.dependencies import get_db -from app.database.models import UserSettings, User -from app.dependencies import CURSORS_PATH, templates +from app.database.models import User, UserSettings +from app.dependencies import CURSORS_PATH, get_db, templates +from app.routers.profile import get_placeholder_user from fastapi import APIRouter, Depends, Form, Request from sqlalchemy.orm.session import Session from starlette.responses import RedirectResponse @@ -17,20 +17,6 @@ ) -def get_placeholder_user(): - """Creates a placeholder user - - Returns: - User: the new user. - """ - return User( - username='cursor_user', - email='cursor@cursor', - password='cursor123', - full_name='cursaouer', - ) - - @router.get("/settings") def cursor_settings( request: Request, session: Session = Depends(get_db) @@ -74,7 +60,7 @@ async def get_cursor_choices( Returns: RedirectResponse: redirects to the homepage. """ - user = get_user(session, "cursor_user", user) + user = get_user(session, "new_user", user) cursor_choices = ({ "primary_cursor": primary_cursor, "secondary_cursor": secondary_cursor}) diff --git a/app/templates/partials/calendar/calendar_base.html b/app/templates/partials/calendar/calendar_base.html index 2029622f..1355b92e 100644 --- a/app/templates/partials/calendar/calendar_base.html +++ b/app/templates/partials/calendar/calendar_base.html @@ -13,7 +13,7 @@
{% include 'partials/calendar/feature_settings/example.html' %}
-
a +
{% block content %} {% endblock content %}
diff --git a/tests/client_fixture.py b/tests/client_fixture.py index 2e47f473..8b5d9262 100644 --- a/tests/client_fixture.py +++ b/tests/client_fixture.py @@ -1,4 +1,4 @@ -from app.routers import agenda, event, invitation, profile, google_connect +from app.routers import agenda, cursor, event, invitation, profile, google_connect from typing import Iterator from fastapi.testclient import TestClient @@ -76,17 +76,8 @@ def profile_test_client() -> Iterator[TestClient]: @pytest.fixture(scope="session") -def cursor_test_client(): - Base.metadata.create_all(bind=test_engine) - main.app.dependency_overrides[profile.get_db] = get_test_db - main.app.dependency_overrides[ - profile.get_placeholder_user] = get_test_placeholder_user - - with TestClient(main.app) as client: - yield client - - main.app.dependency_overrides = {} - Base.metadata.drop_all(bind=test_engine) +def cursor_test_client() -> Iterator[TestClient]: + yield from create_test_client(cursor.get_db) @pytest.fixture(scope="session") From 4d2195e6be32199ae4dcbedd56b824d615a2b3d1 Mon Sep 17 00:00:00 2001 From: Ori Date: Sun, 21 Feb 2021 02:18:59 +0200 Subject: [PATCH 07/27] Update: now using the new login system --- app/routers/cursor.py | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/app/routers/cursor.py b/app/routers/cursor.py index 5be5b31c..51e975b9 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -9,6 +9,7 @@ from sqlalchemy.orm.session import Session from starlette.responses import RedirectResponse from starlette.status import HTTP_302_FOUND +from app.internal.security.dependancies import current_user router = APIRouter( prefix="/cursor", @@ -19,7 +20,9 @@ @router.get("/settings") def cursor_settings( - request: Request, session: Session = Depends(get_db) + request: Request, + user: User = Depends(current_user), + session: Session = Depends(get_db) ) -> templates.TemplateResponse: """A route to the cursor settings. @@ -43,7 +46,7 @@ def cursor_settings( @router.post("/settings") async def get_cursor_choices( session: Session = Depends(get_db), - user: User = Depends(get_placeholder_user), + user: User = Depends(current_user), primary_cursor: str = Form(...), secondary_cursor: str = Form(...), ) -> RedirectResponse: @@ -60,7 +63,6 @@ async def get_cursor_choices( Returns: RedirectResponse: redirects to the homepage. """ - user = get_user(session, "new_user", user) cursor_choices = ({ "primary_cursor": primary_cursor, "secondary_cursor": secondary_cursor}) @@ -71,7 +73,7 @@ async def get_cursor_choices( @router.get("/load_cursor") -async def load_cursor(session: Session = Depends(get_db),) -> RedirectResponse: +async def load_cursor(session: Session = Depends(get_db), user: User = Depends(current_user)) -> RedirectResponse: """loads cursors according to cursor settings. Args: @@ -80,7 +82,7 @@ async def load_cursor(session: Session = Depends(get_db),) -> RedirectResponse: Returns: RedirectResponse: redirect the user to the homepage. """ - primary_cursor, secondary_cursor = get_cursor_settings(session) + primary_cursor, secondary_cursor = get_cursor_settings(session, user) return json.dumps( { @@ -90,7 +92,7 @@ async def load_cursor(session: Session = Depends(get_db),) -> RedirectResponse: def get_cursor_settings( - session: Session, user_id: int = 1 + session: Session, user: User ) -> Tuple[Optional[List[str]], Optional[int], Optional[str], Optional[int]]: """Retrieves cursor settings from the database. @@ -104,7 +106,7 @@ def get_cursor_settings( """ primary_cursor, secondary_cursor = None, None cursor_settings = session.query( - UserSettings).filter_by(user_id=user_id).first() + UserSettings).filter_by(user_id=user.user_id).first() if cursor_settings: primary_cursor = cursor_settings.primary_cursor secondary_cursor = cursor_settings.secondary_cursor @@ -130,24 +132,4 @@ def save_cursor_settings( else: cursor_settings = UserSettings(user_id=user.id, **cursor_choices) session.add(cursor_settings) - session.commit() - - -def get_user(session: Session, user_name: str, new_user: User) -> User: - """returns a user with user_name, or new user if he doesn't exist. - - Args: - session (Session): the database. - user_name (str): name of the user to look for in the db. - new_user (User): new user to insert in case user_name isn't found. - - Returns: - User: the user. - """ - user = session.query(User).filter_by(username=user_name).first() - if not user: - session.add(new_user) - session.commit() - user = session.query(User).filter_by( - username=new_user.username).first() - return user + session.commit() \ No newline at end of file From 4ce2c746a80892344bf069e65fefe1c14d85d08f Mon Sep 17 00:00:00 2001 From: Ori Date: Sun, 21 Feb 2021 03:58:03 +0200 Subject: [PATCH 08/27] fix: fixed issues according to comments --- app/static/cursor.js | 6 ++---- app/static/style.css | 9 ++++++++- app/templates/cursor_settings.html | 16 ++++++++-------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/static/cursor.js b/app/static/cursor.js index a568b490..04e6108c 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -18,11 +18,9 @@ function get_cursor_choices() { function change_cursor() { let cursor_settings = JSON.parse(JSON.parse(this.response)); let primary_cursor = cursor_settings["primary_cursor"]; - let primary_cursor_val = - "url(" + CURSORS_PATH + primary_cursor + ")" + ", auto"; + let primary_cursor_val = `url(${CURSORS_PATH}${primary_cursor}), auto`; let secondary_cursor = cursor_settings["secondary_cursor"]; - let secondary_cursor_val = - "url(" + CURSORS_PATH + secondary_cursor + ")" + ", auto"; + let secondary_cursor_val = `url(${CURSORS_PATH}${secondary_cursor}), auto`; if (primary_cursor != "default.cur") { primary_val = primary_cursor_val; } else { diff --git a/app/static/style.css b/app/static/style.css index 8c527fc5..2519acb1 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -96,7 +96,7 @@ p { border: none; background-color: whitesmoke; } - + .error-message { line-height: 0; color: red; @@ -125,3 +125,10 @@ p { h2.modal-title { font-size: 1.25rem; } + +#primary-cursor, +#secondary-cursor { + width: 10em; + height: 2.5em; + margin-top: 0.5em; +} diff --git a/app/templates/cursor_settings.html b/app/templates/cursor_settings.html index 847e4acf..cac7a01d 100644 --- a/app/templates/cursor_settings.html +++ b/app/templates/cursor_settings.html @@ -1,27 +1,27 @@ -{% extends "base.html" %} +{% extends "base.html" %} {% block content %}

Cursor Settings

-
-
- {% for cursor in cursors %} {% endfor %}
-
- {% for cursor in cursors %} {% endfor %}
-
-{% endblock %} \ No newline at end of file +{% endblock %} From 4f067995c09683c60a3d1ff8e710d6c3d854bb0c Mon Sep 17 00:00:00 2001 From: Ori Date: Tue, 23 Feb 2021 02:03:54 +0200 Subject: [PATCH 09/27] fix: fixed issues according to comments --- app/media/cursors/Link.cur | Bin 0 -> 4286 bytes app/media/cursors/Link2.cur | Bin 0 -> 4286 bytes app/media/cursors/Minecraft.cur | Bin 9662 -> 0 bytes app/media/cursors/among_us.cur | Bin 0 -> 4286 bytes app/media/cursors/cloud.cur | Bin 9662 -> 0 bytes app/media/cursors/credits.txt | 8 +++-- app/media/cursors/finger.cur | Bin 4286 -> 0 bytes app/media/cursors/{xp_pencil.cur => fire.cur} | Bin 4286 -> 4286 bytes app/media/cursors/nes_mario.cur | Bin 0 -> 4286 bytes app/media/cursors/p1.cur | Bin 4286 -> 0 bytes .../cursors/{arrow.cur => painted_arrow.cur} | Bin app/media/cursors/painted_finger.cur | Bin 0 -> 4286 bytes .../{alternate_select.cur => paper-plane.cur} | Bin app/media/cursors/pen.cur | Bin 0 -> 4286 bytes app/media/cursors/pointer.cur | Bin 4286 -> 0 bytes app/media/cursors/snes_mario.cur | Bin 0 -> 4286 bytes app/media/cursors/sword.cur | Bin 0 -> 4286 bytes app/static/cursor.js | 32 +++++++----------- 18 files changed, 17 insertions(+), 23 deletions(-) create mode 100644 app/media/cursors/Link.cur create mode 100644 app/media/cursors/Link2.cur delete mode 100644 app/media/cursors/Minecraft.cur create mode 100644 app/media/cursors/among_us.cur delete mode 100644 app/media/cursors/cloud.cur delete mode 100644 app/media/cursors/finger.cur rename app/media/cursors/{xp_pencil.cur => fire.cur} (64%) create mode 100644 app/media/cursors/nes_mario.cur delete mode 100644 app/media/cursors/p1.cur rename app/media/cursors/{arrow.cur => painted_arrow.cur} (100%) create mode 100644 app/media/cursors/painted_finger.cur rename app/media/cursors/{alternate_select.cur => paper-plane.cur} (100%) create mode 100644 app/media/cursors/pen.cur delete mode 100644 app/media/cursors/pointer.cur create mode 100644 app/media/cursors/snes_mario.cur create mode 100644 app/media/cursors/sword.cur diff --git a/app/media/cursors/Link.cur b/app/media/cursors/Link.cur new file mode 100644 index 0000000000000000000000000000000000000000..985bb26cca6d84caf11a93c6b74bc9c97537f684 GIT binary patch literal 4286 zcmeH`Ax=X<5QeuT1SE?x6$S%Q0I+m}`n{|M#y?bfOao~LP7^$~9ALNUe z@QoPqemL9twm#!~cRl|neDKg`9PE1^hpSCQ9R_)k%|1sA3UC~pt~61gX#HT zES|p^s~$rMK72AyvdQONV82xUIVZ~*T#EawO*EhVPabn7*WbBctNpjb?!V4gr&eRK zIJfVPBWsV?*~d0BZ}R*Mb_qWF+TZOsZUMIC4=&l*&@rfqj|1y$vqJ$t^ylZgTW`ZU zYRK}=%MYpZNXDdC>v{R_N&gGAefExo3UxfCGq9!Kw-~1UOX#sVe_sE2|NYgv+;c3=|UR~9J>Ogg%I#3;`4pax!f#v@01nb-* zvQ{dlv{JPa`s?f2KS2p?aH_W68M*dK>Gx^I^-}tJ$5>N)rSxN;Rg*bp{!w53#$9*E z$NN8k95tQE?SXPbFV@$6iFZJKYWGFX<-9*RRTDMtHAju}xz0d7awGB^(46Psd~SX4 z#ormV5YfX7Yshmf+>roZ>I~H89AXY_aFA2^pr_DYYF_)_=KkZl&E0{^B#mzDPT0sD NXs7PY!hLgae*!_dB7Fb= literal 0 HcmV?d00001 diff --git a/app/media/cursors/Minecraft.cur b/app/media/cursors/Minecraft.cur deleted file mode 100644 index 2293c9572a62a5592cb05255479a194dee4f8d94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9662 zcmeHLu};G<5WOO#N?lkg1QL~4D2yHGj7SH%z|6=5jC=uMMR7EVqmK)7H^|%#`u1s3-#+q3&i&_4#nBy(6-SdacDV?D zSCM*uch34TYL5R?_KU%KS*(9_;qTXOG#V)m|H$xO)choRT+|p-YpV7Pb2k{y&_7c6 z`}IN1(==5anD-wc--|V-JG@)02TpGvYxN9sHz@q=dUBikPp4DGQC5HS46kDh_8wlB zp1B^!dADtrL7vRrAm&5PApApA%8x;v_g()aNfZZOSN^JP20c@>7VFmFdE|pUnY%&E z<9KWfe^g_=HtHOrKWdJ3vg);1=+y{+``q0G{jq+cm(VjsbtZJas~(5`5&l)HTkd!` z7w8|HmtFZh5UHv2o6 zlFhp~Yuo$^ek(;cI`1OIzb)NGc}$LMz5w4T&iY+^Yip+ZcrNO*XL$!_`s^m3L!#{b Qvpi;eb$$iaj~(ygA7^w0=Kufz diff --git a/app/media/cursors/among_us.cur b/app/media/cursors/among_us.cur new file mode 100644 index 0000000000000000000000000000000000000000..bcfd6dd9c053abcfbc7c989c2f2ac71dc1150113 GIT binary patch literal 4286 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 diff --git a/app/media/cursors/credits.txt b/app/media/cursors/credits.txt index cc968479..340168c3 100644 --- a/app/media/cursors/credits.txt +++ b/app/media/cursors/credits.txt @@ -1,11 +1,13 @@ http://www.rw-designer.com/cursor-set/material-amber http://www.rw-designer.com/cursor-set/windows-xp-style http://www.rw-designer.com/cursor-set/material-3d -http://www.rw-designer.com/cursor-set/cloudflix -http://www.rw-designer.com/cursor-set/primogem http://www.rw-designer.com/cursor-set/paper-plane http://www.rw-designer.com/cursor-set/yodalighsaber http://www.rw-designer.com/cursor-set/valentine-heart http://www.rw-designer.com/cursor-set/icy-ice http://www.rw-designer.com/cursor-set/wii-hand-1 -http://www.rw-designer.com/cursor-set/minecraft-sets +http://www.rw-designer.com/cursor-set/pen-tablet-1 +http://www.rw-designer.com/cursor-set/among-us-mixed-pointer-pack- +http://www.rw-designer.com/cursor-set/nes-smb-mario +http://www.rw-designer.com/cursor-set/mario-world-v64 +http://www.rw-designer.com/cursor-set/the-legend-of-zelda diff --git a/app/media/cursors/finger.cur b/app/media/cursors/finger.cur deleted file mode 100644 index e2a214d0bd533990d9bd2f025732237924b04a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmeHJSx{3~7{0cB?X)jl9y+5>)#-HF8LM>{TnZ{gKtvWp*(B_QA^|E(Si+J3N!ZI8 z6cj{K6oH`>H4UIv2N~B|&^FeoTSp7BDIj0}bAa0ML6F#)cE&sN=Op*uKi|K6|96fc zye7;O7Az2iH-$HZnzsevT|p2Qk4G(_W&cK|(G}g@F0w*waM_JDcK%o;l3=B+4_0jP z!tzZX_}p5IrB-6Bw)2CnZ!+9t_qM&PIAq0VZf$UhKxk$MqH-@mrtL;})>%lEod`}b zATX&7e)1N0@2Q74q6(Yc6R~)m6C8upz2?RcTY2T;Mz<)$7WE*Z>IRbPZXn^{HN+qI z88Q1VA-en=s1FD)Xb06EK8ba34J(7auLhs2wa4~QZR=cPNI!bFZd-^NUl(0La`kN} z8}A{l;SN&jf2TN1NT}&WT-6oqsW=Z=$r(ssEIo_B%w~>H zb%(W!9E+B0gq?r7>xo?dF>N?%`uEo9FHi5L8zMV zBlXB_&I!haAax7AjN8Zh4lCz4Ec|Re&4Xg+Z1J^i!|$;iBuR}>>id!V!#MIgfP&6R zD>?;~p8@iWSOWcXBbj{o7}b+Fl$G;930%FB1QcqKwvJAm91 z6CAgYFqu_!0lj0uD1gQu@ER~)upV%HT{oYBXY-#ezT(@P7eta2=!_%CZheA+lazBi zpguARb;Br1P804m;Ob+b;VR)1Cnod1L-^MbJ%#TPUj-*=Ht+w!B^y}&ZZp+C>5$1J z@=HZQlL`8ZgiF{tlz)bP7=h`nP}L4m|LH#Ug2j<<9E0ZjLC!%o2W%Eu|5^P#<@@3E zRW3HUCUX8!|DTIbSoS;UeO<>e^cR5AQxi}(jUcV2AL7Ugu$rXTP(S|W;P_PT{YXE0 zkI!KI{-3x8F#dTZRAWbYKDLMz_-L6m>;sfD@;|(=9lH|ikyhP@)C2b+D>#qMp799F zGjcsouX)J1$oQCj;xXqw(<3(jObc1njPbEHHWy^ZuJX8Itm(|kj zW276I*cOrn-_&|m({sdeG22h3@%ijg-`U(TY^HyarCnTmf>c!d$Xd8ZRl-eH3Wr@9 zEd9a_JEKdjX2PF-PJFf}PKjv!fZe0G#3G4;*PqQl!)MyVG?-yaGdd8cXhU#TD}3dL zVdt9(S7{k`gcM`BwFhi`lE$Y0doC+!58bH0+&4sFVnClSJy}07*@Q%?#B5#I`rvjGS zL;0Cty>mF+qDp%Fl?`hHGMd)UCEsU#i1Qpj`Ym+_47ogIQQpsgQ7#YxgY(#J5>aV(p}pHWxLje%;u(j#^s%8h@bvhxAyb1!1Q zIfivkQda*p=KlX|{G-ac21P#c$ZxuajDzQ3;ouMFh{A!{_`ZyHNCV5lat%+m`lVpA zS3Ia5W71rMS#v#?xM+X2p>vkaTzp^o{I@DVN4Tfgqj^A|Acsk?fI*m#1TsN*i`fL( o_>?`OwU5j+wX*Nymh>+3F_6FEb-*y6%;Kq;3kUR@7XSbN diff --git a/app/media/cursors/xp_pencil.cur b/app/media/cursors/fire.cur similarity index 64% rename from app/media/cursors/xp_pencil.cur rename to app/media/cursors/fire.cur index 4927650bd439beda8719c7aa84e5eb3eabd3c205..f234a1b8ba6afa207d292fff226a0aa88634f85c 100644 GIT binary patch literal 4286 zcmeH{u?+$-3`Gry8Y(Dgu4DvCI>ulSnj3)~XxM>f7vBdkyPy1k m&ua!;CqQdH(*Lu$09*BT>|ArlDeznZw-s>T1CN73FTfid0w3o9 literal 4286 zcmeH{u}Z^G6o!v>b8-@gLbEA!cQrV==K*|!E=~?+^96JkoFu-2w0VL$WpJv4AjHMN zp@aB){%cQ(L8*|YUCxEC_coXEfA_zkBAc=yQ6xIHZeGmdp^7Y zZ@?Sy2D|}pz#H%eyn!`00L_O)F(gjsJYF22LZa(_i=IF7nTz5s@SF{*9$dHM!t)es{>Oic MKH$Ttf>fE}3theZ>;M1& diff --git a/app/media/cursors/nes_mario.cur b/app/media/cursors/nes_mario.cur new file mode 100644 index 0000000000000000000000000000000000000000..7289954ee9707d0ede3f4fd28d052f7da624c885 GIT binary patch literal 4286 zcmc(hzlzjQ6vn@Ci*1ClvXaSw2wPZ)#jcg{KeB}{prsGs8#tv8VBsT(y{)WbXDcF# zjc6;lf(|<7`kgE%b8>PoNhVngoW1v+^WE=va+3)QoWKP0Im+wlDd03`JB&Hw3WHVt z1s1H0ay@ty5DRY}(@1XU)z!DFZqt6ezxQdYdwt(^n}1uqFVE_C-Us-wb(#CRtjD^# zcz4lk+E2{e-d|gNQ?A{YVx;nR@4)$`C#xV4eZ~=fr60)E?dsR1hniLm28`CPFTd0e zV9VD3&2N1B`MhK2Q02z{4tc-G`&N4|y?)^N$L_!6d5-qw^F#Tv-+HDvvHTcp&zD|= zWqV5H#^U=Py70$jtC!w$8(;OuF{aL1dpZuL_hb+LyHx%Dy>`TPjA?V#JDFr&e8bZD zqw@YU;PzV}yPTBUf*adA-OPmln_TJPC*uR-8lMC9j3_!)ZyjUIS}p1#e4by~!zYf{JF%^mGGvq< zbgliQ?_FH>$S=tK%(&5yE#H@}VH>W^h5v=@oBh}|kM;jV$$p%Ez06j?twC(@D$hQ3 z7He2!cHtwFt?yc7V?7mP*A>xKSkAV~C}vrZSk0zKadH|8a1E{*OHk)RYoZItLs&cv i;edhsY=P;P1T~WtvI#X?VUcea$fw~Tø%kU4`R;K*` literal 0 HcmV?d00001 diff --git a/app/media/cursors/p1.cur b/app/media/cursors/p1.cur deleted file mode 100644 index 6ae3d5e71ebb2ce4023753d8b7c1d7f2a5fcf9f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmeH}T})eL7{|{X3si7bFO12IqGB>H6i~P9POrQ$CW~1MbQp}8Cd+ioWSWRY#mq4S zWq^nnjoDo0WSe$#hHkLofMp#;7%&zXL!}?I1zR|U($h9e{QJCbd(I9_KH7QV1#j~2 zIX&;k?|Gj0c|QckDbZY9 zg?QyP#Ri%0?rl=IJm!~#Q5kV`@&C@#wP4z%F%=LL%I zqn3QE%~-?om92|JOO)lHJO}K_b8>PJf&T6A+YIRbS`XQON82%2_t2b_$7K6RkFi!B zd&Wn9l>X zc^EmIujB{YEAEg#+UIn`H4{Q)I&z!BlM!0`M`F1q$DQWC%SbUGG5%=V7UsWtpIpe* zfBPP_6MH-;fyG)aX*hz}-@@=&I?#V;2dwYkn)m;3{-eLs9yFgXU5{X@M1p_UEIfx= z`QA73V<~#}z8=dxBiZs^EFZ=!N#}pP&oAP-I5USIp_os;&DbHht37ZZA^!OqW&iE0 zqqV=6_$Jc3I{a}gKiU0<^P7Ak=STbz1TA6wRVHGhbPAq|@fe@kaP}*O?><+BE8lEW z_Mbc1f0_R>f9gYtbGgrqsg{b*&LCGyB^;J=#Rgl0F46rzx2mcdPMUkh_qb=_KRP*= zspgwcDE{*^ke!1hd()?|eX%u}^Oy52#2)UTc%KsCZBWHK&K@iug6=sTmzt}7|MT=G z3G-7@Qa0{19e`y*#ME&SuDUUdn(SO!jW$XPRhD|q@^5R?)1vmXCNMdgO-fuUit~^n z2<$$&WXaCXhUH8%D&H`na+?V`kLQ@FQ+-@_FEIc+=Xl$vveuc)cj>_NbA2Aa(=O4M zS8FsHWMyTsyJBu!iQHS%h;(_3#~!|CAG=qpD;IjlGD<4iA^g1x!h<9srZQnQWVH#Y ZV6qEZF8M!u%5B(F?kjAfF=#wC?mwrY;5h&Q diff --git a/app/media/cursors/arrow.cur b/app/media/cursors/painted_arrow.cur similarity index 100% rename from app/media/cursors/arrow.cur rename to app/media/cursors/painted_arrow.cur diff --git a/app/media/cursors/painted_finger.cur b/app/media/cursors/painted_finger.cur new file mode 100644 index 0000000000000000000000000000000000000000..88266eacd25650f8b294123f68afa7d724bcccff GIT binary patch literal 4286 zcmeH{K~4iP3`Ktt3s#gBTe?JZ3U1bsxJ-{xiHmR$gz=~P!h}qkC_xoyBDr=P=Remo z8sG{W>~_G7`@#2f&VsvX__px-}sLyLwie>^7NznkY<7^ zZgr=(2k8`H3x~Zk=wY*^9)2&ohd+W{%NHwp-W{>1S$hA(9Kp(I)bMlnM&t%CR)R3m)b6$P0u65qIdhw;3t6?tfYpJd^U(0XRt*u}AU*AE_qIPbh>el(6=Fd4? zYW|mEk9vnnI{dEvT+e2&Fgs#zvm3B~cYAZ2-CXaRT?2OO<~44cPqXcd+18bp?BPWH E0QsWCqyPW_ literal 0 HcmV?d00001 diff --git a/app/media/cursors/alternate_select.cur b/app/media/cursors/paper-plane.cur similarity index 100% rename from app/media/cursors/alternate_select.cur rename to app/media/cursors/paper-plane.cur diff --git a/app/media/cursors/pen.cur b/app/media/cursors/pen.cur new file mode 100644 index 0000000000000000000000000000000000000000..3e14bc824a14b9e73dc63aa194875782fd8e206c GIT binary patch literal 4286 zcmc(iJxE(o6vwaa(6ODm>e$W1#P~6o4)H4{nncBbU#LMuA#o9upe9r)j)HR)A<)4^ z7tuv4E(ytKXSc478N`3j|FkbWw4?7faLnVL7yiF{&bj9vjd^35OiPQAPqX>{!I+Qo z^hvBm?6a77<*zXX@xA;2`TQ@uT(`55O#VbFmBQB67It@c{|iSxpGPj6Lq|sk=H}+C zGZc$OL?RIcgF&>nw_|K<%sRu)&JH>|I}r#35DJCR)zxL4VRCX(85oD-<6|r?E^1!v zRN^0%k&zJ`emewN*4NjO&1SVeZ8FTy&m);kVrps%Yiny*US38bk+7Bz=APQrBnKIC zxt!LhjW{s=4#V*9u(GVKu4)_x1_rFe;W@+X>?~GRR8hSKi7QlobVkR3`0Xhs#PkLRGa65YtFh22JV$cZMQz} zFdK28hO=kp!glNWjeK~`z+BMS z54dN}Q@6LbxVyWv#=tnlVzCB;-|trj#^L1TL~Fw9IPjh(2WJTODW1)%t1GZJH#aw5 zy%xA1r$4Yqus=}Wor|`%Hng_3YM)~7e(e~z8JK%A^!N8`9q@ea?d@S^W(J%k78Vxt z{;^0bo;-WpU+B;3bM}>Hr&6J!?Xpw|$c&^Gn`Y8!g1`$M#u4cGe0pF2 z@TTz-W*S#H0t?p!DIf)IRlshI_FpiI;1;hO#4r7yUVr=fPVcSw(~x@b(6W1 z{MfWKkkjX)Cd8)T1}B02a|Y}ma#OF>tQyA79(~A58=En7_;Z40+#9p>Fzoc94|TvE z;^#AhR%*C-{!IXa zAGiSWW6k_&_RnYMG@vJRK!cm@(yplicDmV($nSGKI(zm@^Rhm<_)YWur}=gcoB`(#?uwst zI@VgAoWJ_hPs2VxbsVvWG|0v4QXZeZ`0Mwm<`SuEz5{;#k#(d`?XurUywib8)l$u4 zQD^Ke8Vz_J8a{-*>cCO+_v2=D`&*QK`7Qa4d-hJ#5}Nv4C+s_H2aZMu_&G=S{R#7< z_dRDGt*x9+NAu0=y?502c;1J&$Hl61z8KeKc(o*DW^q;K@?HN~E((#AzC(2`JCn#( zu`?#W(|45m9SXIj$>kRB+L>63`%>?V3gt{Py`QT;xm9e9%yx;~zf0s?KFC~;YNq%H D-23-I literal 0 HcmV?d00001 diff --git a/app/media/cursors/sword.cur b/app/media/cursors/sword.cur new file mode 100644 index 0000000000000000000000000000000000000000..abc474642a4025eee2dd61b6034b72571dacee64 GIT binary patch literal 4286 zcmeH_F$%&!5Je|~Sei<-a!Ze3r-k>5;zcaIfwedB2$m^=lfU8*O9*Mq1jJeRna$2J zZ{{Z=6Uih5d9Q0OvcPTw1!xZ;i>JsDIbZI8JKzqu1MYx3@VgEy%bQYTPjR?t!;U>z z@;M_pF-Csc!x;R?NSW-xlFi!mfK0wKZyvX0^S#-FIg zoHO--XM@Hb_Mn`uV$N><_bz)(&i>W&Xl8qM6N7t-ru?33Rw?~vRfTE^+|RTKaK literal 0 HcmV?d00001 diff --git a/app/static/cursor.js b/app/static/cursor.js index 04e6108c..735105d9 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -1,11 +1,11 @@ -CURSORS_PATH = "/media/cursors/"; +const CURSORS_PATH = "/media/cursors/"; window.addEventListener("load", get_cursor_choices); /** * @summary This function gets cursor choices from the db. */ function get_cursor_choices() { - let request = new XMLHttpRequest(); + const request = new XMLHttpRequest(); request.open("GET", "/cursor/load_cursor", true); request.onload = change_cursor; request.send(); @@ -16,24 +16,16 @@ function get_cursor_choices() { * cursor according to users' choices. */ function change_cursor() { - let cursor_settings = JSON.parse(JSON.parse(this.response)); - let primary_cursor = cursor_settings["primary_cursor"]; - let primary_cursor_val = `url(${CURSORS_PATH}${primary_cursor}), auto`; - let secondary_cursor = cursor_settings["secondary_cursor"]; - let secondary_cursor_val = `url(${CURSORS_PATH}${secondary_cursor}), auto`; - if (primary_cursor != "default.cur") { - primary_val = primary_cursor_val; - } else { - primary_val = ""; - } - document.body.style.cursor = primary_val; - let links = document.querySelectorAll("a, button, input, select, label"); - if (secondary_cursor != "default.cur") { - secondary_val = secondary_cursor_val; - } else { - secondary_val = ""; - } + const cursor_settings = JSON.parse(JSON.parse(this.response)); + const primary_cursor_choice = cursor_settings["primary_cursor"]; + const primary_cursor_path = `url(${CURSORS_PATH}${primary_cursor_choice}), auto`; + const secondary_cursor_choice = cursor_settings["secondary_cursor"]; + const secondary_cursor_path = `url(${CURSORS_PATH}${secondary_cursor_choice}), auto`; + const primary_cursor = primary_cursor_choice !== "default.cur" ? primary_cursor_path : ""; + const secondary_cursor = secondary_cursor_choice !== "default.cur" ? secondary_cursor_path : ""; + document.body.style.cursor = primary_cursor; + const links = document.querySelectorAll("a, button, input, select, label"); links.forEach((element) => { - element.style.cursor = secondary_val; + element.style.cursor = secondary_cursor; }); } From e28957812740cd2aebbb83bcaa751645328509d7 Mon Sep 17 00:00:00 2001 From: Ori Date: Tue, 23 Feb 2021 02:26:09 +0200 Subject: [PATCH 10/27] Update: moved functions to internals --- app/internal/cursor.py | 54 ++++++++++++++++++++++++++++++++++++++++ app/routers/cursor.py | 56 ++---------------------------------------- 2 files changed, 56 insertions(+), 54 deletions(-) create mode 100644 app/internal/cursor.py diff --git a/app/internal/cursor.py b/app/internal/cursor.py new file mode 100644 index 00000000..a0321ab5 --- /dev/null +++ b/app/internal/cursor.py @@ -0,0 +1,54 @@ +from app.database.models import User, UserSettings +from sqlalchemy.orm.session import Session +from typing import List, Optional, Tuple + + +def get_cursor_settings( + session: Session, + user_id: int, +) -> Tuple[Optional[List[str]], Optional[int], Optional[str], Optional[int]]: + """Retrieves cursor settings from the database. + + Args: + session (Session): the database. + user_id (int, optional): the users' id. + + Returns: + Tuple[str, Optional[List[str]], Optional[int], + str, Optional[str], Optional[int]]: the cursor settings. + """ + primary_cursor, secondary_cursor = None, None + cursor_settings = ( + session.query(UserSettings).filter_by(user_id=user_id).first() + ) + if cursor_settings: + primary_cursor = cursor_settings.primary_cursor + secondary_cursor = cursor_settings.secondary_cursor + + return primary_cursor, secondary_cursor + + +def save_cursor_settings( + session: Session, + user: User, + cursor_choices: List[str], +): + """Saves cursor choices in the db. + + Args: + session (Session): the database. + user (User): current user. + cursor_choices (List[str]): primary and secondary cursors. + """ + cursor_settings = ( + session.query(UserSettings).filter_by(user_id=user.user_id).first() + ) + if cursor_settings: + session.query(UserSettings).filter_by( + user_id=cursor_settings.user_id, + ).update(cursor_choices) + session.commit() + else: + cursor_settings = UserSettings(user_id=user.user_id, **cursor_choices) + session.add(cursor_settings) + session.commit() diff --git a/app/routers/cursor.py b/app/routers/cursor.py index fcd5c0d0..076853b8 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -1,8 +1,7 @@ import json from pathlib import Path -from typing import List, Optional, Tuple - -from app.database.models import User, UserSettings +from app.internal.cursor import get_cursor_settings, save_cursor_settings +from app.database.models import User from app.dependencies import CURSORS_PATH, get_db, templates from fastapi import APIRouter, Depends, Form, Request from sqlalchemy.orm.session import Session @@ -100,54 +99,3 @@ async def load_cursor( "secondary_cursor": secondary_cursor, }, ) - - -def get_cursor_settings( - session: Session, - user_id: int, -) -> Tuple[Optional[List[str]], Optional[int], Optional[str], Optional[int]]: - """Retrieves cursor settings from the database. - - Args: - session (Session): the database. - user_id (int, optional): the users' id. - - Returns: - Tuple[str, Optional[List[str]], Optional[int], - str, Optional[str], Optional[int]]: the cursor settings. - """ - primary_cursor, secondary_cursor = None, None - cursor_settings = ( - session.query(UserSettings).filter_by(user_id=user_id).first() - ) - if cursor_settings: - primary_cursor = cursor_settings.primary_cursor - secondary_cursor = cursor_settings.secondary_cursor - - return primary_cursor, secondary_cursor - - -def save_cursor_settings( - session: Session, - user: User, - cursor_choices: List[str], -): - """Saves cursor choices in the db. - - Args: - session (Session): the database. - user (User): current user. - cursor_choices (List[str]): primary and secondary cursors. - """ - cursor_settings = ( - session.query(UserSettings).filter_by(user_id=user.user_id).first() - ) - if cursor_settings: - session.query(UserSettings).filter_by( - user_id=cursor_settings.user_id, - ).update(cursor_choices) - session.commit() - else: - cursor_settings = UserSettings(user_id=user.user_id, **cursor_choices) - session.add(cursor_settings) - session.commit() From 44da8b74593b6e9b53e5b937e82f49ca80433430 Mon Sep 17 00:00:00 2001 From: Ori Date: Tue, 23 Feb 2021 02:32:12 +0200 Subject: [PATCH 11/27] fix: fixed issue in pytest --- tests/client_fixture.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/client_fixture.py b/tests/client_fixture.py index 4dc3cfab..9e7928d5 100644 --- a/tests/client_fixture.py +++ b/tests/client_fixture.py @@ -107,6 +107,7 @@ def cursor_test_client() -> Iterator[TestClient]: yield from create_test_client(cursor.get_db) +@pytest.fixture(scope="session") def audio_test_client() -> Iterator[TestClient]: yield from create_test_client(audio.get_db) From 8b0cd2188b632bfdf6221edb49716b97a11d197d Mon Sep 17 00:00:00 2001 From: Ori Date: Wed, 24 Feb 2021 00:56:20 +0200 Subject: [PATCH 12/27] fix: a small fix --- app/routers/cursor.py | 2 +- tests/test_cursor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/routers/cursor.py b/app/routers/cursor.py index 076853b8..d09b2a42 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -7,7 +7,7 @@ from sqlalchemy.orm.session import Session from starlette.responses import RedirectResponse from starlette.status import HTTP_302_FOUND -from app.internal.security.dependancies import current_user +from app.internal.security.dependencies import current_user router = APIRouter( prefix="/cursor", diff --git a/tests/test_cursor.py b/tests/test_cursor.py index a2bc3c1e..6dae885e 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -1,5 +1,5 @@ from app.routers.cursor import get_cursor_settings, router -from app.internal.security.dependancies import current_user +from app.internal.security.dependencies import current_user from tests.test_login import test_login_successfull CURSOR_SETTINGS_URL = router.url_path_for("cursor_settings") From dbae35dd1b6b3cb8eeacf78dacaca77c9744eeee Mon Sep 17 00:00:00 2001 From: Ori Date: Thu, 25 Feb 2021 04:17:08 +0200 Subject: [PATCH 13/27] fix: fixed issues according to comments --- app/dependencies.py | 2 +- app/internal/cursor.py | 3 +-- app/routers/cursor.py | 2 +- app/static/cursor.js | 2 +- app/templates/index.html | 2 +- app/templates/partials/base.html | 22 +++++++++++----------- tests/test_cursor.py | 8 ++++---- 7 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/dependencies.py b/app/dependencies.py index bab5d022..7694f3b1 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -13,7 +13,7 @@ MEDIA_PATH = os.path.join(APP_PATH, config.MEDIA_DIRECTORY) STATIC_PATH = os.path.join(APP_PATH, "static") TEMPLATES_PATH = os.path.join(APP_PATH, "templates") -CURSORS_PATH = os.path.join(APP_PATH, "media/cursors/") +CURSORS_PATH = os.path.join(MEDIA_PATH, "cursors") SOUNDS_PATH = os.path.join(STATIC_PATH, "tracks") templates = Jinja2Templates(directory=TEMPLATES_PATH) templates.env.add_extension("jinja2.ext.i18n") diff --git a/app/internal/cursor.py b/app/internal/cursor.py index a0321ab5..801b004c 100644 --- a/app/internal/cursor.py +++ b/app/internal/cursor.py @@ -47,8 +47,7 @@ def save_cursor_settings( session.query(UserSettings).filter_by( user_id=cursor_settings.user_id, ).update(cursor_choices) - session.commit() else: cursor_settings = UserSettings(user_id=user.user_id, **cursor_choices) session.add(cursor_settings) - session.commit() + session.commit() diff --git a/app/routers/cursor.py b/app/routers/cursor.py index d09b2a42..03394f33 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -74,7 +74,7 @@ async def get_cursor_choices( return RedirectResponse("/", status_code=HTTP_302_FOUND) -@router.get("/load_cursor") +@router.get("/load") async def load_cursor( session: Session = Depends(get_db), user: User = Depends(current_user), diff --git a/app/static/cursor.js b/app/static/cursor.js index 735105d9..5b0d3be9 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -6,7 +6,7 @@ window.addEventListener("load", get_cursor_choices); */ function get_cursor_choices() { const request = new XMLHttpRequest(); - request.open("GET", "/cursor/load_cursor", true); + request.open("GET", "/cursor/load", true); request.onload = change_cursor; request.send(); } diff --git a/app/templates/index.html b/app/templates/index.html index 7e967f1f..5c926972 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,4 +1,4 @@ -{% extends "partials/index/index_base.html" %} +{% extends "base.html" %} {% block content %} diff --git a/app/templates/partials/base.html b/app/templates/partials/base.html index c9d8a7c5..f1613015 100644 --- a/app/templates/partials/base.html +++ b/app/templates/partials/base.html @@ -20,17 +20,6 @@ integrity="sha384-SlE991lGASHoBfWbelyBPLsUlwY1GwNDJo3jSJO04KZ33K2bwfV9YBauFfnzvynJ" crossorigin="anonymous"> - - - - - - - @@ -43,5 +32,16 @@ {% block body %} {% endblock %} + + + + + + + diff --git a/tests/test_cursor.py b/tests/test_cursor.py index 6dae885e..badfafb7 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -17,7 +17,7 @@ def test_cursor_choices(session, cursor_test_client): test_login_successfull(session, cursor_test_client) data = { "primary_cursor": "arrow", - "secondary_cursor": "p1", + "secondary_cursor": "sword", "user": current_user, } first_response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) @@ -28,13 +28,13 @@ def test_cursor_choices(session, cursor_test_client): primary2, secondary2 = get_cursor_settings(session, user_id=1) assert first_response.ok and second_response.ok - assert primary1 == "arrow" and secondary1 == "p1" + assert primary1 == "arrow" and secondary1 == "sword" assert primary2 == "arrow" and secondary2 == "default" def test_load_cursor(session, cursor_test_client): data = { - "primary_cursor": "cloud", + "primary_cursor": "fire", "secondary_cursor": "ice", } response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) @@ -42,4 +42,4 @@ def test_load_cursor(session, cursor_test_client): primary_cursor, secondary_cursor = get_cursor_settings(session, user_id=1) assert response.ok - assert primary_cursor == "cloud" and secondary_cursor == "ice" + assert primary_cursor == "fire" and secondary_cursor == "ice" From 458fb3fd4e2f1a1688cf7c3db4072d401fcc9a67 Mon Sep 17 00:00:00 2001 From: Ori Date: Thu, 25 Feb 2021 04:51:52 +0200 Subject: [PATCH 14/27] fix: fixed a bug in pytest --- tests/meds/test_routers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/meds/test_routers.py b/tests/meds/test_routers.py index caa34785..81773c65 100644 --- a/tests/meds/test_routers.py +++ b/tests/meds/test_routers.py @@ -33,7 +33,7 @@ def test_meds_send_form_success( assert response.ok message = "PyLendar" in response.text assert message is pylendar - message = "alert" in response.text + message = "alert-danger" in response.text assert message is not pylendar event = session.query(Event).first() if pylendar: From df699f6fa6667e6494267a5abf4d2ec9ee6b41a6 Mon Sep 17 00:00:00 2001 From: Ori Date: Thu, 25 Feb 2021 08:20:48 +0200 Subject: [PATCH 15/27] fix: fixed a small bug in one of the tests. now coverage is 100% on this ticket as well :) --- tests/test_cursor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_cursor.py b/tests/test_cursor.py index badfafb7..08dbe2c8 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -1,5 +1,5 @@ -from app.routers.cursor import get_cursor_settings, router from app.internal.security.dependencies import current_user +from app.routers.cursor import get_cursor_settings, router from tests.test_login import test_login_successfull CURSOR_SETTINGS_URL = router.url_path_for("cursor_settings") @@ -7,7 +7,8 @@ LOAD_CURSOR_URL = router.url_path_for("load_cursor") -def test_get_cursor_settings(cursor_test_client): +def test_get_cursor_settings(session, cursor_test_client): + test_login_successfull(session, cursor_test_client) response = cursor_test_client.get(url=CURSOR_SETTINGS_URL) assert response.ok assert b"Cursor Settings" in response.content From c847b4103911cf2731de1da769a9860a78211f04 Mon Sep 17 00:00:00 2001 From: Ori Date: Thu, 25 Feb 2021 22:45:48 +0200 Subject: [PATCH 16/27] fix: fixed issues according to comments --- app/internal/cursor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/internal/cursor.py b/app/internal/cursor.py index 801b004c..1d5415e5 100644 --- a/app/internal/cursor.py +++ b/app/internal/cursor.py @@ -1,7 +1,9 @@ -from app.database.models import User, UserSettings -from sqlalchemy.orm.session import Session from typing import List, Optional, Tuple +from sqlalchemy.orm.session import Session + +from app.database.models import User, UserSettings + def get_cursor_settings( session: Session, @@ -48,6 +50,5 @@ def save_cursor_settings( user_id=cursor_settings.user_id, ).update(cursor_choices) else: - cursor_settings = UserSettings(user_id=user.user_id, **cursor_choices) - session.add(cursor_settings) + session.merge(UserSettings(user_id=user.user_id, **cursor_choices)) session.commit() From b01c4591c46321128dc409882b4b206b0a108816 Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 00:01:02 +0200 Subject: [PATCH 17/27] fix: a tiny fix --- app/static/cursor.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/static/cursor.js b/app/static/cursor.js index 5b0d3be9..b0e1eb98 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -21,8 +21,10 @@ function change_cursor() { const primary_cursor_path = `url(${CURSORS_PATH}${primary_cursor_choice}), auto`; const secondary_cursor_choice = cursor_settings["secondary_cursor"]; const secondary_cursor_path = `url(${CURSORS_PATH}${secondary_cursor_choice}), auto`; - const primary_cursor = primary_cursor_choice !== "default.cur" ? primary_cursor_path : ""; - const secondary_cursor = secondary_cursor_choice !== "default.cur" ? secondary_cursor_path : ""; + const primary_cursor = + primary_cursor_choice !== "default.cur" ? primary_cursor_path : ""; + const secondary_cursor = + secondary_cursor_choice !== "default.cur" ? secondary_cursor_path : ""; document.body.style.cursor = primary_cursor; const links = document.querySelectorAll("a, button, input, select, label"); links.forEach((element) => { From 969bcb17f06afe196a2eccfb700923c19395d0a1 Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 13:44:05 +0200 Subject: [PATCH 18/27] fix: fixed finale comment --- app/static/cursor.js | 60 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/app/static/cursor.js b/app/static/cursor.js index b0e1eb98..2252345f 100644 --- a/app/static/cursor.js +++ b/app/static/cursor.js @@ -1,5 +1,18 @@ const CURSORS_PATH = "/media/cursors/"; -window.addEventListener("load", get_cursor_choices); + +// These must be global so the mutationObserver could access them. +let primary_cursor; +let secondary_cursor; + +window.addEventListener("load", init); + +/** + * @summary In charge of initialising the customization of the cursor. + */ +function init() { + get_cursor_choices(); + initMutationObserver(); +} /** * @summary This function gets cursor choices from the db. @@ -21,9 +34,9 @@ function change_cursor() { const primary_cursor_path = `url(${CURSORS_PATH}${primary_cursor_choice}), auto`; const secondary_cursor_choice = cursor_settings["secondary_cursor"]; const secondary_cursor_path = `url(${CURSORS_PATH}${secondary_cursor_choice}), auto`; - const primary_cursor = + primary_cursor = primary_cursor_choice !== "default.cur" ? primary_cursor_path : ""; - const secondary_cursor = + secondary_cursor = secondary_cursor_choice !== "default.cur" ? secondary_cursor_path : ""; document.body.style.cursor = primary_cursor; const links = document.querySelectorAll("a, button, input, select, label"); @@ -31,3 +44,44 @@ function change_cursor() { element.style.cursor = secondary_cursor; }); } + +/** + * @summary Sets up mutation observer to follow dynamically added links + */ +function initMutationObserver() { + const config = { + childList: true, + subtree: true, + }; + const observer = new MutationObserver(mutate); + observer.observe(document, config); +} + +/** + * @summary This function identifies a new element for the secondary cursor, + * and sets it according to the users' choices. + */ +function mutate(mutationList) { + const links = ["a", "button", "input", "select", "label"]; + for (let mutation of mutationList) { + if (mutation.type == "childList") { + handle_potential_links(mutation.addedNodes, links); + } + } +} + +/** + * @summary Helper function to the mutate function which + * on creation of new nodes in the DOM, if it is a link - changes its' + * style. + */ +function handle_potential_links(nodes, links) { + nodes.forEach((element) => { + if ( + typeof(element.tagName) !== "undefined" && + links.includes(element.tagName.toLowerCase()) + ) { + element.setAttribute("style", secondary_cursor); + } + }); +} From 01456c8eba2c9bc6a5b8cca5b6d4a72b2f306b41 Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 13:55:58 +0200 Subject: [PATCH 19/27] fix: trying to fix the same bug from favorite_quotes in pytest --- app/internal/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/internal/event.py b/app/internal/event.py index c8867499..7ce4fd2c 100644 --- a/app/internal/event.py +++ b/app/internal/event.py @@ -142,7 +142,7 @@ def add_countries_to_db(session: Session) -> None: session.commit() -@functools.lru_cache +@functools.lru_cache() def get_all_countries_names(session: Session) -> List[str]: """ Returns a cached list of the countries names. From 5b54d04866de4407ad35e37c7cb9d3994ee30dc9 Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 14:03:44 +0200 Subject: [PATCH 20/27] fix: fixed issue in pytest --- app/internal/features.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/internal/features.py b/app/internal/features.py index 4ef8628e..ab1711f2 100644 --- a/app/internal/features.py +++ b/app/internal/features.py @@ -100,8 +100,8 @@ async def is_access_allowd(request: Request, route: str) -> bool: # Get current user. # Note: can't use dependency beacause its designed for routes only. # current_user return schema not an db model. - jwt = await get_authorization_cookie(request=request) - user = await current_user(request=request, jwt=jwt, db=session) + jwt = get_authorization_cookie(request=request) + user = current_user(request=request, jwt=jwt, db=session) feature = session.query(Feature).filter_by(route=route).first() From 0fdce31e75c92f59c927979b8e72875a8fbba639 Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 14:10:23 +0200 Subject: [PATCH 21/27] fix: trying to fix the bug --- app/internal/features.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/internal/features.py b/app/internal/features.py index ab1711f2..4ef8628e 100644 --- a/app/internal/features.py +++ b/app/internal/features.py @@ -100,8 +100,8 @@ async def is_access_allowd(request: Request, route: str) -> bool: # Get current user. # Note: can't use dependency beacause its designed for routes only. # current_user return schema not an db model. - jwt = get_authorization_cookie(request=request) - user = current_user(request=request, jwt=jwt, db=session) + jwt = await get_authorization_cookie(request=request) + user = await current_user(request=request, jwt=jwt, db=session) feature = session.query(Feature).filter_by(route=route).first() From 7dcfc020996a496f9292d7d7ed8541b9dcc0d3da Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 14:14:37 +0200 Subject: [PATCH 22/27] fix: trying to fix the bug --- app/routers/cursor.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/routers/cursor.py b/app/routers/cursor.py index 03394f33..f3f0eb46 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -1,13 +1,15 @@ import json from pathlib import Path -from app.internal.cursor import get_cursor_settings, save_cursor_settings -from app.database.models import User -from app.dependencies import CURSORS_PATH, get_db, templates + from fastapi import APIRouter, Depends, Form, Request from sqlalchemy.orm.session import Session from starlette.responses import RedirectResponse from starlette.status import HTTP_302_FOUND -from app.internal.security.dependencies import current_user + +from app.database.models import User +from app.dependencies import CURSORS_PATH, get_db, templates +from app.internal.cursor import get_cursor_settings, save_cursor_settings +from app.routers.profile import get_placeholder_user router = APIRouter( prefix="/cursor", @@ -19,7 +21,7 @@ @router.get("/settings") def cursor_settings( request: Request, - user: User = Depends(current_user), + user: User = Depends(get_placeholder_user), session: Session = Depends(get_db), ) -> templates.TemplateResponse: """A route to the cursor settings. @@ -48,7 +50,7 @@ def cursor_settings( @router.post("/settings") async def get_cursor_choices( session: Session = Depends(get_db), - user: User = Depends(current_user), + user: User = Depends(get_placeholder_user), primary_cursor: str = Form(...), secondary_cursor: str = Form(...), ) -> RedirectResponse: @@ -77,7 +79,7 @@ async def get_cursor_choices( @router.get("/load") async def load_cursor( session: Session = Depends(get_db), - user: User = Depends(current_user), + user: User = Depends(get_placeholder_user), ) -> RedirectResponse: """loads cursors according to cursor settings. From 021881b76f0815aa57ad83b673298085dbbdaf6d Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 14:23:30 +0200 Subject: [PATCH 23/27] fix: trying to fix the bug --- app/routers/cursor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/routers/cursor.py b/app/routers/cursor.py index f3f0eb46..06dfc7a4 100644 --- a/app/routers/cursor.py +++ b/app/routers/cursor.py @@ -9,7 +9,7 @@ from app.database.models import User from app.dependencies import CURSORS_PATH, get_db, templates from app.internal.cursor import get_cursor_settings, save_cursor_settings -from app.routers.profile import get_placeholder_user +from app.internal.security.dependencies import current_user router = APIRouter( prefix="/cursor", @@ -21,7 +21,7 @@ @router.get("/settings") def cursor_settings( request: Request, - user: User = Depends(get_placeholder_user), + user: User = Depends(current_user), session: Session = Depends(get_db), ) -> templates.TemplateResponse: """A route to the cursor settings. @@ -50,7 +50,7 @@ def cursor_settings( @router.post("/settings") async def get_cursor_choices( session: Session = Depends(get_db), - user: User = Depends(get_placeholder_user), + user: User = Depends(current_user), primary_cursor: str = Form(...), secondary_cursor: str = Form(...), ) -> RedirectResponse: @@ -79,7 +79,7 @@ async def get_cursor_choices( @router.get("/load") async def load_cursor( session: Session = Depends(get_db), - user: User = Depends(get_placeholder_user), + user: User = Depends(current_user), ) -> RedirectResponse: """loads cursors according to cursor settings. From 8e057c54cb5f4388b7f68462487c6145b61c7214 Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 14:51:25 +0200 Subject: [PATCH 24/27] fix: trying to fix the bug --- tests/fixtures/client_fixture.py | 6 ----- tests/test_cursor.py | 46 -------------------------------- 2 files changed, 52 deletions(-) diff --git a/tests/fixtures/client_fixture.py b/tests/fixtures/client_fixture.py index 896b06e2..eb96ef68 100644 --- a/tests/fixtures/client_fixture.py +++ b/tests/fixtures/client_fixture.py @@ -10,7 +10,6 @@ agenda, audio, categories, - cursor, dayview, event, friendview, @@ -111,11 +110,6 @@ def profile_test_client() -> Generator[Session, None, None]: Base.metadata.drop_all(bind=test_engine) -@pytest.fixture(scope="session") -def cursor_test_client() -> Iterator[TestClient]: - yield from create_test_client(cursor.get_db) - - @pytest.fixture(scope="session") def audio_test_client() -> Iterator[TestClient]: yield from create_test_client(audio.get_db) diff --git a/tests/test_cursor.py b/tests/test_cursor.py index 08dbe2c8..e69de29b 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -1,46 +0,0 @@ -from app.internal.security.dependencies import current_user -from app.routers.cursor import get_cursor_settings, router -from tests.test_login import test_login_successfull - -CURSOR_SETTINGS_URL = router.url_path_for("cursor_settings") -GET_CHOICES_URL = router.url_path_for("get_cursor_choices") -LOAD_CURSOR_URL = router.url_path_for("load_cursor") - - -def test_get_cursor_settings(session, cursor_test_client): - test_login_successfull(session, cursor_test_client) - response = cursor_test_client.get(url=CURSOR_SETTINGS_URL) - assert response.ok - assert b"Cursor Settings" in response.content - - -def test_cursor_choices(session, cursor_test_client): - test_login_successfull(session, cursor_test_client) - data = { - "primary_cursor": "arrow", - "secondary_cursor": "sword", - "user": current_user, - } - first_response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) - primary1, secondary1 = get_cursor_settings(session, user_id=1) - - data["secondary_cursor"] = "default" - second_response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) - primary2, secondary2 = get_cursor_settings(session, user_id=1) - - assert first_response.ok and second_response.ok - assert primary1 == "arrow" and secondary1 == "sword" - assert primary2 == "arrow" and secondary2 == "default" - - -def test_load_cursor(session, cursor_test_client): - data = { - "primary_cursor": "fire", - "secondary_cursor": "ice", - } - response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) - response = cursor_test_client.get(url=LOAD_CURSOR_URL) - - primary_cursor, secondary_cursor = get_cursor_settings(session, user_id=1) - assert response.ok - assert primary_cursor == "fire" and secondary_cursor == "ice" From 2ec4a77de4934dcd66ef208124994040fe2ddc8f Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 15:32:11 +0200 Subject: [PATCH 25/27] fix: trying to fix the bug --- tests/fixtures/client_fixture.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/fixtures/client_fixture.py b/tests/fixtures/client_fixture.py index eb96ef68..896b06e2 100644 --- a/tests/fixtures/client_fixture.py +++ b/tests/fixtures/client_fixture.py @@ -10,6 +10,7 @@ agenda, audio, categories, + cursor, dayview, event, friendview, @@ -110,6 +111,11 @@ def profile_test_client() -> Generator[Session, None, None]: Base.metadata.drop_all(bind=test_engine) +@pytest.fixture(scope="session") +def cursor_test_client() -> Iterator[TestClient]: + yield from create_test_client(cursor.get_db) + + @pytest.fixture(scope="session") def audio_test_client() -> Iterator[TestClient]: yield from create_test_client(audio.get_db) From d0502460bd63ff094c0735da15e5b7fb8614c4ef Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 15:37:12 +0200 Subject: [PATCH 26/27] fix: trying to fix the bug --- tests/test_cursor.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_cursor.py b/tests/test_cursor.py index e69de29b..08dbe2c8 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -0,0 +1,46 @@ +from app.internal.security.dependencies import current_user +from app.routers.cursor import get_cursor_settings, router +from tests.test_login import test_login_successfull + +CURSOR_SETTINGS_URL = router.url_path_for("cursor_settings") +GET_CHOICES_URL = router.url_path_for("get_cursor_choices") +LOAD_CURSOR_URL = router.url_path_for("load_cursor") + + +def test_get_cursor_settings(session, cursor_test_client): + test_login_successfull(session, cursor_test_client) + response = cursor_test_client.get(url=CURSOR_SETTINGS_URL) + assert response.ok + assert b"Cursor Settings" in response.content + + +def test_cursor_choices(session, cursor_test_client): + test_login_successfull(session, cursor_test_client) + data = { + "primary_cursor": "arrow", + "secondary_cursor": "sword", + "user": current_user, + } + first_response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) + primary1, secondary1 = get_cursor_settings(session, user_id=1) + + data["secondary_cursor"] = "default" + second_response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) + primary2, secondary2 = get_cursor_settings(session, user_id=1) + + assert first_response.ok and second_response.ok + assert primary1 == "arrow" and secondary1 == "sword" + assert primary2 == "arrow" and secondary2 == "default" + + +def test_load_cursor(session, cursor_test_client): + data = { + "primary_cursor": "fire", + "secondary_cursor": "ice", + } + response = cursor_test_client.post(url=GET_CHOICES_URL, data=data) + response = cursor_test_client.get(url=LOAD_CURSOR_URL) + + primary_cursor, secondary_cursor = get_cursor_settings(session, user_id=1) + assert response.ok + assert primary_cursor == "fire" and secondary_cursor == "ice" From a1a9f66535a49b0f1177bb22193ced264e2f02be Mon Sep 17 00:00:00 2001 From: Ori Date: Fri, 26 Feb 2021 18:44:35 +0200 Subject: [PATCH 27/27] fix: fixed the bug! --- tests/test_feature_panel.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_feature_panel.py b/tests/test_feature_panel.py index 1ee57b7c..21d697d4 100644 --- a/tests/test_feature_panel.py +++ b/tests/test_feature_panel.py @@ -1,8 +1,8 @@ import pytest -from app.database.models import Feature, UserFeature import app.internal.features as internal import app.routers.features as route +from app.database.models import Feature, UserFeature from tests.test_login import LOGIN_DATA, REGISTER_DETAIL @@ -128,10 +128,10 @@ def test_delete_feature(session, feature): def test_is_feature_exist_in_db(session, feature): - assert internal.is_feature_exists({ - 'name': 'test', - 'route': '/test' - }, session) + assert internal.is_feature_exists( + {"name": "test", "route": "/test"}, + session, + ) def test_update_feature(session, feature, update_dict): @@ -167,14 +167,14 @@ def test_create_feature(session): assert feat.name == "test1" -def test_index(security_test_client): +def test_index(session, security_test_client): url = route.router.url_path_for("index") resp = security_test_client.get(url) assert resp.ok -def test_add_feature_to_user(form_mock, security_test_client): +def test_add_feature_to_user(session, form_mock, security_test_client): url = route.router.url_path_for("add_feature_to_user") security_test_client.post(