Bez popisu

views.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. from __future__ import annotations
  2. from django.shortcuts import render, redirect, get_object_or_404
  3. from django.contrib import messages
  4. from django.urls import reverse
  5. from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
  6. from orgs.models import Organization, UserProfile
  7. from recycle_core.models import ProvidedService
  8. from recycle_core.models import Material, ScrapListing, PickupOrder
  9. from cms.models import Post, PostCategory
  10. from .forms import (
  11. PickupRequestForm,
  12. ContactForm,
  13. RegistrationForm,
  14. PublicUserForm,
  15. PublicUserPhotoForm,
  16. PublicUserProfileExtraForm,
  17. )
  18. from .models import Lead
  19. from recycle_core.controllers.pickup_request import (
  20. PickupRequestController,
  21. PickupRequestData,
  22. )
  23. from django.contrib.auth.views import LoginView
  24. from django.urls import reverse
  25. def home(request):
  26. org = getattr(request, "org", None)
  27. posts_qs = Post.objects.all()
  28. if org is not None:
  29. posts_qs = posts_qs.filter(organization=org)
  30. posts = posts_qs.order_by("-published_at")[:3]
  31. listings_qs = ScrapListing.objects.filter(is_public=True)
  32. if org is not None:
  33. listings_qs = listings_qs.filter(organization=org)
  34. listings = listings_qs.order_by("-created_at")[:6]
  35. materials_qs = Material.objects.all().order_by("name")
  36. if org is not None:
  37. materials_qs = materials_qs.filter(organization=org)
  38. materials = materials_qs[:12]
  39. services_qs = ProvidedService.objects.none()
  40. if org is not None:
  41. services_qs = ProvidedService.objects.filter(organization=org, is_enabled=True).order_by("display_order", "id")
  42. services = list(services_qs)
  43. pickup_form = PickupRequestForm()
  44. contact_form = ContactForm()
  45. register_form = RegistrationForm()
  46. return render(
  47. request,
  48. "public_frontend/home.html",
  49. {
  50. "posts": posts,
  51. "listings": listings,
  52. "materials": materials,
  53. "services": services,
  54. "pickup_form": pickup_form,
  55. "contact_form": contact_form,
  56. "org": org,
  57. "register_form": register_form,
  58. },
  59. )
  60. def materials_list(request):
  61. org = getattr(request, "org", None)
  62. qs = Material.objects.all().order_by("name")
  63. if org is not None:
  64. qs = qs.filter(organization=org)
  65. return render(request, "public_frontend/materials_list.html", {"materials": qs, "org": org})
  66. def listings_list(request):
  67. org = getattr(request, "org", None)
  68. qs = ScrapListing.objects.filter(is_public=True, status=ScrapListing.STATUS_OPEN).order_by("-created_at")
  69. if org is not None:
  70. qs = qs.filter(organization=org)
  71. paginator = Paginator(qs, 12)
  72. page = request.GET.get("page")
  73. try:
  74. page_obj = paginator.page(page)
  75. except PageNotAnInteger:
  76. page_obj = paginator.page(1)
  77. except EmptyPage:
  78. page_obj = paginator.page(paginator.num_pages)
  79. return render(request, "public_frontend/listings_list.html", {"page_obj": page_obj, "listings": page_obj.object_list, "org": org})
  80. def listing_detail(request, pk: int):
  81. org = getattr(request, "org", None)
  82. base_qs = ScrapListing.objects.filter(is_public=True)
  83. if org is not None:
  84. base_qs = base_qs.filter(organization=org)
  85. listing = get_object_or_404(base_qs, pk=pk)
  86. return render(request, "public_frontend/listing_detail.html", {"listing": listing, "org": org})
  87. def service_detail(request, pk: int):
  88. org = getattr(request, "org", None)
  89. base_qs = ProvidedService.objects.filter(is_enabled=True)
  90. if org is not None:
  91. base_qs = base_qs.filter(organization=org)
  92. service = get_object_or_404(base_qs, pk=pk)
  93. return render(request, "public_frontend/service_detail.html", {"service": service, "org": org})
  94. def pickup_request(request):
  95. org = getattr(request, "org", None)
  96. # Prefill form for authenticated users
  97. initial = {}
  98. if request.user.is_authenticated:
  99. u = request.user
  100. full_name = (getattr(u, "get_full_name", lambda: "")() or "").strip()
  101. initial["name"] = full_name or u.username
  102. if getattr(u, "email", ""):
  103. initial["email"] = u.email
  104. form = PickupRequestForm(request.POST or None, request.FILES or None, initial=initial)
  105. if request.method == "POST":
  106. if not org:
  107. messages.error(request, "Organization context missing.")
  108. return redirect("public_frontend:pickup_request")
  109. if form.is_valid():
  110. ctrl = PickupRequestController()
  111. data = PickupRequestData(
  112. organization=org,
  113. name=form.cleaned_data.get("name"),
  114. email=form.cleaned_data.get("email", ""),
  115. phone=form.cleaned_data.get("phone", ""),
  116. address=form.cleaned_data.get("address", ""),
  117. materials=form.cleaned_data.get("materials", ""),
  118. preferred_at=form.cleaned_data.get("preferred_at"),
  119. files=request.FILES.getlist("photos") if hasattr(request, "FILES") and "photos" in request.FILES else [],
  120. created_by=request.user if request.user.is_authenticated else None,
  121. )
  122. result = ctrl.submit(data)
  123. if result.ok:
  124. messages.success(request, "Thanks! Your pickup request was submitted.")
  125. return redirect("public_frontend:home")
  126. else:
  127. messages.error(request, result.error or "Unable to submit your request.")
  128. else:
  129. messages.error(request, "Please correct the errors below.")
  130. return render(request, "public_frontend/pickup_request.html", {"form": form, "org": org})
  131. def contact(request):
  132. org = getattr(request, "org", None)
  133. form = ContactForm(request.POST or None)
  134. if request.method == "POST":
  135. if not org:
  136. messages.error(request, "Organization context missing.")
  137. return redirect("public_frontend:contact")
  138. if form.is_valid():
  139. Lead.objects.create(
  140. organization=org,
  141. name=form.cleaned_data["name"],
  142. email=form.cleaned_data["email"],
  143. phone=form.cleaned_data.get("phone", ""),
  144. subject=form.cleaned_data.get("subject", ""),
  145. message=form.cleaned_data.get("message", ""),
  146. source="contact",
  147. created_by=request.user if request.user.is_authenticated else None,
  148. )
  149. messages.success(request, "Thanks! We will get back to you shortly.")
  150. return redirect("public_frontend:home")
  151. messages.error(request, "Please correct the errors below.")
  152. return render(request, "public_frontend/contact.html", {"form": form, "org": org})
  153. def register(request):
  154. from django.contrib.auth import get_user_model
  155. from django.contrib.auth.models import Group
  156. from orgs.models import UserProfile
  157. org = getattr(request, "org", None)
  158. if not org:
  159. messages.error(request, "Organization context missing.")
  160. return redirect("public_frontend:home")
  161. form = RegistrationForm(request.POST or None)
  162. if request.method == "POST":
  163. if form.is_valid():
  164. User = get_user_model()
  165. user = User(username=form.cleaned_data["username"], email=form.cleaned_data["email"], is_active=False)
  166. user.set_password(form.cleaned_data["password1"])
  167. user.save()
  168. role = form.cleaned_data["role"]
  169. # Create org profile
  170. UserProfile.objects.create(user=user, organization=org, role=role)
  171. # Add to group matching the role if exists
  172. try:
  173. g = Group.objects.filter(name=role).first()
  174. if g:
  175. user.groups.add(g)
  176. except Exception:
  177. pass
  178. messages.success(request, "Account created. Please wait for admin approval.")
  179. return redirect("public_frontend:home")
  180. else:
  181. messages.error(request, "Please correct the errors below.")
  182. return render(request, "public_frontend/register.html", {"form": form, "org": org})
  183. def blog_list(request):
  184. org = getattr(request, "org", None)
  185. qs = Post.objects.all().order_by("-published_at", "-created_at")
  186. if org is not None:
  187. qs = qs.filter(organization=org)
  188. paginator = Paginator(qs, 10)
  189. page = request.GET.get("page")
  190. try:
  191. page_obj = paginator.page(page)
  192. except PageNotAnInteger:
  193. page_obj = paginator.page(1)
  194. except EmptyPage:
  195. page_obj = paginator.page(paginator.num_pages)
  196. return render(request, "public_frontend/blog_list.html", {"page_obj": page_obj, "posts": page_obj.object_list, "org": org})
  197. def blog_detail(request, slug: str):
  198. org = getattr(request, "org", None)
  199. base_qs = Post.objects.all()
  200. if org is not None:
  201. base_qs = base_qs.filter(organization=org)
  202. post = get_object_or_404(base_qs, slug=slug)
  203. # Ensure markdown is rendered; fallback to raw content if needed
  204. html = getattr(post, "content_html", None) or getattr(post, "content", "")
  205. return render(request, "public_frontend/blog_detail.html", {"post": post, "html": html, "org": org})
  206. class PublicLoginView(LoginView):
  207. template_name = "public_frontend/login.html"
  208. redirect_authenticated_user = True
  209. def get_success_url(self):
  210. return reverse("public_frontend:home")
  211. from django.contrib.auth.decorators import login_required
  212. from django.contrib.auth import get_user_model
  213. from django.utils import timezone
  214. from recycle_core.models import CarbonEvent, CarbonBalance
  215. @login_required
  216. def my_profile(request):
  217. user = request.user
  218. profile = getattr(user, "recycle_profile", None)
  219. role_code = getattr(profile, "role", None)
  220. role_label = dict(UserProfile.ROLE_CHOICES).get(role_code, "-") if role_code else "-"
  221. if request.method == "POST":
  222. form_user = PublicUserForm(request.POST, instance=user)
  223. form_photo = PublicUserPhotoForm(request.POST, request.FILES, instance=profile) if profile else None
  224. form_extras = PublicUserProfileExtraForm(request.POST, instance=profile) if profile else None
  225. ok_user = form_user.is_valid()
  226. ok_photo = True if form_photo is None else form_photo.is_valid()
  227. ok_extras = True if form_extras is None else form_extras.is_valid()
  228. if ok_user and ok_photo and ok_extras:
  229. form_user.save()
  230. if form_photo is not None:
  231. form_photo.save()
  232. if form_extras is not None:
  233. form_extras.save()
  234. messages.success(request, "Profile updated.")
  235. return redirect(reverse("public_frontend:my_profile"))
  236. messages.error(request, "Please correct the errors below.")
  237. else:
  238. form_user = PublicUserForm(instance=user)
  239. form_photo = PublicUserPhotoForm(instance=profile) if profile else None
  240. form_extras = PublicUserProfileExtraForm(instance=profile) if profile else None
  241. # Request history (pickup requests submitted via public form, matched by email)
  242. history = []
  243. try:
  244. email = (user.email or "").strip()
  245. if email:
  246. qs = Lead.objects.filter(email__iexact=email, source="pickup_request").order_by("-created_at")
  247. org = getattr(request, "org", None)
  248. if org is not None:
  249. qs = qs.filter(organization=org)
  250. history = list(qs[:20])
  251. except Exception:
  252. history = []
  253. # Carbon summary for user's organization
  254. org = getattr(request, "org", None) or getattr(profile, "organization", None)
  255. carbon = None
  256. if org is not None:
  257. today = timezone.now().date()
  258. bal = CarbonBalance.objects.filter(organization=org, year=today.year, month=today.month).first()
  259. pending = CarbonEvent.objects.filter(organization=org, status=CarbonEvent.STATUS_PENDING).count()
  260. carbon = {
  261. "pending_events": pending,
  262. "approved_mtd": bal.approved_kgco2e if bal else 0,
  263. }
  264. return render(
  265. request,
  266. "public_frontend/my_profile.html",
  267. {
  268. "form_user": form_user,
  269. "form_photo": form_photo,
  270. "form_extras": form_extras,
  271. "has_profile": bool(profile),
  272. "username_value": user.username,
  273. "role_label": role_label,
  274. "request_history": history,
  275. "carbon": carbon,
  276. },
  277. )