adjust citysite table name to activesites
This commit is contained in:
1 parent
fb0f996776
commit
64b37b6b6d
27 files changed
+382
-221
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
Settled matrix (v1):
|
||||
- buh, director (+ their admins until specified otherwise): view only
|
||||
- gm + gm_admin: view + register city sites (URL) + sync from cities
|
||||
- gm + gm_admin: view + register active sites (URL) + sync from sites
|
||||
- gm only (not gm_admin): approve cycle close
|
||||
"""
|
||||
|
||||
@@ -22,7 +22,7 @@ VIEW_ONLY_STATUSES = frozenset(
|
||||
}
|
||||
)
|
||||
|
||||
# Can register city dashboard URLs into the HQ mirror registry.
|
||||
# Can register site dashboard URLs into the HQ mirror registry.
|
||||
SITE_REGISTRY_STATUSES = frozenset(
|
||||
{
|
||||
User.STATUS_GM,
|
||||
@@ -30,7 +30,7 @@ SITE_REGISTRY_STATUSES = frozenset(
|
||||
}
|
||||
)
|
||||
|
||||
# Can trigger city → HQ mirror sync.
|
||||
# Can trigger site → HQ mirror sync.
|
||||
SYNC_STATUSES = frozenset(
|
||||
{
|
||||
User.STATUS_GM,
|
||||
@@ -73,8 +73,8 @@ def can_sync(user) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def can_register_city_site(user) -> bool:
|
||||
"""GM / GM Admin may add city dashboard URLs to the HQ registry."""
|
||||
def can_register_active_site(user) -> bool:
|
||||
"""GM / GM Admin may add site dashboard URLs to the HQ registry."""
|
||||
return bool(
|
||||
user
|
||||
and getattr(user, "is_authenticated", False)
|
||||
@@ -105,9 +105,9 @@ def can_manage_users(user) -> bool:
|
||||
return _status(user) in USER_ADMIN_STATUSES
|
||||
|
||||
|
||||
def can_manage_city_registry(user) -> bool:
|
||||
"""Alias used by city-site write endpoints — same as register permission."""
|
||||
return can_register_city_site(user) or bool(getattr(user, "is_superuser", False))
|
||||
def can_manage_active_site_registry(user) -> bool:
|
||||
"""Alias used by active-site write endpoints — same as register permission."""
|
||||
return can_register_active_site(user) or bool(getattr(user, "is_superuser", False))
|
||||
|
||||
|
||||
class IsExecutive(permissions.BasePermission):
|
||||
@@ -120,9 +120,9 @@ class IsExecutiveAdmin(permissions.BasePermission):
|
||||
return can_manage_users(request.user)
|
||||
|
||||
|
||||
class CanRegisterCitySite(permissions.BasePermission):
|
||||
class CanRegisterActiveSite(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return can_register_city_site(request.user) or bool(
|
||||
return can_register_active_site(request.user) or bool(
|
||||
getattr(request.user, "is_superuser", False)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""Visibility scoping for HQ executives.
|
||||
|
||||
- director / director_admin: all sites / city sites / GMs
|
||||
- director / director_admin: all sites / active sites / GMs
|
||||
- buh / bu_admin: only GMs (and gm_admins) they manage, plus those GMs' sites
|
||||
- gm / gm_admin: only their own sites / city sites they registered
|
||||
- gm / gm_admin: only their own sites / active sites they registered
|
||||
|
||||
Farm Site rows are only visible to non-directors when linked to an active
|
||||
CitySite in the viewer's Pengaturan registry.
|
||||
ActiveSite in the viewer's Pengaturan registry.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -61,7 +61,7 @@ def managed_gm_ids(buh: User) -> list[int]:
|
||||
|
||||
def visible_owner_ids(user: User) -> list[int] | None:
|
||||
"""
|
||||
Site.user / CitySite.registered_by ids this viewer may see.
|
||||
Site.user / ActiveSite.registered_by ids this viewer may see.
|
||||
|
||||
Returns None when there is no restriction (director / superuser).
|
||||
"""
|
||||
@@ -74,13 +74,13 @@ def visible_owner_ids(user: User) -> list[int] | None:
|
||||
return []
|
||||
|
||||
|
||||
def visible_active_city_site_ids(user: User) -> list[int] | None:
|
||||
def visible_active_site_ids(user: User) -> list[int] | None:
|
||||
"""
|
||||
Active CitySite pks in this viewer's Pengaturan registry.
|
||||
Enabled ActiveSite pks in this viewer's Pengaturan registry.
|
||||
|
||||
Returns None for directors (no restriction). Empty list means no farm sites.
|
||||
"""
|
||||
from apps.sync.models import CitySite
|
||||
from apps.sync.models import ActiveSite
|
||||
|
||||
owners = visible_owner_ids(user)
|
||||
if owners is None:
|
||||
@@ -88,7 +88,7 @@ def visible_active_city_site_ids(user: User) -> list[int] | None:
|
||||
if not owners:
|
||||
return []
|
||||
return list(
|
||||
CitySite.objects.filter(
|
||||
ActiveSite.objects.filter(
|
||||
registered_by_id__in=owners,
|
||||
is_active=True,
|
||||
).values_list("pk", flat=True)
|
||||
@@ -120,13 +120,13 @@ def filter_sites_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
return qs
|
||||
if not owners:
|
||||
return qs.none()
|
||||
city_ids = visible_active_city_site_ids(viewer)
|
||||
if not city_ids:
|
||||
active_ids = visible_active_site_ids(viewer)
|
||||
if not active_ids:
|
||||
return qs.none()
|
||||
return qs.filter(user_id__in=owners, city_site_id__in=city_ids)
|
||||
return qs.filter(user_id__in=owners, active_site_id__in=active_ids)
|
||||
|
||||
|
||||
def filter_city_sites_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
def filter_active_sites_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
owners = visible_owner_ids(viewer)
|
||||
if owners is None:
|
||||
return qs
|
||||
@@ -141,10 +141,12 @@ def filter_kandangs_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
return qs
|
||||
if not owners:
|
||||
return qs.none()
|
||||
city_ids = visible_active_city_site_ids(viewer)
|
||||
if not city_ids:
|
||||
active_ids = visible_active_site_ids(viewer)
|
||||
if not active_ids:
|
||||
return qs.none()
|
||||
return qs.filter(site__user_id__in=owners, site__city_site_id__in=city_ids)
|
||||
return qs.filter(
|
||||
site__user_id__in=owners, site__active_site_id__in=active_ids
|
||||
)
|
||||
|
||||
|
||||
def filter_flocks_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
@@ -153,12 +155,12 @@ def filter_flocks_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
return qs
|
||||
if not owners:
|
||||
return qs.none()
|
||||
city_ids = visible_active_city_site_ids(viewer)
|
||||
if not city_ids:
|
||||
active_ids = visible_active_site_ids(viewer)
|
||||
if not active_ids:
|
||||
return qs.none()
|
||||
return qs.filter(
|
||||
kandang__site__user_id__in=owners,
|
||||
kandang__site__city_site_id__in=city_ids,
|
||||
kandang__site__active_site_id__in=active_ids,
|
||||
)
|
||||
|
||||
|
||||
@@ -168,10 +170,10 @@ def filter_cycles_queryset(qs: QuerySet, viewer: User) -> QuerySet:
|
||||
return qs
|
||||
if not owners:
|
||||
return qs.none()
|
||||
city_ids = visible_active_city_site_ids(viewer)
|
||||
if not city_ids:
|
||||
active_ids = visible_active_site_ids(viewer)
|
||||
if not active_ids:
|
||||
return qs.none()
|
||||
return qs.filter(
|
||||
kandang__site__user_id__in=owners,
|
||||
kandang__site__city_site_id__in=city_ids,
|
||||
kandang__site__active_site_id__in=active_ids,
|
||||
)
|
||||
@@ -3,7 +3,7 @@ from rest_framework.test import APIClient
|
||||
|
||||
from apps.accounts.models import User
|
||||
from apps.farms.models import Kandang, Site
|
||||
from apps.sync.models import CitySite
|
||||
from apps.sync.models import ActiveSite
|
||||
|
||||
|
||||
class VisibilityScopeTests(TestCase):
|
||||
@@ -27,23 +27,23 @@ class VisibilityScopeTests(TestCase):
|
||||
status=User.STATUS_GM,
|
||||
managed_by=self.buh_other,
|
||||
)
|
||||
self.city_a = CitySite.objects.create(
|
||||
self.active_a = ActiveSite.objects.create(
|
||||
code="a",
|
||||
name="City A",
|
||||
name="Site A",
|
||||
api_base_url="https://a.example/api/v1",
|
||||
registered_by=self.gm,
|
||||
)
|
||||
self.city_b = CitySite.objects.create(
|
||||
self.active_b = ActiveSite.objects.create(
|
||||
code="b",
|
||||
name="City B",
|
||||
name="Site B",
|
||||
api_base_url="https://b.example/api/v1",
|
||||
registered_by=self.gm_other,
|
||||
)
|
||||
self.site = Site.objects.create(
|
||||
site_name="Site A", user=self.gm, city_site=self.city_a
|
||||
site_name="Site A", user=self.gm, active_site=self.active_a
|
||||
)
|
||||
self.site_other = Site.objects.create(
|
||||
site_name="Site B", user=self.gm_other, city_site=self.city_b
|
||||
site_name="Site B", user=self.gm_other, active_site=self.active_b
|
||||
)
|
||||
self.orphan = Site.objects.create(site_name="Orphan Seed", user=self.gm)
|
||||
Kandang.objects.create(kandang_name="K1", site=self.site)
|
||||
@@ -70,9 +70,9 @@ class VisibilityScopeTests(TestCase):
|
||||
names = {row["display_name"] for row in response.data["results"]}
|
||||
self.assertEqual(names, {"gm1"})
|
||||
|
||||
def test_gm_sees_own_city_sites_only(self):
|
||||
def test_gm_sees_own_active_sites_only(self):
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.get("/api/v1/city-sites/")
|
||||
response = self.client.get("/api/v1/active-sites/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
codes = {row["code"] for row in response.data["results"]}
|
||||
self.assertEqual(codes, {"a"})
|
||||
@@ -85,16 +85,16 @@ class VisibilityScopeTests(TestCase):
|
||||
self.assertEqual(names, {"Site A"})
|
||||
|
||||
def test_gm_with_empty_registry_sees_no_sites(self):
|
||||
self.city_a.delete()
|
||||
self.active_a.delete()
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.get("/api/v1/sites/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["results"], [])
|
||||
|
||||
def test_registering_city_site_attaches_matching_seed_site(self):
|
||||
def test_registering_active_site_attaches_matching_seed_site(self):
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.post(
|
||||
"/api/v1/city-sites/",
|
||||
"/api/v1/active-sites/",
|
||||
{
|
||||
"code": "orphan-seed",
|
||||
"name": "Orphan Seed",
|
||||
@@ -105,14 +105,14 @@ class VisibilityScopeTests(TestCase):
|
||||
)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.orphan.refresh_from_db()
|
||||
self.assertEqual(self.orphan.city_site_id, response.data["id"])
|
||||
self.assertEqual(self.orphan.active_site_id, response.data["id"])
|
||||
listed = self.client.get("/api/v1/sites/")
|
||||
names = {row["site_name"] for row in listed.data["results"]}
|
||||
self.assertIn("Orphan Seed", names)
|
||||
|
||||
def test_director_sees_all_city_sites(self):
|
||||
def test_director_sees_all_active_sites(self):
|
||||
self.client.force_authenticate(self.director)
|
||||
response = self.client.get("/api/v1/city-sites/")
|
||||
response = self.client.get("/api/v1/active-sites/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
codes = {row["code"] for row in response.data["results"]}
|
||||
self.assertEqual(codes, {"a", "b"})
|
||||
Reference in new issue
Block a user