From f56df5e2358ff88747849e2df7b5fb9f092679a5 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 5 Feb 2026 16:44:24 +0000 Subject: [PATCH] fix: adding bookmark throws a 404 --- ai/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 404 bytes ai/__pycache__/local_ai_api.cpython-311.pyc | Bin 0 -> 19874 bytes config/__init__.py | 3 + config/__pycache__/__init__.cpython-311.pyc | Bin 159 -> 242 bytes config/__pycache__/celery.cpython-311.pyc | Bin 0 -> 978 bytes config/__pycache__/settings.cpython-311.pyc | Bin 5552 -> 6432 bytes config/__pycache__/urls.cpython-311.pyc | Bin 1557 -> 859 bytes config/celery.py | 20 ++ config/settings.py | 33 ++ config/urls.py | 31 +- core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 2520 bytes core/__pycache__/api_views.cpython-311.pyc | Bin 0 -> 2825 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 5547 bytes core/__pycache__/serializers.cpython-311.pyc | Bin 0 -> 3990 bytes core/__pycache__/tasks.cpython-311.pyc | Bin 0 -> 4842 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 2087 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 9189 bytes core/admin.py | 29 +- core/api_views.py | 32 ++ core/migrations/0001_initial.py | 94 ++++++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 5510 bytes core/models.py | 67 ++++- core/serializers.py | 40 +++ core/tasks.py | 91 ++++++ core/templates/base.html | 180 +++++++++-- core/templates/core/bookmark_detail.html | 147 +++++++++ core/templates/core/bookmark_form.html | 54 ++++ core/templates/core/index.html | 284 +++++++++--------- core/templates/core/team_detail.html | 56 ++++ core/templates/core/team_list.html | 33 ++ core/templates/registration/login.html | 46 +++ core/urls.py | 27 +- core/views.py | 150 +++++++-- requirements.txt | 8 + 34 files changed, 1216 insertions(+), 209 deletions(-) create mode 100644 ai/__pycache__/__init__.cpython-311.pyc create mode 100644 ai/__pycache__/local_ai_api.cpython-311.pyc create mode 100644 config/__pycache__/celery.cpython-311.pyc create mode 100644 config/celery.py create mode 100644 core/__pycache__/api_views.cpython-311.pyc create mode 100644 core/__pycache__/serializers.cpython-311.pyc create mode 100644 core/__pycache__/tasks.cpython-311.pyc create mode 100644 core/api_views.py create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/serializers.py create mode 100644 core/tasks.py create mode 100644 core/templates/core/bookmark_detail.html create mode 100644 core/templates/core/bookmark_form.html create mode 100644 core/templates/core/team_detail.html create mode 100644 core/templates/core/team_list.html create mode 100644 core/templates/registration/login.html diff --git a/ai/__pycache__/__init__.cpython-311.pyc b/ai/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9beeae7341d797e2eac5af07416a24e9ee781f92 GIT binary patch literal 404 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 delta 99 zcmeywIG@pSIWI340}xbw%g+SSk3j?sFhUuh*?^4c3@Hr344RC7D;bKI7#Jp2yDG8* Yg+XeH`GLd-W=2NF4-7D(h#4pb04&H3>i_@% diff --git a/config/__pycache__/celery.cpython-311.pyc b/config/__pycache__/celery.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8983b9dfabaed0c0ee40d659e6478d832f0ac48b GIT binary patch literal 978 zcmZ8e&1(}u6o0eXY?|0wDKhKt$6V5rW+IA&b)bF^PBg6`(>Qy}^?zGLFI?wX1nGp3ET6srko&WW7&EtZZkZy11X5O3 zkrfe9>vb{Fd%jraR;_52s!Vglgo-Uv-PWva#)Cd=A@|$)3j7*k@~fyWM()=v>Kg0; zzlE+pvHcBIH$n(Gg?KagPT{6Eyt)dI$Zp~UCXZp#OJ=;;g*Gg9V9|rcGi9_{-h0|s zk{u=4jC4nV$S06IhNO4n?)Q`j$$qI1bD3*Hssorzoe}w6>P>3TY0WiLZ8Fm#GhKcgy0|=r_kwA-R>422S2%wQ04c6xogYLc|0CHwr19@bq1}wU#^inFze!>y7MIE0|wh}Wnq+9c2VdyilNivA!+P29E(%P6~T>&BLrn0GrA1sMfJaXV_SBcB4(Gyb*AVsS~m zY@0WHcixu;UvBzx%a=P?+;(fbq}wLF_c+>Lb;I~Wl9Caq99-pvthwZKjL$VEp=cT| zOA5_%5?iQv{f11V~H%uXKBGjL%CgLGz@)MbwuFmY<=UTL#)G zX}8S>*bQK_^fmxIvi2p)1fnOp2@f=7H|!1650X z*dd04-tQvJ$3gGllhiZD8JuvYa$iq#-%oQ6h}L5x`@}(~bAHZQ$bXHke2=X-ptpWz uXg_rLc0gbK?1%$m_3pE~5ibbh!@e^#>VUWx><4{u2Xxnc?KylJEByjfJyY)h delta 191 zcmZ2rv_YG1IWI340}xbw%g=NXnaC%>bcJQ3#$^^(=3oX*m(3Nd)0r9BCkt>+-+Z1^ zf|*SYsI*9P^IM+dOpL20Z{=@b($wF)T<|cXsWC{#1W4Rs&de(*G6iwXL4*Z}umllS zAi@Sn++r;*$jK}za+o|{SbTD~h!l4kP?Qmfi|;XS-XP-3D99ni^nn3JH1K^8*~}+a P!^C}oK?sP71c6EcXsa&G diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 0b85e94ece283a83ff1af1d71f1b265c943eb37a..5d3b77da5cf15fd7c5608efcaf04eaed12f62b3c 100644 GIT binary patch literal 859 zcma)2zi-n(6h7N$Cr;`X0fGt=iMHyHB}O2mD2ia|5)`o@QOOL*wU5-nKccfuAyXul zOsGR8Ix+>J@JCeq1$Jer(XC8uk-Bx_os%FUb>R7ay!+mFpTBqCG_8anJ`X-QeF33g z{ICplkzC$}$`@oKTX2yl7=pla(G@+(kUVVQ{4BY$rx*nRiHJxxCPJ^c6zVON&{#xh z0&^ZYQznI8?tx=p%Zt*=HipyWFJdb{3S$WnnDfYWilDQ$ME*-hy;i3LYS=E1q2~$A zd93>GVnegnUZR@zo0n?>lTF)m{F=yc-;54f!SO9Owh6DicgRsVP;!Dk*f1?Ci2W$k z-Jt9EI+ODqh}bthGPv-??5pRmj%K@g_sL9#lgXg9fTOwr?Hb<7kbtHZs~=JAu0=E<`Z?__vqii^X& zM7?+VJgYpss5H(ijkMCtD$TRI32vvjo#FO^bN|%Hs*f(J>wAt+uji>#ULBPKrAj k?&O@&&C&j7|M+#PZDrb4inlYoJyYckaaiUby#sCh0Wh=P@c;k- literal 1557 zcmb7Ezi-<{6h2a`&9y)mR5TN}FQWWXXv428G3D6*L?bJ<0fDW1Zjz3VjNPzapJ3Kyq?|a{ScjS+a zhJj#=|DxWehS2Zwp%|6icw;CCeUDt!MXutiz7nWiRgredF9ljx3-qp@K5KqCsB|l- zt^3uW)~zW>MMQVYL>;A>B`1#ou`388`7g zU*<*qmTaNZY_LhC)QVZCNgLxap4Ua6pT64h<|4TG!2MmY|X6a7%(M zNY5=}_qiPa7jGN);?PMIr)g8tPfWb+y4Vh}7ethaHpYEM101~Q*-?}`Gnpy=T8@_$ z$O4usgit7UxY2BG=5-BeKsIq`2jo^$+Rc`6*S9&pdIW{JORVoxLB)_TDa1?_7jo_c z*Z)x?@utmtl4PUxFGYKy&UD1oMtq34|o|rLr0PMiA4N-Li?SEIsG{iSDQA%}g4T|C&VOo*;=Po0rs=4pgG= z?B03wSyH{TeX!fx+3O_6!QuAd-rXMTP*TtB&cla?KcP23gx;E#JZ4sW6o(?V$aCVv zf->tFWnc5ic8HaIxao&sMlar0ZwAzjeR7*!gmC%W;eW!iC@G4vP*)Xgf#iabvOYn0 z`JSVXC-1uFXJHUBM^`3i??oBuwd0+W3#Zp#wqJfa)h^Gp%X6&?u8&S0pFBSOYT8(z zHP)xvrI~hVp_f+G?C;%I?i&uYrGP|`uS?f&KIx|?c&hZ0zEp*A0@v#d~_1}Ku$8i7v diff --git a/config/celery.py b/config/celery.py new file mode 100644 index 0000000..19539c5 --- /dev/null +++ b/config/celery.py @@ -0,0 +1,20 @@ +import os +from celery import Celery + +# Set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +app = Celery('config') + +# Using a string here means the worker doesn't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object('django.conf:settings', namespace='CELERY') + +# Load task modules from all registered Django app configs. +app.autodiscover_tasks() + +@app.task(bind=True, ignore_result=True) +def debug_task(self): + print(f'Request: {self.request!r}') diff --git a/config/settings.py b/config/settings.py index 291d043..9c8820b 100644 --- a/config/settings.py +++ b/config/settings.py @@ -55,6 +55,9 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', + 'taggit', + 'django_filters', 'core', ] @@ -180,3 +183,33 @@ if EMAIL_USE_SSL: # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# DRF Settings +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated', + ], + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.BasicAuthentication', + ], + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + ), +} + +# Celery Settings +CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL', 'redis://localhost:6379/0') +CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', 'redis://localhost:6379/0') +CELERY_ACCEPT_CONTENT = ['json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_TIMEZONE = TIME_ZONE + +# Run tasks synchronously in development (no Redis required) +CELERY_TASK_ALWAYS_EAGER = True +CELERY_TASK_EAGER_PROPAGATES = True + +# Login/Logout Redirects +LOGIN_REDIRECT_URL = 'home' +LOGOUT_REDIRECT_URL = 'home' \ No newline at end of file diff --git a/config/urls.py b/config/urls.py index bcfc074..4da058d 100644 --- a/config/urls.py +++ b/config/urls.py @@ -1,29 +1,10 @@ -""" -URL configuration for config project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/5.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" from django.contrib import admin -from django.urls import include, path -from django.conf import settings -from django.conf.urls.static import static +from django.urls import path, include +from django.contrib.auth import views as auth_views urlpatterns = [ - path("admin/", admin.site.urls), - path("", include("core.urls")), + path('admin/', admin.site.urls), + path('accounts/login/', auth_views.LoginView.as_view(), name='login'), + path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'), + path('', include('core.urls')), ] - -if settings.DEBUG: - urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") - urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392d6714413db63120e4233d2e96cbadb5de..9e20c2719805698d0bf8bb231bd97087dc1f9d38 100644 GIT binary patch literal 2520 zcmb7FOK;mo5Z>iOqGdfCzu-sexNw|UMI*I`AUzZXQou-y#J)Jlffs@j*EVBPRF`BM z-vl&3uf8R}z(wFcV*xw}iyi{>l$%@|J>=9G(v%`f1styQX}LT5?aVhjt3OMn0)gl3 zkKgToWkUYOm-NY9cP{^-guEdZv8Y2_s#8iV$&ww(m37%wbj4M5)y?TSH?QYiP1h)q z$P;2IuZg8H`f@5IJcS+3;F~;pK_W0aoxU^q6!tlTZ}P+(IpD~HRWThMjTvV$jM@aoGB8$d%y`S1*(23k zA5hsU6{=~ucIyLXrkWGVb7s1tjJU(x2IKyr-4@!9o_FM${7972-rk|fnJ8?(3b@$} zY_BEqd!Y+9$IU^?ppz#2auHtM5Jq%r5nW=ku2@u8EeTwa#r)`gKersa#kc}XcEKmj zFj}U|3_}zQ!}YAt!G6&&ehEz{nc;Yxg$k3*5h_Ec-jPcfBOf2s4?UOF!$#N&!a94! znqlDa`fnaT^4n&U)teq?^;m>j`MN*xksI6y~@UU`EKV4wqzYHnHLL1zbB zA_baFmSXfkV|o)QKn6w3j+9BFg(9>@T1HBMWj5tw5m`p#c+fS2s>HWoQ~njeZdJ`> zK8~7ipg|_j_izjWQ=d7w@8=$4c=tO`u_ckA>Ey_eIGg0SOe@1`PePLTeXte9xZs+} z$=!XYO!PV9?$@x8Bj5?RM4vy+M!oY4TjD8NeWs_y>$_;w%>xFvN?KIB2DDibzr`_x z+t{fi#Fd0YyVY4ywqBa81FzQfS^>8kT!wLxi?1D@t7wiF5zixFy>a||IK@Xq+8DA;Zxv?BLZEyPH&Od|Kn2#!AJGjY{}Y`#!l0f8gH%8Q!%1Ux0m? zQraWy=cAtP=j|c_9;NabQ1S(YkawqRV|rrnNp9hm-AS{_X`%Tp=LK%D}#m`cmrBwTP~v zFI(tK7TzvI%T@Gc3w_x_mOBIcfiHzFU5)5!U!wGzJ{gA-7A@Ypz=V!Z delta 168 zcmca1e1$P(IWI340}xFAk)LS~q#uJgFu(+5d=>&SrZc24q%h_%pF diff --git a/core/__pycache__/api_views.cpython-311.pyc b/core/__pycache__/api_views.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e92c52b8f29025cfd74fe7cd3f0970b21489a92 GIT binary patch literal 2825 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 literal 209 zcmZ3^%ge<81k-=yXIcX3#~=<2FhLogg@BCd3@HpLj5!Rsj8Tk?3@J>(44TX@K?*b( zZ?Wa(r=;c-`)M-W;!Md(%uCPLOGzqX21>4E_zY6>OHV%|KQ~psG^sSNq*On(A~m_R zB)>?%JijQrxF9h(RX;huC{-U~j9x+IFAf_ZyEG@&u80Guoe_wOWr4&8W=2NF8w@fR Ku%RM0pb7xLi8T)Z diff --git a/core/__pycache__/serializers.cpython-311.pyc b/core/__pycache__/serializers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..593f0323106ea9a2fdb330d8fca8b6b049e5490a GIT binary patch literal 3990 zcmbtX&2JM&6rb_#+8aBL?Ib_~rAhcmaRH|R(H=R+)zmaswaXj%H|1+=x2~ zBjMy+*H-Hqw;D$TebV-xDj&=yG4U*mH%DS1^KRWTaFAr1XD~ z%1WsokV>+2L{0^5F!r?Nq+AN*dLvTNHT$Jh8l?JIu5CX9R%VXmvwxw&JQXRIaoa50 z4;XjhRs9Oy^KqenR;k=}OnzIax0vaOKBPC9GY?`*_NqvS{JABQGm-q_p2y9SXIEB4 zVy^1Im-q8AarlUEbc_s>H zian2kO<)AgP%H&bq>4V=dvmFX&QN;$XhbJd40REr;U{WnRt&T#y8}@zg`3PX^NNVs zme8xPz>4WGp)tp_%d=>xVi9MGMUgBPor+a0<8yDZc&BQX0}jUn2@RO6EaD4j0rw@@ z#@+8;oLH(jY@#|}UGb_D>>evsy$YZBw!&|_t7eH!lq#G}z_Ou_3aj^pihJT&_&x!{ zeHX~1#;=|oT-WNkbH42Bar|*SHGc=l>`Rpo;>#fP625y zuo;VK=Ql|w*}}PWugV&4TT~e#Th*|9*~Y3H+?`9BfFa(#a`{xg~C|%BW73 zI5RzF6-^J^-flxQ6F-7v2uYhY(eQi>NH2`;?way**gM?Ck9W~Gf^$exsISvI)99Uc zg5H@%@3b=l@1VNw-kAYTYZfxaqQAzT1CqXi5i~=U*{MKws%_b+Awn8)D-PL}5d96@ zXoU0(z>dz@ovO{571=@~X-`D9(3-T6yfH}LcmM&rM>^F9j@1LlDpg(BEOdNd;*xW`9ymJz?IVy0U`_0VihYZ z<@?1T;k(8}9zGg?;hqE1%&>ZP2vfM^%f243ADi}rt`GULHxs%P8VNKI+=K#S`*$Eu z$QoIOzd^Go24&Ee8-9BjTcgifHH0Ey)_iKg^I{4O_ZRjI4eZ<#C>X{?!8R_vZ zafc8fPaxr)<8IqSZ5-#2;Emya1EeL?Qe%E=?bD1#m(%$zrpMk~RgW-+>X>o4u zWG#25mYaAz^}4W;`=rCaofn66?lsUNQ#8opw))|7Cu;)D%C!vo1{DSGh={ITavr8k4z9ExZ-SnQ1o%Y;o<^^G8gMouE4xFYAu^9 zifIDpuHg&@7mp*sYk=!WWD4Q`Y@#1-UHEsP02m*Zxm@BJ z)=MID6AR~N%ml?Lrb1vbX+UYBsM&V)sh}Re!f%2w-g)jK&`pI>S|{UvwAaa5KiccW z@DI}K)%FeFS6-{SAzlY-#`i literal 0 HcmV?d00001 diff --git a/core/__pycache__/tasks.cpython-311.pyc b/core/__pycache__/tasks.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7074f2030d6f55a46b92e7947e5b18e9b141e7b4 GIT binary patch literal 4842 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 Z8au$8VQPp<+8uqbZ+la{gUrI7{|ll{k(U4f literal 0 HcmV?d00001 diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659f6c6e0ae848e54157af197c543a09315f..82edf1b42877da3681ac8f04430bb4d75422233c 100644 GIT binary patch literal 2087 zcmcIk%}*Og6rVMNzt-!;IN-#Ax{8dE+n8;_N1NgzM3E2`$^qOWEmG0K4q({zTC?k> z-9wAoLsO{-ZspKJ4;5{d9CGBCKVepy1KLxio^nIVEvL+^y$}og*zxZ3y!U>;_c1d& ze@Rjju<^n7Khpvp^{04NO3kEO+RWKuJMBsYF7%-suhe%Y z`XGwx(dV(V0fuL9dkCN}K8|8~;wGNu6!avMiNkCdrMm`Qe4gsg5^y9YlYKeqo*bER z#J-%No}3Khr22A(dvdajBlYFn>d6^loT0v)(Vm}`)Tb|Rm#w>|a~>WDtJ0m|dAxS<^JVN-Js{8+uQYqWG!7JO+L?P)E; zS+`ma!VDoVS=N!M;iIqW=*b3he6fZ!v!j&b{!r*&tJ@AY`@^B-bBr_xb=#Jaqt%UW z+m}tf2hKnpZi*+oik{}$J5h3Bwv7V~qnfq9Z?sb;b~=UIZc1y`E1}AojPGbRYG@|% z2OSp8cH+(5jmY z4c8!~*B#|LbcO5gotrov@kQv4$t`Z_Cf4Tdw^Q&&w(B}8W+cQ_0(Zp;mdA|-T`O*a z|5psQajx7pUH2gdzD^I2t&j4!JnD=3q1M>9rdrsrF~3_dy9|LpijnPX?_suLPb_>i zg@e;$<3a9B?^+m5u?XAWu;XFlD5u4^CmDF!);PB&xZoEp7P&}qA#uU4SS&N)N87Re zI7a)daD@GsW(Sdd$gA>2CfTWGo`(Goc+EKA&3%P$s?yqNHJp}$zD2t&$HEG))oyCL zNa2MjoIch3?oGUET6)Vs3-}%*^KZ$1k9|Xz1A-u2fCng`b? zTnhjsF-{7PNZ|o3Jn{>!Gyv`9*y(9)t;TB4;TvZ@le<-siqw>ppqWO|)Ue?h0$z3CdAu8}QFpzT4MLi^vK z*4rdIc~Yg>NiSQb+42jKz^VtU6jnPS@?`Ac>0>(f&>NefV>9QU5xDBXRSH+z0Y*=9 gC%Mzn-|u>v37VPkV2Q%gWiBO0jz#{%@3V9A7B^-o$p8QV literal 347 zcmZ3^%ge<81XbViGqZs7V-N=hSfPy1HbBO7h7^Vr#vF!R#wbQch7_h0<{YM6<|t-H z1}277#uS!R<`mX7Y|EG#7*+!@1f(-Yv81pEGiY+W1PN#|-eM_8EXjDu2<2qt=ce9b zC<567lFLiXP4&}cy~Ul9m6(^FuUA@>Q(Ocx>=tWTW@>pcSm7;hpfJ#slGLKS;*|`a zfzk}Wbo4<+>6a#z=9QG{r&gpUmzLxg>6hmhWfvDDCa3Br=NAE`f#&HIRQ}?y$<0qG z%}KQ@;sa_1Ii}bPNPJ*sWMsU-Aael~Jzx;OfQmk_F|cwrxO510gv?;Oz#@N*W8V3M3{aHHz diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd69370b38a98d8b01bf8eb9817c42f16ed6..61d1f79bd4db2b49e5f767ad366e30525e4a640f 100644 GIT binary patch literal 9189 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 literal 1364 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 - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}Knowledge Base{% endblock %} + + + + + + + + + + + {% block extra_css %}{% endblock %} - - {% block content %}{% endblock %} - + +
+ {% block content %}{% endblock %} +
+ + + {% block extra_js %}{% endblock %} + diff --git a/core/templates/core/bookmark_detail.html b/core/templates/core/bookmark_detail.html new file mode 100644 index 0000000..bd2c8cb --- /dev/null +++ b/core/templates/core/bookmark_detail.html @@ -0,0 +1,147 @@ +{% extends "base.html" %} + +{% block title %}{{ bookmark.title|default:bookmark.url }} - Knowledge Base{% endblock %} + +{% block content %} + + +
+
+
+
+

{{ bookmark.title|default:bookmark.url }}

+ {% if bookmark.user == request.user %} + + {% endif %} +
+ +

+ {{ bookmark.url }} +

+ + {% if bookmark.notes %} +
+
My Notes
+
+ {{ bookmark.notes|linebreaks }} +
+
+ {% endif %} + + {% if bookmark.summary %} +
+
AI Summary
+
+ {{ bookmark.summary.content }} +
+
+ {% else %} +
+
+ AI Summary is being generated... +
+ {% endif %} + +
+ {% for tag in bookmark.tags.all %} + #{{ tag.name }} + {% endfor %} +
+
+ + {% if bookmark.extraction %} +
+
Extracted Text Content
+
+ {{ bookmark.extraction.content_text|linebreaks }} +
+
+ {% endif %} +
+ +
+ {% if bookmark.user == request.user %} +
+
Share with Teams
+
+ {% for team in user.teams.all %} +
+
+
{{ team.name }}
+
{{ team.members.count }} members
+
+ +
+ {% empty %} +

You are not a member of any teams. Explore teams.

+ {% endfor %} +
+
+ {% endif %} + +
+
Information
+
    +
  • Saved on: {{ bookmark.created_at|date:"F d, Y H:i" }}
  • +
  • Last updated: {{ bookmark.updated_at|date:"F d, Y H:i" }}
  • +
  • Saved by: {{ bookmark.user.username }}
  • +
+
+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/bookmark_form.html b/core/templates/core/bookmark_form.html new file mode 100644 index 0000000..a1b1ad3 --- /dev/null +++ b/core/templates/core/bookmark_form.html @@ -0,0 +1,54 @@ +{% extends "base.html" %} + +{% block title %}{% if object %}Edit{% else %}Add{% endif %} Bookmark - Knowledge Base{% endblock %} + +{% block content %} +
+
+
+

{% if object %}Edit{% else %}Add New{% endif %} Bookmark

+ +
+ {% csrf_token %} + +
+ + + {% if form.url.errors %}
{{ form.url.errors }}
{% endif %} +
+ +
+ + +
We'll try to fetch the title automatically if you leave this blank.
+
+ +
+ + +
+ +
+ + +
Comma-separated tags.
+
+ +
+
+ + +
+
+ +
+ Cancel + +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..cacb58a 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,151 @@ {% extends "base.html" %} -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% block title %}My Bookmarks - Knowledge Base{% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+ +
+
+
Filter by Tag
+
+ All + {% for tag in all_tags %} + + #{{ tag.name }} + + {% endfor %} +
+
+ +
+
My Teams
+
+ {% for team in teams %} + + {{ team.name }} + + {% empty %} +

No teams yet. Explore

+ {% endfor %} +
+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
+ + +
+
+

{% if request.GET.tag %}Tag: #{{ request.GET.tag }}{% else %}My Bookmarks{% endif %}

+ + New Bookmark + +
+ +
+
+ + + {% if request.GET.tag %} + + {% endif %} + +
+
+ +
+ {% for bookmark in bookmarks %} +
+
+
+
+
+
+
+ {{ bookmark.title|default:bookmark.url }} +
+ {% if bookmark.is_favorite %} + + {% endif %} +
+

{{ bookmark.url }}

+ + {% if bookmark.notes %} +

{{ bookmark.notes|truncatewords:40 }}

+ {% endif %} + +
+ {% for tag in bookmark.tags.all %} + #{{ tag.name }} + {% endfor %} +
+
+
+ + {{ bookmark.created_at|date:"M d" }} +
+
+
+
+
+ {% empty %} +
+
+
+

No bookmarks found

+

Start building your knowledge base by adding your first bookmark.

+ +
+
+ {% endfor %} +
+ + {% if is_paginated %} + + {% endif %} +
+ + + {% endblock %} \ No newline at end of file diff --git a/core/templates/core/team_detail.html b/core/templates/core/team_detail.html new file mode 100644 index 0000000..bbf1bd3 --- /dev/null +++ b/core/templates/core/team_detail.html @@ -0,0 +1,56 @@ +{% extends "base.html" %} + +{% block title %}{{ team.name }} - Team Space{% endblock %} + +{% block content %} + + +
+
+
+

{{ team.name }}

+ {{ team.members.count }} Members +
+

{{ team.description }}

+
+
+ +

Shared Bookmarks

+ +
+ {% for bookmark in shared_bookmarks %} +
+
+
+
+
+ {{ bookmark.title|default:bookmark.url }} +
+

{{ bookmark.url }}

+
+ {% for tag in bookmark.tags.all %} + {{ tag.name }} + {% endfor %} +
+
+
+ Shared by {{ bookmark.shares.first.shared_by.username }} + {{ bookmark.shares.first.shared_at|date:"M d, Y" }} +
+
+
+
+ {% empty %} +
+
+ No bookmarks have been shared with this team yet. +
+
+ {% endfor %} +
+{% endblock %} diff --git a/core/templates/core/team_list.html b/core/templates/core/team_list.html new file mode 100644 index 0000000..3f0b428 --- /dev/null +++ b/core/templates/core/team_list.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} + +{% block title %}My Teams - Knowledge Base{% endblock %} + +{% block content %} +
+
+

My Teams

+

Collaborate with your colleagues by sharing bookmarks in shared team spaces.

+
+
+ +
+ {% for team in teams %} +
+
+
{{ team.name }}
+

{{ team.description|truncatewords:20 }}

+ +
+
+ {% empty %} +
+
+

No teams yet

+

You aren't a member of any teams yet. Teams allow you to share knowledge with others.

+
+
+ {% endfor %} +
+{% endblock %} diff --git a/core/templates/registration/login.html b/core/templates/registration/login.html new file mode 100644 index 0000000..a35e145 --- /dev/null +++ b/core/templates/registration/login.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} + +{% block title %}Login - Knowledge Base{% endblock %} + +{% block content %} +
+
+
+

Login

+
+ {% csrf_token %} + {% for field in form %} +
+ + {{ field }} + {% if field.errors %} +
{{ field.errors }}
+ {% endif %} +
+ {% endfor %} + +
+
+
+
+{% endblock %} + +{% block extra_css %} + +{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..1846784 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,26 @@ -from django.urls import path +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from core.api_views import BookmarkViewSet, TeamViewSet +from core.views import ( + BookmarkListView, BookmarkCreateView, BookmarkDetailView, + BookmarkUpdateView, BookmarkDeleteView, + TeamListView, TeamDetailView, BookmarkShareToggleView +) -from .views import home +router = DefaultRouter() +router.register(r'bookmarks', BookmarkViewSet, basename='api-bookmark') +router.register(r'teams', TeamViewSet, basename='api-team') urlpatterns = [ - path("", home, name="home"), -] + path("", BookmarkListView.as_view(), name="home"), + path("bookmark/add/", BookmarkCreateView.as_view(), name="bookmark-add"), + path("bookmark//", BookmarkDetailView.as_view(), name="bookmark-detail"), + path("bookmark//edit/", BookmarkUpdateView.as_view(), name="bookmark-edit"), + path("bookmark//delete/", BookmarkDeleteView.as_view(), name="bookmark-delete"), + path("bookmark//share//", BookmarkShareToggleView.as_view(), name="bookmark-share-toggle"), + + path("teams/", TeamListView.as_view(), name="team-list"), + path("teams//", TeamDetailView.as_view(), name="team-detail"), + + path("api/", include(router.urls)), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..b337d92 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,133 @@ -import os -import platform +from django.shortcuts import render, redirect, get_object_or_404 +from django.views import View +from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.urls import reverse_lazy +from django.db.models import Q +from django.http import JsonResponse +from .models import Bookmark, Team, Extraction, BookmarkShare +from .tasks import process_bookmark -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone +class BookmarkListView(LoginRequiredMixin, ListView): + model = Bookmark + template_name = 'core/index.html' + context_object_name = 'bookmarks' + paginate_by = 20 + def get_queryset(self): + queryset = Bookmark.objects.filter(user=self.request.user).order_by('-created_at') + + # Search filter + query = self.request.GET.get('q') + if query: + queryset = queryset.filter( + Q(title__icontains=query) | + Q(url__icontains=query) | + Q(notes__icontains=query) | + Q(extraction__content_text__icontains=query) + ).distinct() + + # Tag filter + tag = self.request.GET.get('tag') + if tag: + queryset = queryset.filter(tags__name__in=[tag]) + + return queryset -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + # Add all tags used by the user for a sidebar or filter list + from taggit.models import Tag + context['all_tags'] = Tag.objects.filter(bookmark__user=self.request.user).distinct() + context['teams'] = self.request.user.teams.all() + return context - context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), - } - return render(request, "core/index.html", context) +class BookmarkCreateView(LoginRequiredMixin, CreateView): + model = Bookmark + fields = ['url', 'title', 'notes', 'is_favorite'] + template_name = 'core/bookmark_form.html' + success_url = reverse_lazy('home') + + def form_valid(self, form): + form.instance.user = self.request.user + response = super().form_valid(form) + # Handle tags if provided in a separate field or as a comma-separated string + # For simplicity, we'll assume the model's TaggableManager handles it if added to fields, + # but here we might need to handle it manually if we use a custom field. + # Let's add 'tags' to fields in the actual form. + tags = self.request.POST.get('tags_input') + if tags: + form.instance.tags.add(*[t.strip() for t in tags.split(',')]) + + process_bookmark.delay(self.object.id) + return response + +class BookmarkUpdateView(LoginRequiredMixin, UpdateView): + model = Bookmark + fields = ['url', 'title', 'notes', 'is_favorite'] + template_name = 'core/bookmark_form.html' + success_url = reverse_lazy('home') + + def get_queryset(self): + return Bookmark.objects.filter(user=self.request.user) + +class BookmarkDeleteView(LoginRequiredMixin, DeleteView): + model = Bookmark + success_url = reverse_lazy('home') + + def get_queryset(self): + return Bookmark.objects.filter(user=self.request.user) + +class BookmarkDetailView(LoginRequiredMixin, DetailView): + model = Bookmark + template_name = 'core/bookmark_detail.html' + context_object_name = 'bookmark' + + def get_queryset(self): + # Allow viewing if it's the user's bookmark OR shared with one of their teams + user_teams = self.request.user.teams.all() + return Bookmark.objects.filter( + Q(user=self.request.user) | + Q(shares__team__in=user_teams) + ).distinct() + +class TeamListView(LoginRequiredMixin, ListView): + model = Team + template_name = 'core/team_list.html' + context_object_name = 'teams' + + def get_queryset(self): + return self.request.user.teams.all() + +class TeamDetailView(LoginRequiredMixin, DetailView): + model = Team + template_name = 'core/team_detail.html' + context_object_name = 'team' + + def get_queryset(self): + return self.request.user.teams.all() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + # Get bookmarks shared with this team + context['shared_bookmarks'] = Bookmark.objects.filter(shares__team=self.object).order_by('-shares__shared_at') + return context + +class BookmarkShareToggleView(LoginRequiredMixin, View): + def post(self, request, pk, team_id): + bookmark = get_object_or_404(Bookmark, pk=pk, user=request.user) + team = get_object_or_404(Team, pk=team_id, members=request.user) + + share, created = BookmarkShare.objects.get_or_create( + bookmark=bookmark, + team=team, + defaults={'shared_by': request.user} + ) + + if not created: + share.delete() + shared = False + else: + shared = True + + return JsonResponse({'shared': shared}) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e22994c..68fb336 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,11 @@ Django==5.2.7 mysqlclient==2.2.7 python-dotenv==1.1.1 +djangorestframework==3.15.2 +beautifulsoup4==4.12.3 +html2text==2024.2.26 +httpx==0.27.2 +django-taggit==6.1.0 +celery==5.4.0 +redis==5.0.8 +django-filter==24.3 \ No newline at end of file