Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into siren-validator
Browse files Browse the repository at this point in the history
  • Loading branch information
ikarius authored Mar 13, 2024
2 parents bfcb0b8 + 3bf0521 commit 1c1b7c5
Show file tree
Hide file tree
Showing 19 changed files with 202 additions and 51 deletions.
1 change: 1 addition & 0 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
),
path("admin-division-search/", dora.admin_express.views.search),
path("admin-division-reverse-search/", dora.admin_express.views.reverse_search),
path("admin-division-departments/", dora.admin_express.views.get_departments),
path(
"city-label/<insee_code:insee_code>/", dora.admin_express.views.get_city_label
),
Expand Down
24 changes: 24 additions & 0 deletions dora/admin_express/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from operator import attrgetter

from django.contrib.gis.geos import Point
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import Value
Expand Down Expand Up @@ -115,3 +117,25 @@ def get_city_label(request, insee_code):
if city:
return Response(city.name)
raise NotFound


@api_view()
@permission_classes([permissions.AllowAny])
def get_departments(request):
class DeptSerializer(serializers.Serializer):
code = serializers.CharField()
name = serializers.CharField()
geom = GeometrySerializerMethodField()

def get_geom(self, obj):
return obj.geom.simplify(tolerance=0.1)

q = request.GET.get("dept_codes", "")
if q:
departments = [Department.objects.get_from_code(code) for code in q.split(",")]
else:
departments = Department.objects.all()

return Response(
DeptSerializer(sorted(departments, key=attrgetter("code")), many=True).data
)
8 changes: 4 additions & 4 deletions dora/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_structures_api_response(authenticated_user, api_client):

@pytest.mark.loaddata("structure_typology", "service_subcategory")
def test_structures_serialization_exemple(
setup_structure_data, authenticated_user, api_client
setup_structure_data, authenticated_user, api_client, settings
):
# Example adapté de la doc data·inclusion :
# https://www.data.inclusion.beta.gouv.fr/schemas-de-donnees-de-loffre/schema-des-structures-et-services-dinsertion
Expand Down Expand Up @@ -123,7 +123,7 @@ def test_structures_serialization_exemple(
"labels_autres": ["Nièvre médiation numérique"],
"labels_nationaux": ["MOBIN", "AFPA"],
"latitude": 48.7703,
"lien_source": f"http://localhost:3000/structures/{struct.slug}",
"lien_source": f"{settings.FRONTEND_URL}/structures/{struct.slug}",
"longitude": 7.848133,
"nom": "MOBILETTE",
"presentation_detail": None,
Expand Down Expand Up @@ -169,7 +169,7 @@ def test_unpublished_service_is_not_serialized(authenticated_user, api_client):
"service_coach_orientation_mode",
"service_beneficiary_access_mode",
)
def test_service_serialization_exemple(authenticated_user, api_client):
def test_service_serialization_exemple(authenticated_user, api_client, settings):
# Example adapté de la doc data·inclusion :
# https://www.data.inclusion.beta.gouv.fr/schemas-de-donnees-de-loffre/schema-des-structures-et-services-dinsertion
baker.make(Department, code="29", name="Finistère")
Expand Down Expand Up @@ -253,7 +253,7 @@ def test_service_serialization_exemple(authenticated_user, api_client):
"id": str(service.id),
"justificatifs": ["Carte d'identité, passeport ou permis de séjour"],
"latitude": 23.88654,
"lien_source": f"http://localhost:3000/services/{service.slug}",
"lien_source": f"{settings.FRONTEND_URL}/services/{service.slug}",
"longitude": 3.76855,
"modes_accueil": ["a-distance", "en-presentiel"],
"nom": "TISF",
Expand Down
2 changes: 1 addition & 1 deletion dora/rest_auth/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Meta:
model = User
fields = [
"bookmarks",
"department",
"departments",
"email",
"first_name",
"full_name",
Expand Down
22 changes: 11 additions & 11 deletions dora/rest_auth/tests/test_invite_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_establishment():

def test_manager_can_invite(api_client):
assert not Structure.objects.filter(siret=TEST_SIRET).exists()
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand All @@ -43,7 +43,7 @@ def test_manager_can_invite(api_client):

def test_can_invite_to_existing_structure(api_client):
make_structure(siret=TEST_SIRET)
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand Down Expand Up @@ -101,7 +101,7 @@ def test_superuser_can_invite(api_client):


def test_siret_is_mandatory(api_client):
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand All @@ -112,7 +112,7 @@ def test_siret_is_mandatory(api_client):


def test_invitee_email_is_mandatory(api_client):
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand All @@ -123,7 +123,7 @@ def test_invitee_email_is_mandatory(api_client):


def test_cant_invite_non_pe_agents_to_pe_structure(api_client):
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
siret_pe = SIREN_POLE_EMPLOI + "12345"
baker.make("Establishment", siret=siret_pe)
api_client.force_authenticate(user=user)
Expand All @@ -136,7 +136,7 @@ def test_cant_invite_non_pe_agents_to_pe_structure(api_client):


def test_can_invite_pe_agents_to_pe_structure(api_client):
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
siret_pe = SIREN_POLE_EMPLOI + "12345"
baker.make("Establishment", siret=siret_pe)
api_client.force_authenticate(user=user)
Expand All @@ -151,7 +151,7 @@ def test_can_invite_pe_agents_to_pe_structure(api_client):
def test_cant_invite_other_admins(api_client):
structure = make_structure(siret=TEST_SIRET)
make_user(structure=structure, is_staff=False, is_manager=False, is_admin=True)
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand All @@ -167,7 +167,7 @@ def test_cant_reinvite_already_existing_admin(api_client):
admin = make_user(
structure=structure, is_staff=False, is_manager=False, is_admin=True
)
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand All @@ -194,7 +194,7 @@ def test_promote_already_invited_user(api_client):
invited_by_admin=True,
is_admin=False,
)
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand All @@ -216,7 +216,7 @@ def test_promote_already_existing_member(api_client):
StructureMember.objects.get(structure=structure, user=existing_user).is_admin
is False
)
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand Down Expand Up @@ -245,7 +245,7 @@ def test_accept_and_promote_waiting_member(api_client):
user=existing_user,
invited_by_admin=False,
)
user = make_user(is_staff=False, is_manager=True, department=31)
user = make_user(is_staff=False, is_manager=True, departments=[31])
api_client.force_authenticate(user=user)
response = api_client.post(
"/auth/invite-first-admin/",
Expand Down
4 changes: 3 additions & 1 deletion dora/rest_auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def user_info(request):
user, token = TokenAuthentication().authenticate_credentials(key)
except exceptions.AuthenticationFailed:
raise Http404

update_last_login(user)

return Response(UserInfoSerializer(user, context={"token": token}).data, status=200)


Expand Down Expand Up @@ -196,7 +198,7 @@ def join_structure(request):
@transaction.atomic
def invite_first_admin(request):
inviter = request.user
if not (inviter.is_staff or (inviter.is_manager and inviter.department)):
if not (inviter.is_staff or (inviter.is_manager and inviter.departments)):
raise exceptions.PermissionDenied
siret = request.data.get("siret")
if not siret:
Expand Down
6 changes: 3 additions & 3 deletions dora/services/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def test_can_create_service_from_any_model(api_client):


def test_manager_can_create_model_from_service_in_its_dept(api_client):
user = baker.make("users.User", is_valid=True, is_manager=True, department="31")
user = baker.make("users.User", is_valid=True, is_manager=True, departments=["31"])
struct_src = make_structure(department="31")
struct_dest = make_structure(department="31")
service = make_service(status=ServiceStatus.PUBLISHED, structure=struct_src)
Expand All @@ -173,7 +173,7 @@ def test_manager_can_create_model_from_service_in_its_dept(api_client):


def test_manager_cant_create_model_from_service_outside_its_dept(api_client):
user = baker.make("users.User", is_valid=True, is_manager=True, department="31")
user = baker.make("users.User", is_valid=True, is_manager=True, departments=["31"])
struct_src = make_structure(department="44")
struct_dest = make_structure(department="31")
service = make_service(status=ServiceStatus.PUBLISHED, structure=struct_src)
Expand All @@ -187,7 +187,7 @@ def test_manager_cant_create_model_from_service_outside_its_dept(api_client):


def test_manager_cant_create_model_in_struct_outside_its_dept(api_client):
user = baker.make("users.User", is_valid=True, is_manager=True, department="31")
user = baker.make("users.User", is_valid=True, is_manager=True, departments=["31"])
struct_src = make_structure(department="31")
struct_dest = make_structure(department="44")
service = make_service(status=ServiceStatus.PUBLISHED, structure=struct_src)
Expand Down
2 changes: 1 addition & 1 deletion dora/services/tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def setUp(self):
)

self.manager = baker.make(
"users.User", is_manager=True, is_valid=True, department="31"
"users.User", is_manager=True, is_valid=True, departments=["31"]
)
self.struct_31 = make_structure(department="31")
self.service_31 = make_service(
Expand Down
8 changes: 4 additions & 4 deletions dora/services/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ def get_visible_services(user):
# Staff can see everything
elif user.is_staff:
qs = all_services
elif user.is_manager and user.department:
elif user.is_manager and user.departments:
qs = all_services.filter(
Q(status=ServiceStatus.PUBLISHED)
| Q(structure__department=user.department)
| Q(structure__department__in=user.departments)
| Q(structure__membership__user=user)
)
else:
Expand Down Expand Up @@ -628,9 +628,9 @@ def filter_custom_choices(choices):
)
filters |= Q(structure_id__in=user_structures)

if user.is_manager and user.department:
if user.is_manager and user.departments:
manager_structures = Structure.objects.filter(
department=user.department
department__in=user.departments
)
filters |= Q(structure__in=manager_structures)

Expand Down
4 changes: 2 additions & 2 deletions dora/structures/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ def is_manager(self, user: User):
return (
user.is_authenticated
and user.is_manager
and user.department
and user.department == self.department
and user.departments
and self.department in user.departments
)

@property
Expand Down
4 changes: 2 additions & 2 deletions dora/structures/tests/test_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def setUp(self):
self.me = baker.make("users.User", is_valid=True)
self.superuser = baker.make("users.User", is_staff=True, is_valid=True)
self.manager = baker.make(
"users.User", is_valid=True, is_manager=True, department="31"
"users.User", is_valid=True, is_manager=True, departments=["31"]
)
# Structure dont je suis administrateur
self.my_struct = make_structure()
Expand Down Expand Up @@ -346,7 +346,7 @@ def setUp(self):
self.unaccepted_admin = baker.make("users.User", is_valid=True)
self.superuser = baker.make("users.User", is_staff=True, is_valid=True)
self.manager = baker.make(
"users.User", is_manager=True, is_valid=True, department="31"
"users.User", is_manager=True, is_valid=True, departments=["31"]
)
self.other_struct31_user = baker.make(
"users.User",
Expand Down
12 changes: 6 additions & 6 deletions dora/structures/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ def get_queryset(self):
return Structure.objects.none()
if user.is_staff:
return all_structures.order_by("-modification_date").distinct()
elif user.is_manager and user.department:
elif user.is_manager and user.departments:
return (
all_structures.filter(department=user.department)
all_structures.filter(department__in=user.departments)
.order_by("-modification_date")
.distinct()
)
Expand Down Expand Up @@ -167,9 +167,9 @@ def get_queryset(self):

# Les gestionnaires ont accès à tous les collaborateurs de
# leur département
elif user.is_manager and user.department:
elif user.is_manager and user.departments:
return StructureMember.objects.filter(
structure__department=user.department
structure__department__in=user.departments
)

# Les membres des structures ont accès à tous leurs collègues
Expand Down Expand Up @@ -223,10 +223,10 @@ def get_queryset(self):

# Les gestionnaires ont accès à tous les collaborateurs de
# leur département
elif user.is_manager and user.department:
elif user.is_manager and user.departments:
return StructurePutativeMember.objects.filter(
Q(user__is_valid=True) | Q(invited_by_admin=True),
structure__department=user.department,
structure__department__in=user.departments,
)

# Les membres des structures ont accès à tous leurs collègues
Expand Down
50 changes: 49 additions & 1 deletion dora/support/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,15 @@ def setUp(self):
is_valid=True,
is_staff=False,
is_manager=True,
department=31,
departments=[31],
)

self.bimanager = baker.make(
"users.User",
is_valid=True,
is_staff=False,
is_manager=True,
departments=["31", "08"],
)

def test_coord_can_see_structures_in_his_dept(self):
Expand Down Expand Up @@ -154,3 +162,43 @@ def test_coord_without_dept_cant_see_specific_structure(self):
self.client.force_authenticate(user=manager)
response = self.client.get(f"/structures-admin/{structure.slug}/")
self.assertEqual(response.status_code, 403)

## Plusieurs départements
def test_bicoord_can_see_structures_in_his_depts(self):
structure1 = make_structure(department="31")
structure2 = make_structure(department="08")
self.client.force_authenticate(user=self.bimanager)
response = self.client.get("/structures-admin/")
self.assertEqual(response.status_code, 200)
self.assertTrue(
response.data[0]["slug"] == structure1.slug
or response.data[0]["slug"] == structure2.slug
)
self.assertTrue(
response.data[1]["slug"] == structure1.slug
or response.data[1]["slug"] == structure2.slug
)

def test_bicoord_cant_see_structures_outside_his_depts(self):
make_structure(department="12")
self.client.force_authenticate(user=self.bimanager)
response = self.client.get("/structures-admin/")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 0)

def test_bicoord_can_see_specific_structures_in_his_depts(self):
structure1 = make_structure(department="31")
structure2 = make_structure(department="08")
self.client.force_authenticate(user=self.bimanager)
response = self.client.get(f"/structures-admin/{structure1.slug}/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["slug"], structure1.slug)
response = self.client.get(f"/structures-admin/{structure2.slug}/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["slug"], structure2.slug)

def test_bicoord_cant_see_specific_structure_outside_his_depts(self):
structure = make_structure(department=12)
self.client.force_authenticate(user=self.bimanager)
response = self.client.get(f"/structures-admin/{structure.slug}/")
self.assertEqual(response.status_code, 404)
Loading

0 comments on commit 1c1b7c5

Please sign in to comment.