From 415a23fcaf416e13d87a3ba8d8f8792368bb5ed4 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 23 Feb 2026 18:57:12 +0000 Subject: [PATCH] ONE --- ai/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 404 bytes ai/__pycache__/local_ai_api.cpython-311.pyc | Bin 0 -> 19874 bytes config/__pycache__/__init__.cpython-311.pyc | Bin 159 -> 159 bytes config/__pycache__/settings.cpython-311.pyc | Bin 5552 -> 3315 bytes config/__pycache__/urls.cpython-311.pyc | Bin 1557 -> 1660 bytes config/__pycache__/wsgi.cpython-311.pyc | Bin 679 -> 679 bytes config/settings.py | 194 ++++-------- config/urls.py | 3 +- core/__pycache__/__init__.cpython-311.pyc | Bin 157 -> 157 bytes core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 1886 bytes core/__pycache__/apps.cpython-311.pyc | Bin 524 -> 524 bytes .../context_processors.cpython-311.pyc | Bin 763 -> 763 bytes core/__pycache__/forms.cpython-311.pyc | Bin 0 -> 2164 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 4050 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 754 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 5983 bytes core/admin.py | 23 +- core/context_processors.py | 4 +- core/forms.py | 23 ++ core/migrations/0001_initial.py | 63 ++++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 3376 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 168 -> 168 bytes core/models.py | 47 ++- core/templates/base.html | 136 +++++++-- core/templates/core/farmer_detail.html | 120 ++++++++ core/templates/core/farmer_form.html | 85 ++++++ core/templates/core/farmer_list.html | 92 ++++++ core/templates/core/index.html | 287 +++++++++--------- core/templates/registration/login.html | 31 ++ core/urls.py | 11 +- core/views.py | 116 +++++-- 31 files changed, 911 insertions(+), 324 deletions(-) create mode 100644 ai/__pycache__/__init__.cpython-311.pyc create mode 100644 ai/__pycache__/local_ai_api.cpython-311.pyc create mode 100644 core/__pycache__/forms.cpython-311.pyc create mode 100644 core/forms.py create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/templates/core/farmer_detail.html create mode 100644 core/templates/core/farmer_form.html create mode 100644 core/templates/core/farmer_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..4cb76c3065eb6fa0f174b8525437ee5edf99cec0 GIT binary patch literal 404 zcma)(ze__g5XbX=w3X80>=xYK(l@z@P*AZ{T%3GMh{?50&3lPS9&PBC%EN!~yicvWn4Yg=?nE~p67_eGZ7MhNM+ZwEE_BaF;nOjwn zFzc`9B5OAtLbzgtG(8!H5h@7P!X$o!NdsB|ZI2hU@kHA}K2s&+^>tmjI)^2&+9{JS zl)15*GRUdO2@#cWgbeg*yK`77UQ0MKJueTZ+XIiUB#NRpG+v|eJGxr`&rNv7Hx!e5 AX#fBK literal 0 HcmV?d00001 diff --git a/ai/__pycache__/local_ai_api.cpython-311.pyc b/ai/__pycache__/local_ai_api.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d147f6566e64acb19161c0eeea7e13d262e32d02 GIT binary patch literal 19874 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 delta 19 ZcmbQwIG>SwIWI340}x2uoi>qs3IHpi1kwNi diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 881731c8d6a538112a17daaf3e733deb4d833646..230f107bf6e5e92e413aa63fd2351882b40a02d1 100644 GIT binary patch delta 1674 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 Rk?IoIW$+^K)E_PW{|CAz@I(Lr diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 42d995d2a7469704c70f4eaa3946a8b2e1cd9ee3..f3a665e0e21c3fd0b3bd3605241119d51f5f0f7d 100644 GIT binary patch delta 258 zcmbQr^M^-$IWI340}wo)KPNMtg@NHQhyw#0P{wDYiR%44Obn^4DV#u(C53C^ELmOV zD4taIEMA!6B9l~*63!HEpeP?qG=-;%n}J~s@5Cp@^1REK7#LOqF$7crwMOxz@C7q~ zuqOXzGsgW)s#Tnc$;tVpc_qdART3##iFxVyddc~DB}JJ@dWoea8G5BfImMg5GOIB% zaWhVqWvwxjo{&Bv{eqmuMKQ}OVwM+Ktgf(F-C*HpaOn{02=54=5PgwX@d~fvMHZzi UEJ`2PS@@Y6xIger-pZN)0M{8mnE(I) delta 132 zcmeyvGnGeuIWI340}x2uotCM?!octt#DM{JDC0BNMD>2Abf$?9#6>w%*|Ydy3R1YL zxEUDMa8Gt%6q&5RD9;FDiE{D;Go%2qChz7r#{Ep2omkWunLaX1&SI^Z#3sVQ%FNHy M!2N-1vJ6`S0591YX#fBK diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index e65b92e38cdb8cc8f32da56a8b43631f6c1e8c02..25bcb757ccccdcb80f042a39aa902fb865275ac5 100644 GIT binary patch delta 20 acmZ3^x}24JIWI340}#xbHfJOE93}uX#|2dY delta 20 acmZ3^x}24JIWI340}x2uowkvC4if+}#RU`q diff --git a/config/settings.py b/config/settings.py index 291d043..2a6d7a7 100644 --- a/config/settings.py +++ b/config/settings.py @@ -1,182 +1,98 @@ -""" -Django settings for config project. - -Generated by 'django-admin startproject' using Django 5.2.7. - -For more information on this file, see -https://docs.djangoproject.com/en/5.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.2/ref/settings/ -""" - -from pathlib import Path import os +from pathlib import Path from dotenv import load_dotenv +load_dotenv() + BASE_DIR = Path(__file__).resolve().parent.parent -load_dotenv(BASE_DIR.parent / ".env") -SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "change-me") -DEBUG = os.getenv("DJANGO_DEBUG", "true").lower() == "true" +SECRET_KEY = os.environ.get("SECRET_KEY", "django-insecure-m!i*i@x%^$x&i*i@x%^$x&i*i@x%^$x&i*i@x%^$x&") -ALLOWED_HOSTS = [ - "127.0.0.1", - "localhost", - os.getenv("HOST_FQDN", ""), -] +DEBUG = os.environ.get("DEBUG", "True") == "True" -CSRF_TRUSTED_ORIGINS = [ - origin for origin in [ - os.getenv("HOST_FQDN", ""), - os.getenv("CSRF_TRUSTED_ORIGIN", "") - ] if origin -] -CSRF_TRUSTED_ORIGINS = [ - f"https://{host}" if not host.startswith(("http://", "https://")) else host - for host in CSRF_TRUSTED_ORIGINS -] +ALLOWED_HOSTS = ["*"] -# Cookies must always be HTTPS-only; SameSite=Lax keeps CSRF working behind the proxy. -SESSION_COOKIE_SECURE = True -CSRF_COOKIE_SECURE = True -SESSION_COOKIE_SAMESITE = "None" -CSRF_COOKIE_SAMESITE = "None" - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ - -# Application definition +# CSRF settings for Flatlogic Cloud +CSRF_TRUSTED_ORIGINS = ["https://*.flatlogic.app", "https://*.flatlogic.com"] INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'core', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "core", + "ai", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - # Disable X-Frame-Options middleware to allow Flatlogic preview iframes. - # 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -X_FRAME_OPTIONS = 'ALLOWALL' - -ROOT_URLCONF = 'config.urls' +ROOT_URLCONF = "config.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - # IMPORTANT: do not remove – injects PROJECT_DESCRIPTION/PROJECT_IMAGE_URL and cache-busting timestamp - 'core.context_processors.project_context', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "core.context_processors.deployment_info", ], }, }, ] -WSGI_APPLICATION = 'config.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/5.2/ref/settings/#databases +WSGI_APPLICATION = "config.wsgi.application" DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': os.getenv('DB_NAME', ''), - 'USER': os.getenv('DB_USER', ''), - 'PASSWORD': os.getenv('DB_PASS', ''), - 'HOST': os.getenv('DB_HOST', '127.0.0.1'), - 'PORT': os.getenv('DB_PORT', '3306'), - 'OPTIONS': { - 'charset': 'utf8mb4', - }, - }, + "default": { + "ENGINE": "django.db.backends.mysql", + "NAME": os.environ.get("DB_NAME", "naims_db"), + "USER": os.environ.get("DB_USER", "naims_user"), + "PASSWORD": os.environ.get("DB_PASS", "naims_pass"), + "HOST": os.environ.get("DB_HOST", "127.0.0.1"), + "PORT": os.environ.get("DB_PORT", "3306"), + } } - -# Password validation -# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators - AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] - -# Internationalization -# https://docs.djangoproject.com/en/5.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - +LANGUAGE_CODE = "en-us" +TIME_ZONE = "UTC" USE_I18N = True - USE_TZ = True +STATIC_URL = "static/" +STATIC_ROOT = BASE_DIR / "staticfiles" +STATICFILES_DIRS = [BASE_DIR / "static"] -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.2/howto/static-files/ +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" -STATIC_URL = 'static/' -# Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS. -STATIC_ROOT = BASE_DIR / 'staticfiles' - -STATICFILES_DIRS = [ - BASE_DIR / 'static', - BASE_DIR / 'assets', - BASE_DIR / 'node_modules', -] - -# Email -EMAIL_BACKEND = os.getenv( - "EMAIL_BACKEND", - "django.core.mail.backends.smtp.EmailBackend" -) -EMAIL_HOST = os.getenv("EMAIL_HOST", "127.0.0.1") -EMAIL_PORT = int(os.getenv("EMAIL_PORT", "587")) -EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "") -EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "") -EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "true").lower() == "true" -EMAIL_USE_SSL = os.getenv("EMAIL_USE_SSL", "false").lower() == "true" -DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "no-reply@example.com") -CONTACT_EMAIL_TO = [ - item.strip() - for item in os.getenv("CONTACT_EMAIL_TO", DEFAULT_FROM_EMAIL).split(",") - if item.strip() -] - -# When both TLS and SSL flags are enabled, prefer SSL explicitly -if EMAIL_USE_SSL: - EMAIL_USE_TLS = False -# Default primary key field type -# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +LOGIN_REDIRECT_URL = "home" +LOGOUT_REDIRECT_URL = "home" diff --git a/config/urls.py b/config/urls.py index bcfc074..fecf250 100644 --- a/config/urls.py +++ b/config/urls.py @@ -21,9 +21,10 @@ from django.conf.urls.static import static urlpatterns = [ path("admin/", admin.site.urls), + path("accounts/", include("django.contrib.auth.urls")), 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) + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \ No newline at end of file diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index c26577c63d7804065bab66cd92b4b54d1a493e62..a46187744300f512d8e9b5959cde747e8e04a5f7 100644 GIT binary patch delta 19 ZcmbQsIG2%oIWI340}#xbHfJLDBmgZK1q}cI delta 19 ZcmbQsIG2%oIWI340}x2uoi>qs5&$b#1k3;c diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 2964e119884bc4661330a4d6c594e246c1597120..2b503a80f18a0b505eddb8f87ac46177914bfc7b 100644 GIT binary patch literal 1886 zcmbVMOK%iM5bmCr*ZTlto7jn13t?OFSb{x7xkOPUKp~L;AINQlG#XF0?SXlini&%3 zw2}A$zL7|rjV%8eTiS!wC#0OX*)k_iRL$&cmfclirDx~ce$;$jUsZL#ZMEtQ+aG_u z3BJ)8`dIMbNq&!#E@sUP5% zNBEXaV?Qxrykf#ct6DXCfcqZdTebuIJE{iEnI2&@CNNsaICW{p`Us;rfw6{+)0bv! zj4)ag80*N`xHMx^G=E`s>pjK6=F0R$6vXc-j<%t!CvrPTV%5B##Ay~}xs3fiWj*j9 zlAz}9ZbRVbVU~jz{+xs&h`0SgJh4+{<&xl0?{ znlfT9lFINCArCn;@XHO`R5jP7dDm5S*Nqa9htzMn?u*2B!lsa7gI z@ORv;K!zecWEbSw$J?EqB$AzcGmo>pBY%^Ao+Z%vHG${puII~+pMdNXLAbNK2UM{I z7}!Y{5xRrt-Q05b(dm4<`uS_S0;45676avQ-28O{@*M?)<4@MzhSx{ub=0FZW z3t{8Xf-5w*N-#rk4M8=E5NriuCIMz?dXAv*?K<@qDwxJj+C~@_w2Fde+uesFs_3Kt zsj5-dN~y;v^-KrCjq_*|OQ`7&7gpT|>19&5UWSD*KfLC;We{KqeX+HI2*~{n_!4nc z<_(%7p!kf*d_Xe4BboMa^2mO&%zv%lX20}Hjk>b7%HuJ!3qj<;p6mHp@G{7#B!l0j zyCeO~1DRqzS8zwH?n$WxDwt7Ow`h)layeFx_ej>)-G}s)DJRnnyQQv(XI{LW+`))u z5Nv{u^UA_wB*PR;8~|OwK({H-WvOZu?G%eh)gO~pp{+a(MFkb{Exh@>zCPBzFAwJr zu#P-*0i;h6&vefDDZ6?6`R|ndc>G_#cl>Ji`m3dV>x3`#_`;c{@uf314*RRU*<0td z@PXi5XWHVxzQ(kff~9%F+dbYs({y4Phd<5U;@UYad>|l}rGb6DVj1|N^L1hwg}yJi sKrHM3_(G-?%kj+e318{)6_T_@l15?w+uqzVNm?UGYecg;ZeOJO6T!%wpa1{> delta 148 zcmcb|cZD%!IWI340}x2uot9}2q#uJgFu(+5d=>&SrZc24q%h_%s&fI0#ks&dbZi00gt9&DqG!$OHf{YXpJ- delta 20 acmeBS>0#ks&dbZi00a_ur)}hBWC8#!X#_z4 diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf2234fb21a6b62efc5cec11af9512dd0c9616..17c15907758311ec1631edb5f9e817126d815b15 100644 GIT binary patch delta 42 wcmey(`kR$^IWI340}x0pn3FkSBd;72e@bdWPJU%>YFy+kynn1zn~~TD>b7%Q6rT0&`afz)+>n6X8bU-&sU3x%11gme%^##LQb0m3bhFtRCmYsYcXpRF zJ|GSpazYV`njCVdP!$xFLry41jvUvL57wTLIOXOjIr+qU>$q;>6oHtX{dRudym{}< z`(}UX>x&X7e>~dJA4!D#g`G~3PaDS^8e4=CDj1|B6a<0L02PfuNi2vZsUVf)f-H~# znI=^Ffl!$VE5VMEU6GKd@U}}KizZ{RjXNxdW5GW}Ge0v12 zC$Jj;OW|#of=ETMLGq&Q?WVwlGx|cOg2%r>qpOdJ1!)N)^|Mfr1B3-vI&yx@sI(LK zRo(?po}xE?>5&TlE2+>6rNSM}G)yD9aDG23>_~+Oje>Wv)}^j@**nZu!C|SamY5gO zsZw@IixB8|W!WsVXFct?2)E*RG&f+#=B81NOgV;;&cph>fG2B)YFS>aVyGHhhVx;3 z56)7xZEjTu^Qx_zWz|T}-cIMzpMhk>w56d#K^|v=H30gJAdDOOTn60s3jKaiG{}S*!Gp3YX?`| zFDq{D0oGw&8|L+4-bh~Gp4_>6kazDpmXh7g83!~7by(g?31L^^c;Y4*%&(|dJi2tNq>x|@W1KU(MD*HhI%RFQx<*l3tWakG#cFz zFQA%Oh|(BPE#B&X&h$3dM6b`%t4vvj^j0~uy#5N;ODbPeRL$1E)$KJeVVIiF6ooQN z<9Y>I!~0?1sx+P*ALo~$$rA|37(NJai0NJ(y6rDmWNCYur8{bXS{cz7^(1btT6_Rx z^O@NB#K8NTzk={8f={4W-oFog3h@*Sdtsy$Sg!J`D0ME8Fz5K20BElDBS1Hi8vP^4 zj=bwvU`Hyu{Y5>MJ^0SOPu=$z)>N^UD%MlQM*8i?@{{=f^ugEeJDR}(+Ir6ZQ5-|K1aA)! zLP&&OIKr`$3ntn`Ky~>{#0%0DwY+4u0~_>jE6cySxF31^X?T%4c!^=Aw>jzCbJoUI z_-6YWyu~MA3I0>8Er6yd2ttDlxo2yGeCWQoHl>IFKJ~)JmCb8MIDXQd91NpuFKmo% O=HN0w+1^P%27d!emIe+0 literal 0 HcmV?d00001 diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 18a063c85a879cc83d00a9a0383835944a371483..37ef93b64b89a863dc14cd4c1fff85b75dce822c 100644 GIT binary patch literal 4050 zcmaJ^O>7&-72YM6|3CU6ZP}qJv#}h9P%E}}Yzsx;!qrbCN2CNv5Go?bcEuf&D^sL0 zODYzd06g@dLk>CwwSYK<(SsUQI_S`YfaK7lkIQ0#5?i2`o^+ESopkDZv;1Ar9+Gc& z=e?PIGw=J}8~!a2@N&?8{PQd2zfO+(A3ijiqkeP#cer`U$(+pRxOG0o^PG+Qgp+N5 z;$%DF*KCaedv=aHfS=h?c4j4jmE#>&0<&_0mFpc=4rb*BE6+QuoU(V3i}_B_ff#Q% z*9$Vqsp+~y>;j8q?Ys<^mmJ|zyv(I+HjdcWgt`tED%%e2d$6#B=HgP|LWhkY+wgPf z*n@Eg&9Nz`42wy*n8Pk`*ln4|EqlO8ui;!E8Ksavk>RaNF`MCCmtKfDlFt;ghO?Ac zz9^9s3{Fg-F1S!P2*a_Olk(3EAz#Yn(meDu8s6j9pZ9`z$$iDGH8k8Jx6dc{c^D+E z6TGS<_e{kB(aX_zwy;j(rPWftSc;PuBwZ>NX#C3peXeduX%bHtD2bcZ+_aTlmesFKg@bMzXFqx~QZfz2}jx9FUT zic0fS3%|EBw(Z7`Ipk-cPR2a+23+Y)6z`$HeKkDeS&2?5BquYs3?U8Rq9N4dFgl2$ z`C^LXVFCxMUxR3SuP?IwQ1?Y%t4AMczOkxrO!tjdCTfjtdNh12zX@x;WYw3{eaXti zslYpKo^nm$_My#8u8{*9@zn$@V2OK;e=0iL-aj{f4q=ai%PLX^64!jm~K5OMBTR;o12yAps{@~ARLorTlB+A zJ{Gf?L3dKdS&Aex8N^`5U{DUXJKrEiA6iC105STne;dwjmb-bBDJ_chm(+p{oUz zm`vNqm8g`nf|>z+1gw$3^;I=sT?-HHKGQ;Xw%xU%QOzAOzwNy8Ti=U;F}zcdVU$e% zQ?kYQVz|biERH{!n51k8Ltw+!@ph{H98FLJ;HQ2CqTv(ld0=RJSq}{DZXR9#$B>r% zTnmWRfT#z=%2bnYs^%N1`bKo$h{?43NUgeX+8psy?y5M$zKbpjbH(URM=+aA(?HV- zW+&4aw*ZiB%&}G-yDZ3#L#Ks-PRl$lSva)T9Sci|2RMsKk+nmQm62>le?t%Rc`cj{ z$Ufk7Q1$}^1BP=-qU(g7ynzw)0}vF+Y zjP-N_Ep4LG@FuO(%j zu%cpk)0D7$CKXMnTMEjraXu7SoYa>f+ECu89PLQ1^+tD`dT;cH-yIdTr+?6TrE0IF z_ezzSTIlx9Lp^l+2YMXVLXWGV$9m{-WwsU?#_r+QpB*p%^tl$At%hdx&@3qW?(AIG z`|d!s>Km)}jp==3m8aH{1e7r=lt}>ev^xQgLDM2Y2D0#9q5K2fgtZ1qIO2pS!~kpT zFb2t@?QH30_MWB%kV1AIx-7JSf&=sTnpSRNzXEhWvq6xsCVLNk7N7&f0q8w)0DvAe zLQgVONtbd(Opw!soD4^fm;=sl!uZ16e7R?w7B-^u6b>nA1U@J{vop(+i%WCkpO(Y3 z$|g~Zh4k}I6Gvj^_mdOlKtg#zGtiT)jm7Ndxl>j$ zN>Md}GD$1zQcm2+!QoG(AK~Z+AnKGfT7>L{^DC`0)*k8Fn8J{e?Wz>h}n8nSb8yftt&M=OXNk- z91zYG(k!vUaY#*5WdpI+HE;YDUy0&<_^E#Z(aIaAHs>|xDc2B{#A%QV-fAnDZ`QQH zN;R;e2Ueh7hVSl7>EXLa_x>qq%NZ@4t%kFDI9o~72JY@`>I0xT{#qMIR0k6JKtdD3 zwaCcsLp?HbG^|A)RU?n|$Rka-fte%0V~y=5&Gdl|Cu1(dBd{bvz%2f-5;BO6du!Mu)`9e{s+XVo#*))H>h=fPX#v*hv}N>ULsP;w>1XONmnOa`LAIBbB*N^?@}ia3Bm Xj6hs0JNZ4I6!!%N86YZR1Bw6uW;`GA diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index ebb8c6ea5957e01e2e132c7f2744cb84e6f77cdc..255a09618a18342523c2881a2f2ac4ff0940f477 100644 GIT binary patch literal 754 zcmZvZzi-n(6vyx4=hTjyhEg#A6(dq2h*cpNnyM3#A`!8mE|E~==8hz-f9Y(~lBr`Q z7})551QS0*2S)x3Te48(35ls&q;8#f*TjLMJfF|+eZQZR@7{ebmrDfW*N6AR*OZXo zvdBZ9CFegec}Eyw)Fuw?P)b;VDR#k8ItnEP!YKo;>_NVesi(zH3L)R{oyH}zgp8Ui zOk2q4$Q0*HX(3}EvovRx7cymJv^lf#Khsw`I;%V*)oW)Gs?rR6D;%6*3uK|b9P)!8 zBN+@lhfCMB96r)sSi<2VFf(JCb?spgj#hJ7@IKaDm|11!=gMDZYv_iX{{FU^m1hj& zp*6HeEBw&+M0j6t`9XVHcc=B!YAG|=p5^wvdL-;X$kqzkDIr^$>C6fSyPhSOkk=;U ztqMa<%hEP*4IU8p<_2HMIZEW>o+%G!M!S(4MkYVxy(sjAdEkltz_)tb?0Euh{8`<9 zb)juJ9*b{D=x(?p?^j>&p_b&Sbce05BUMAeTTA74M-dDH#)D27@BwG?V& z&``bgb|<}Y_sjDb8VNK~Xk5y%zJ9{fI}hT`Co!}WXs6Kr*Vy<`iJ_H1D}~lfzj^X7 ay$JV5OL?X*2W diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 8d204fa83f3150a3294bfb86b56b16ef24ab87c4..56d03073c4fc9668f6e3808601e4ace0a1c62706 100644 GIT binary patch literal 5983 zcmb7IU2GKB6`t9Dd;k51jTgL_hOiE-V@h0-@GFdeB3Kh_L0O>N@y=k6nca2n%o@B} zN2?08PGl(wLc&#@){QDc1*-DU2OjbesV{w;S&ih?sFosCJmjfHBvO@!s^{FOi2kqAk}2u=%1A~Q7$^+bjwj=p|0!Wup)GcBeQM0hU?qj;dWQi3dV z(}E0j�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 literal 0 HcmV?d00001 diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 5b49a3986008ff6f9a5fd0ce8ddd603f687562a2..9c0eaf3f7a9bc957a1408c7302d90f6ffbe07150 100644 GIT binary patch delta 19 ZcmZ3%xPp;;IWI340}#xbHfJLDTmUYw1ug&p delta 19 ZcmZ3%xPp;;IWI340}x2uoi>qsE&wd<1nmF- diff --git a/core/models.py b/core/models.py index 71a8362..ba0c825 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,48 @@ from django.db import models -# Create your models here. +class Region(models.Model): + name = models.CharField(max_length=100, unique=True) + code = models.CharField(max_length=10, unique=True, blank=True, null=True) + + def __str__(self): + return self.name + + class Meta: + ordering = ['name'] + +class Constituency(models.Model): + region = models.ForeignKey(Region, on_delete=models.CASCADE, related_name='constituencies') + name = models.CharField(max_length=100) + + def __str__(self): + return f"{self.name} ({self.region.name})" + + class Meta: + ordering = ['region', 'name'] + verbose_name_plural = "Constituencies" + +class Farmer(models.Model): + name = models.CharField(max_length=200) + id_number = models.CharField(max_length=50, unique=True, verbose_name="National ID/Passport") + phone_number = models.CharField(max_length=20, blank=True) + constituency = models.ForeignKey(Constituency, on_delete=models.PROTECT) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.name + +class AgriculturalHolding(models.Model): + HOLDING_TYPES = [ + ('CROP', 'Crop Production'), + ('LIVESTOCK', 'Livestock Production'), + ('MIXED', 'Mixed Farming'), + ('FISHERIES', 'Fisheries/Aquaculture'), + ('FORESTRY', 'Forestry'), + ] + farmer = models.ForeignKey(Farmer, on_delete=models.CASCADE, related_name='holdings') + size_hectares = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Size (Hectares)") + primary_activity = models.CharField(max_length=20, choices=HOLDING_TYPES) + location_description = models.TextField(blank=True) + + def __str__(self): + return f"{self.get_primary_activity_display()} - {self.farmer.name}" \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..d0c6c48 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,123 @@ - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {% block title %}NAIMS - Namibia Agricultural Information Management System{% endblock %} + + + + + + + + {% block extra_css %}{% endblock %} - - {% block content %}{% endblock %} - + - + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} + +
+ {% endfor %} +
+ {% endif %} + +
+ {% block content %}{% endblock %} +
+ +
+
+

© 2026 Ministry of Agriculture, Water and Land Reform (MAWLR) - Republic of Namibia

+ A National Digital Platform Aligned to SADC AIMS & FAO Standards +
+
+ + + + {% block extra_js %}{% endblock %} + + \ No newline at end of file diff --git a/core/templates/core/farmer_detail.html b/core/templates/core/farmer_detail.html new file mode 100644 index 0000000..df7b364 --- /dev/null +++ b/core/templates/core/farmer_detail.html @@ -0,0 +1,120 @@ +{% extends "base.html" %} + +{% block title %}{{ farmer.name }} Profile | NAIMS Namibia{% endblock %} + +{% block content %} +
+
+ +
+
+
+
+
+ {{ farmer.name|slice:":1" }} +
+

{{ farmer.name }}

+ Active Registration +
+ +
    +
  • + ID / Passport + {{ farmer.id_number }} +
  • +
  • + Phone Number + {{ farmer.phone_number|default:"N/A" }} +
  • +
  • + Constituency + {{ farmer.constituency.name }} +
  • +
  • + Region + {{ farmer.constituency.region.name }} +
  • +
  • + Date Joined + {{ farmer.created_at|date:"d M Y" }} +
  • +
+ + +
+
+
+ + +
+
+
+
Agricultural Holdings ({{ farmer.holdings.count }})
+ Add Another Holding +
+
+ {% for holding in farmer.holdings.all %} +
+
+
+
+
{{ holding.get_primary_activity_display }}
+

{{ holding.location_description|default:"No location provided." }}

+
+
+

{{ holding.size_hectares }} Hectares

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

No holdings registered for this farmer.

+
+ {% endfor %} + +
+ +
Historical Performance & Indicators
+
+
+
+ Crop Output + 0.0 t +
+
+
+
+ Livestock + 0 +
+
+
+
+ Input Use + N/A +
+
+
+
+ Food Security + STABLE +
+
+
+ +
+
+
+

System Analysis:

+

Detailed production reports are expected during the next harvest assessment cycle (July 2026).

+
+
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/farmer_form.html b/core/templates/core/farmer_form.html new file mode 100644 index 0000000..25aab01 --- /dev/null +++ b/core/templates/core/farmer_form.html @@ -0,0 +1,85 @@ +{% extends "base.html" %} + +{% block title %}Register Farmer | NAIMS Namibia{% endblock %} + +{% block content %} +
+
+
+
+
+

Farmer Registration

+

Enroll a new farmer and their holding into the national database.

+
+ Back to List +
+ +
+ {% csrf_token %} +
+ +
+
+
+
Personal Information
+ +
+ + {{ f_form.name }} +
+
+ + {{ f_form.id_number }} +
+
+ + {{ f_form.phone_number }} +
+
+ + {{ f_form.constituency }} + Select the constituency where the farmer is based. +
+
+
+
+ + +
+
+
+
Initial Holding Details
+ +
+ + {{ h_form.size_hectares }} +
+
+ + {{ h_form.primary_activity }} +
+
+ + {{ h_form.location_description }} +
+ +
+ Note: + More holdings can be added later from the farmer's profile page. +
+
+
+
+
+ +
+ +

By registering, you confirm the data matches official government records.

+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/farmer_list.html b/core/templates/core/farmer_list.html new file mode 100644 index 0000000..49979c5 --- /dev/null +++ b/core/templates/core/farmer_list.html @@ -0,0 +1,92 @@ +{% extends "base.html" %} + +{% block title %}National Farmer Registry | NAIMS Namibia{% endblock %} + +{% block content %} +
+
+
+

National Farmer Registry

+

Manage and view all registered farmers across Namibia.

+
+ +
+ + +
+
+
+
+ + +
+
+ +
+ {% if selected_region %} +
+ Clear +
+ {% endif %} +
+
+
+ + +
+
+
+ + + + + + + + + + + + + {% for farmer in farmers %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
Full NameID / PassportConstituency (Region)ContactDate RegisteredActions
{{ farmer.name }}{{ farmer.id_number }}{{ farmer.constituency.name }} ({{ farmer.constituency.region.name }}){{ farmer.phone_number|default:"N/A" }}{{ farmer.created_at|date:"d M Y" }} + View Profile +
+
+ + + +
+

No farmers found for the selected filter.

+ Add the first farmer +
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..96e1b11 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,158 @@ {% extends "base.html" %} -{% block title %}{{ project_name }}{% endblock %} +{% block title %}Public Reporting Dashboard | NAIMS Namibia{% endblock %} -{% block head %} - - - - +{% block extra_js %} + + {% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+

National Agricultural Reporting Dashboard

+

Public access to real-time agricultural statistics and performance indicators for the Republic of Namibia.

+
+ {% if user.is_authenticated %} + Register New Farmer + View National Registry + {% else %} + Export National Report (CSV) + Login (Authorized Personnel) + {% endif %} +
-

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) -
+ + +
+
+ +
+
+
+
Total Farmers
+

{{ total_farmers }}

+
+
+
+
+
+
+
Active Holdings
+

{{ total_holdings }}

+
+
+
+
+
+
+
National Regions
+

14

+
+
+
+
+
+
+
System Status
+

ONLINE

+
+
+
+
+ +
+ +
+
+
+
Activity Distribution
+
+
+
+ +
+
+
+
+ + +
+
+
+
Farmer Distribution by Region
+ {% if user.is_authenticated %} + Registry View + {% endif %} +
+
+
+ + + + + + + + + + + {% for stat in region_stats %} + + + + + + + {% empty %} + + + + {% endfor %} + +
RegionRegistered FarmersStatusProgress
{{ stat.name }}{{ stat.count }}Verified + {% with stat.count|default:0 as cnt %} +
+
+
+ {% endwith %} +
No data available yet.
+
+ +
+
+
+
+
{% endblock %} \ No newline at end of file diff --git a/core/templates/registration/login.html b/core/templates/registration/login.html new file mode 100644 index 0000000..7861165 --- /dev/null +++ b/core/templates/registration/login.html @@ -0,0 +1,31 @@ +{% extends 'base.html' %} + +{% block title %}Login - NAIMS Namibia{% endblock %} + +{% block content %} +
+
+
+
+

User Login

+
+ {% csrf_token %} + {% for field in form %} +
+ + {{ field.errors }} + +
+ {% endfor %} +
+ +
+
+
+ Only authorized Ministry personnel and Extension Officers can access the registration system. +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 6299e3d..582cff1 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,10 @@ from django.urls import path - -from .views import home +from . import views urlpatterns = [ - path("", home, name="home"), -] + path("", views.dashboard, name="home"), + path("farmers/", views.farmer_list, name="farmer_list"), + path("farmers/register/", views.farmer_register, name="farmer_register"), + path("farmers//", views.farmer_detail, name="farmer_detail"), + path("export-report/", views.export_report, name="export_report"), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..611b85e 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,101 @@ -import os -import platform +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from django.http import HttpResponse +from .models import Farmer, AgriculturalHolding, Region, Constituency +from .forms import FarmerForm, HoldingForm +import csv -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone - - -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 dashboard(request): + """Public National Dashboard Overview with Analytics""" + total_farmers = Farmer.objects.count() + total_holdings = AgriculturalHolding.objects.count() + regions = Region.objects.all() + + # Simple stats per region + region_stats = [] + for region in regions: + farmers_in_region = Farmer.objects.filter(constituency__region=region).count() + region_stats.append({'name': region.name, 'count': farmers_in_region}) + + # Stats by primary activity + activity_stats = [] + for code, label in AgriculturalHolding.HOLDING_TYPES: + count = AgriculturalHolding.objects.filter(primary_activity=code).count() + activity_stats.append({'label': label, 'value': count}) 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", ""), + "project_name": "NAIMS - Namibia", + "total_farmers": total_farmers, + "total_holdings": total_holdings, + "region_stats": sorted(region_stats, key=lambda x: x['count'], reverse=True), + "activity_stats": activity_stats, + "all_regions": regions, } return render(request, "core/index.html", context) + +@login_required +def farmer_list(request): + """View list of registered farmers with simple filtering.""" + region_id = request.GET.get('region') + farmers = Farmer.objects.all().select_related('constituency__region') + + if region_id: + farmers = farmers.filter(constituency__region_id=region_id) + + regions = Region.objects.all() + return render(request, "core/farmer_list.html", { + "farmers": farmers, + "regions": regions, + "selected_region": int(region_id) if region_id else None + }) + +@login_required +def farmer_register(request): + """Register a new farmer and their first holding.""" + if request.method == "POST": + f_form = FarmerForm(request.POST) + h_form = HoldingForm(request.POST) + + if f_form.is_valid() and h_form.is_valid(): + farmer = f_form.save() + holding = h_form.save(commit=False) + holding.farmer = farmer + holding.save() + + messages.success(request, f"Farmer {farmer.name} registered successfully!") + return redirect('farmer_list') + else: + f_form = FarmerForm() + h_form = HoldingForm() + + return render(request, "core/farmer_form.html", { + "f_form": f_form, + "h_form": h_form + }) + +@login_required +def farmer_detail(request, pk): + """View details of a single farmer.""" + farmer = get_object_or_404(Farmer.objects.select_related('constituency__region'), pk=pk) + return render(request, "core/farmer_detail.html", {"farmer": farmer}) + +def export_report(request): + """Public export of agricultural holdings report.""" + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="naims_agricultural_holdings_report.csv"' + + writer = csv.writer(response) + writer.writerow(['Farmer Name', 'Region', 'Constituency', 'Primary Activity', 'Size (Hectares)']) + + holdings = AgriculturalHolding.objects.select_related('farmer__constituency__region').all() + for h in holdings: + writer.writerow([ + h.farmer.name, + h.farmer.constituency.region.name, + h.farmer.constituency.name, + h.get_primary_activity_display(), + h.size_hectares + ]) + + return response \ No newline at end of file