diff --git a/council/__pycache__/models.cpython-312.pyc b/council/__pycache__/models.cpython-312.pyc index ba5165b..3476787 100644 Binary files a/council/__pycache__/models.cpython-312.pyc and b/council/__pycache__/models.cpython-312.pyc differ diff --git a/council/__pycache__/urls.cpython-312.pyc b/council/__pycache__/urls.cpython-312.pyc index b6d3000..73546aa 100644 Binary files a/council/__pycache__/urls.cpython-312.pyc and b/council/__pycache__/urls.cpython-312.pyc differ diff --git a/council/__pycache__/views.cpython-312.pyc b/council/__pycache__/views.cpython-312.pyc index d85b83d..c138571 100644 Binary files a/council/__pycache__/views.cpython-312.pyc and b/council/__pycache__/views.cpython-312.pyc differ diff --git a/council/models.py b/council/models.py index 0ecbc6e..40aa047 100644 --- a/council/models.py +++ b/council/models.py @@ -55,11 +55,6 @@ class Question(models.Model): body = models.TextField() member = models.ForeignKey(Member, related_name="questions", on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) - # asker metadata - asker_first_name = models.CharField(max_length=150, blank=True) - asker_last_name = models.CharField(max_length=150, blank=True) - asker_city = models.CharField(max_length=150, blank=True) - asker_email = models.EmailField(blank=True) class Meta: verbose_name = "Frage" diff --git a/council/urls.py b/council/urls.py index 798a6a7..b97155f 100644 --- a/council/urls.py +++ b/council/urls.py @@ -7,7 +7,6 @@ urlpatterns = [ path("gemeinden//", views.public_body_detail, name="public_body_detail"), path("mitglieder/", views.members, name="members"), path("mitglieder//", views.member_detail, name="member_detail"), - path("mitglieder//frage/stellen", views.ask_question, name="ask_question"), path("parteien/", views.parties, name="parties"), path("parteien//", views.party_detail, name="party_detail"), path("fragen/", views.questions, name="questions"), diff --git a/council/views.py b/council/views.py index 644bf8b..0161474 100644 --- a/council/views.py +++ b/council/views.py @@ -1,5 +1,4 @@ -from django.shortcuts import render, get_object_or_404, redirect -from django.contrib import messages +from django.shortcuts import render, get_object_or_404 from .models import PublicBody, Party, Member, Question, Vote @@ -40,34 +39,6 @@ def member_detail(request, pk: int): ) -def ask_question(request, pk: int): - member = get_object_or_404(Member, pk=pk) - if request.method != "POST": - return redirect("member_detail", pk=member.pk) - - title = request.POST.get("title", "").strip() - asker_first_name = request.POST.get("asker_first_name", "").strip() - asker_last_name = request.POST.get("asker_last_name", "").strip() - asker_city = request.POST.get("asker_city", "").strip() - asker_email = request.POST.get("asker_email", "").strip() - - if not title or not asker_first_name or not asker_last_name or not asker_city or not asker_email: - messages.error(request, "Bitte alle Pflichtfelder ausfüllen.") - return redirect("member_detail", pk=member.pk) - - Question.objects.create( - title=title, - body=title, - member=member, - asker_first_name=asker_first_name, - asker_last_name=asker_last_name, - asker_city=asker_city, - asker_email=asker_email, - ) - messages.success(request, "Frage wurde eingereicht.") - return redirect("member_detail", pk=member.pk) - - def parties(request): items = Party.objects.all() return render(request, "council/parties.html", {"items": items}) diff --git a/db.sqlite3 b/db.sqlite3 index ac4eee1..7ed9b0f 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/flake.nix b/flake.nix index 4f8a751..a1fa7bc 100644 --- a/flake.nix +++ b/flake.nix @@ -28,16 +28,18 @@ dependencies = with python3Packages; [ django requests jinja2 ]; installPhase = '' - mkdir -p $out/share/fragdenrat + mkdir -p $out/share # Project code - cp -r "${./fragdenrat}/." $out/share/fragdenrat/ - # Django app: council - cp -r ${./council} $out/share/fragdenrat/council + cp -r ${./fragdenrat} $out/share/fragdenrat # Django manage helper install -Dm755 ${./manage.py} $out/bin/fragdenrat-manage # Templates and static assets - cp -r ./templates $out/share/fragdenrat/ - cp -r ./assets $out/share/fragdenrat/ + if [ -d ./templates ]; then + cp -r ./templates $out/share/fragdenrat/ + fi + if [ -d ./assets ]; then + cp -r ./assets $out/share/fragdenrat/ + fi ''; passthru.pythonPath = python3Packages.makePythonPath dependencies; diff --git a/fragdenrat/__pycache__/settings.cpython-312.pyc b/fragdenrat/__pycache__/settings.cpython-312.pyc index 84fad05..b3ae2f5 100644 Binary files a/fragdenrat/__pycache__/settings.cpython-312.pyc and b/fragdenrat/__pycache__/settings.cpython-312.pyc differ diff --git a/fragdenrat/__pycache__/urls.cpython-312.pyc b/fragdenrat/__pycache__/urls.cpython-312.pyc index 6cafa8b..8c58cdf 100644 Binary files a/fragdenrat/__pycache__/urls.cpython-312.pyc and b/fragdenrat/__pycache__/urls.cpython-312.pyc differ diff --git a/fragdenrat/settings.py b/fragdenrat/settings.py index c1525c0..fd08fb6 100644 --- a/fragdenrat/settings.py +++ b/fragdenrat/settings.py @@ -2,7 +2,6 @@ from pathlib import Path import os BASE_DIR = Path(__file__).resolve().parent.parent -PROJECT_DIR = Path(__file__).resolve().parent DATA_DIR = Path(os.environ.get("FRAGDENRAT_DATA_DIR", str(BASE_DIR))) SECRET_KEY = os.environ.get("SECRET_KEY", "dev-secret-key-change-me") @@ -34,10 +33,7 @@ ROOT_URLCONF = "fragdenrat.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [ - BASE_DIR / "templates", - PROJECT_DIR / "templates", - ], + "DIRS": [BASE_DIR / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -65,9 +61,6 @@ USE_I18N = True USE_TZ = True STATIC_URL = "/static/" -STATICFILES_DIRS = [ - BASE_DIR / "assets", - PROJECT_DIR / "assets", -] +STATICFILES_DIRS = [BASE_DIR / "assets"] STATIC_ROOT = DATA_DIR / "staticfiles" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" \ No newline at end of file diff --git a/fragdenrat/urls.py b/fragdenrat/urls.py index 264b370..de090f4 100644 --- a/fragdenrat/urls.py +++ b/fragdenrat/urls.py @@ -1,10 +1,7 @@ from django.contrib import admin from django.urls import path, include -from django.views.generic import TemplateView urlpatterns = [ path("admin/", admin.site.urls), - path("impressum", TemplateView.as_view(template_name="impressum.html"), name="impressum"), - path("datenschutz", TemplateView.as_view(template_name="datenschutz.html"), name="datenschutz"), path("", include("council.urls")), ] \ No newline at end of file diff --git a/module.nix b/module.nix index 6e95863..390bfaa 100644 --- a/module.nix +++ b/module.nix @@ -5,64 +5,35 @@ ... }: let + cfg = config.services.fragdenrat; - dataDir = "/var/lib/fragdenrat"; - manageCmd = "${pkgs.fragdenrat}/bin/fragdenrat-manage"; - pythonPath = "${pkgs.fragdenrat}/share:${pkgs.fragdenrat.pythonPath}"; - envVars = [ - "PYTHONPATH=${pythonPath}" - "DJANGO_SETTINGS_MODULE=fragdenrat.settings" - "FRAGDENRAT_DATA_DIR=${dataDir}" - ]; + in { + options = { services.fragdenrat = { + enable = lib.mkEnableOption "FragDenRat web app"; + }; }; config = lib.mkIf cfg.enable { - # Ensure data dir exists with proper perms - systemd.tmpfiles.rules = [ - "d ${dataDir} 0750 fragdenrat fragdenrat -" - "d ${dataDir}/staticfiles 0750 fragdenrat fragdenrat -" - ]; - - # One-shot setup: migrate DB and collect static into dataDir - systemd.services.fragdenrat-setup = { - description = "Initialize FragDenRat database and static files"; - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; - serviceConfig = { - Type = "oneshot"; - User = "fragdenrat"; - Group = "fragdenrat"; - WorkingDirectory = dataDir; - Environment = envVars; - }; - script = '' - set -euo pipefail - ${manageCmd} migrate --noinput - ${manageCmd} collectstatic --noinput - ''; - }; - - # uWSGI app services.uwsgi = { enable = true; plugins = [ "python3" ]; + instance = { type = "emperor"; vassals = { fragdenrat = { type = "normal"; - # Use data dir as working directory - chdir = dataDir; + chdir = "${pkgs.fragdenrat}/share/fragdenrat"; - # Absolute WSGI entrypoint in Nix store - wsgi-file = "${pkgs.fragdenrat}/share/fragdenrat/wsgi.py"; + # Use absolute wsgi entrypoint to avoid module resolution issues + wsgi-file = "${pkgs.fragdenrat}/share/fragdenrat/fragdenrat/wsgi.py"; callable = "application"; socket = "${config.services.uwsgi.runDir}/fragdenrat.sock"; @@ -78,13 +49,15 @@ in need-app = true; "no-orphans" = true; - env = envVars; + env = [ + # Ensure python sees the app and dependencies + "PYTHONPATH=${pkgs.fragdenrat}/share/fragdenrat:${pkgs.fragdenrat.pythonPath}" + "DJANGO_SETTINGS_MODULE=fragdenrat.settings" + ]; settings = { - # Serve collected static files from dataDir - "static-map" = "/static=${dataDir}/staticfiles"; - # Python import path for the app and its deps - pythonpath = "${pkgs.fragdenrat}/share"; + "static-map" = "/static=${pkgs.fragdenrat}/share/fragdenrat/assets"; + pythonpath = "${pkgs.fragdenrat}/share/fragdenrat"; }; }; }; diff --git a/result b/result index 430629b..8440bcc 120000 --- a/result +++ b/result @@ -1 +1 @@ -/nix/store/jsjajfw1bbm7h28fh8q64zignm6z09yn-fragdenrat-0.1.0 \ No newline at end of file +/nix/store/wqsr5qqw8lj49hrags3pzairq70fwwb6-fragdenrat-0.1.0 \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 327f623..2e3eaca 100644 --- a/templates/base.html +++ b/templates/base.html @@ -13,7 +13,7 @@ .navbar-brand img { height: 36px; } - + -
+
{% block content %}{% endblock %}
-
- {% block extra_js %}{% endblock %} diff --git a/templates/council/member_detail.html b/templates/council/member_detail.html index 16b1645..4d78d99 100644 --- a/templates/council/member_detail.html +++ b/templates/council/member_detail.html @@ -1,26 +1,11 @@ {% extends "base.html" %} {% block title %}{{ member }} – Stadträt:in{% endblock %} {% block content %} -
-
-

{{ member.first_name }} {{ member.last_name }}

-

- {% if member.public_body %}in {{ member.public_body.name }}{% endif %} - {% if member.party %} • {{ member.party }} (Profil){% endif %} -

-
-
- -
-
- -{% if messages %} -
- {% for message in messages %} -
{{ message }}
- {% endfor %} -
-{% endif %} +

{{ member.first_name }} {{ member.last_name }}

+

+ {% if member.public_body %}in {{ member.public_body.name }}{% endif %} + {% if member.party %} • {{ member.party }} (Profil){% endif %} +

Fragen an {{ member.first_name }}

    @@ -48,47 +33,4 @@
  • Keine Abstimmungen.
  • {% endfor %}
- - - {% endblock %} \ No newline at end of file