Add org supervisor model and services
This commit is contained in:
parent
d361dabd16
commit
5e84490703
|
|
@ -1,4 +1,4 @@
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin
|
||||||
from django.contrib.auth import admin as auth_admin
|
from django.contrib.auth import admin as auth_admin
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
@ -11,12 +11,6 @@ from vbv_lernwelt.core.models import (
|
||||||
SecurityRequestResponseLog,
|
SecurityRequestResponseLog,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.utils import pretty_print_json
|
from vbv_lernwelt.core.utils import pretty_print_json
|
||||||
from vbv_lernwelt.learning_mentor.services import (
|
|
||||||
create_or_sync_ausbildungsverantwortlicher as create_or_sync_av,
|
|
||||||
)
|
|
||||||
from vbv_lernwelt.learning_mentor.services import (
|
|
||||||
create_or_sync_berufsbildner as create_or_sync_bb,
|
|
||||||
)
|
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
@ -38,45 +32,6 @@ class LogAdmin(admin.ModelAdmin):
|
||||||
return pretty_print_json(json_string)
|
return pretty_print_json(json_string)
|
||||||
|
|
||||||
|
|
||||||
@admin.action(description="Berufsbildner: Create or Sync")
|
|
||||||
def create_or_sync_berufsbildner(modeladmin, request, queryset):
|
|
||||||
# keep it easy
|
|
||||||
success = []
|
|
||||||
for user in queryset:
|
|
||||||
success.append(create_or_sync_bb(user))
|
|
||||||
if all(success):
|
|
||||||
messages.add_message(
|
|
||||||
request,
|
|
||||||
messages.SUCCESS,
|
|
||||||
"Berufsbildner erfolgreich erstellt oder synchronisiert",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
messages.add_message(
|
|
||||||
request,
|
|
||||||
messages.ERROR,
|
|
||||||
"Einige Berufsbildner konnten nicht erstellt oder synchronisiert werden",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.action(description="Ausbildungsverantwortlicher: Create or Sync")
|
|
||||||
def create_or_sync_ausbildungsverantwortlicher(modeladmin, request, queryset):
|
|
||||||
success = []
|
|
||||||
for user in queryset:
|
|
||||||
success.append(create_or_sync_av(user))
|
|
||||||
if all(success):
|
|
||||||
messages.add_message(
|
|
||||||
request,
|
|
||||||
messages.SUCCESS,
|
|
||||||
"Ausbildungsverantwortlicher erfolgreich erstellt oder synchronisiert",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
messages.add_message(
|
|
||||||
request,
|
|
||||||
messages.ERROR,
|
|
||||||
"Einige Ausbildungsverantwortliche konnten nicht erstellt oder synchronisiert werden",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
class UserAdmin(auth_admin.UserAdmin):
|
class UserAdmin(auth_admin.UserAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
|
@ -139,7 +94,6 @@ class UserAdmin(auth_admin.UserAdmin):
|
||||||
]
|
]
|
||||||
list_filter = ("is_staff", "is_superuser", "is_active", "groups", "organisation")
|
list_filter = ("is_staff", "is_superuser", "is_active", "groups", "organisation")
|
||||||
search_fields = ["first_name", "last_name", "email", "username", "sso_id"]
|
search_fields = ["first_name", "last_name", "email", "username", "sso_id"]
|
||||||
actions = [create_or_sync_berufsbildner, create_or_sync_ausbildungsverantwortlicher]
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(JobLog)
|
@admin.register(JobLog)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,35 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin, messages
|
||||||
|
|
||||||
from vbv_lernwelt.learning_mentor.models import (
|
from vbv_lernwelt.learning_mentor.models import (
|
||||||
AgentParticipantRelation,
|
AgentParticipantRelation,
|
||||||
MentorInvitation,
|
MentorInvitation,
|
||||||
|
OrganisationSupervisor,
|
||||||
|
OrganisationSupervisortRoleType,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.learning_mentor.services import (
|
||||||
|
create_or_sync_ausbildungsverantwortlicher,
|
||||||
|
create_or_sync_berufsbildner,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.action(description="Organisation Supervisor: Sync")
|
||||||
|
def create_or_sync_org_supervisor(_modeladmin, request, queryset):
|
||||||
|
success = []
|
||||||
|
for supervisor in queryset:
|
||||||
|
sync_fn = create_or_sync_berufsbildner if supervisor.role == OrganisationSupervisortRoleType.BERUFSBILDNER.value else create_or_sync_ausbildungsverantwortlicher
|
||||||
|
success.append(sync_fn(supervisor.supervisor, supervisor.organisation))
|
||||||
|
if all(success):
|
||||||
|
messages.add_message(
|
||||||
|
request,
|
||||||
|
messages.SUCCESS,
|
||||||
|
"Organisation Supervisor synchronisiert",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
messages.add_message(
|
||||||
|
request,
|
||||||
|
messages.ERROR,
|
||||||
|
"Einige Organisation Supervisors konnten nicht synchronisiert werden",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(AgentParticipantRelation)
|
@admin.register(AgentParticipantRelation)
|
||||||
|
|
@ -33,3 +59,19 @@ class MentorInvitationAdmin(admin.ModelAdmin):
|
||||||
list_display = ["id", "email", "participant", "created"]
|
list_display = ["id", "email", "participant", "created"]
|
||||||
readonly_fields = ["id", "created", "email", "participant"]
|
readonly_fields = ["id", "created", "email", "participant"]
|
||||||
search_fields = ["email"]
|
search_fields = ["email"]
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(OrganisationSupervisor)
|
||||||
|
class OrganisationSupervisorAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["supervisor", "organisation", "role"]
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
"supervisor"
|
||||||
|
]
|
||||||
|
|
||||||
|
raw_id_fields = [
|
||||||
|
"supervisor",
|
||||||
|
]
|
||||||
|
|
||||||
|
list_filter = ["role", "organisation"]
|
||||||
|
actions = [create_or_sync_org_supervisor]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Generated by Django 4.2.13 on 2024-11-20 06:22
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("core", "0012_auto_20240621_1626"),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("learning_mentor", "0010_alter_agentparticipantrelation_role"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="OrganisationSupervisor",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"role",
|
||||||
|
models.CharField(
|
||||||
|
choices=[
|
||||||
|
(
|
||||||
|
"AUSBILDUNGSVERANTWORTLICHER",
|
||||||
|
"AUSBILDUNGSVERANTWORTLICHER",
|
||||||
|
),
|
||||||
|
("BERUFSBILDNER", "BERUFSBILDNER"),
|
||||||
|
],
|
||||||
|
default="AUSBILDUNGSVERANTWORTLICHER",
|
||||||
|
max_length=255,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"organisation",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="core.organisation",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"supervisor",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -4,7 +4,7 @@ from enum import Enum
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django_extensions.db.models import TimeStampedModel
|
from django_extensions.db.models import TimeStampedModel
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import Organisation, User
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,3 +65,45 @@ class MentorInvitation(TimeStampedModel):
|
||||||
verbose_name = "Lernbegleiter Einladung"
|
verbose_name = "Lernbegleiter Einladung"
|
||||||
verbose_name_plural = "Lernbegleiter Einladungen"
|
verbose_name_plural = "Lernbegleiter Einladungen"
|
||||||
unique_together = [["email", "participant"]]
|
unique_together = [["email", "participant"]]
|
||||||
|
|
||||||
|
|
||||||
|
class OrganisationSupervisortRoleType(Enum):
|
||||||
|
AUSBILDUNGSVERANTWORTLICHER = "AUSBILDUNGSVERANTWORTLICHER"
|
||||||
|
BERUFSBILDNER = "BERUFSBILDNER"
|
||||||
|
|
||||||
|
|
||||||
|
class OrganisationSupervisor(models.Model):
|
||||||
|
supervisor = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
role = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
choices=[(t.value, t.value) for t in OrganisationSupervisortRoleType],
|
||||||
|
default=OrganisationSupervisortRoleType.AUSBILDUNGSVERANTWORTLICHER.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.role == OrganisationSupervisortRoleType.AUSBILDUNGSVERANTWORTLICHER.value:
|
||||||
|
from vbv_lernwelt.core.admin import (
|
||||||
|
create_or_sync_ausbildungsverantwortlicher,
|
||||||
|
)
|
||||||
|
create_or_sync_ausbildungsverantwortlicher(self.supervisor, self.organisation)
|
||||||
|
else:
|
||||||
|
from vbv_lernwelt.learning_mentor.services import (
|
||||||
|
create_or_sync_berufsbildner,
|
||||||
|
)
|
||||||
|
create_or_sync_berufsbildner(self.supervisor, self.organisation)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
if self.role == OrganisationSupervisortRoleType.AUSBILDUNGSVERANTWORTLICHER.value:
|
||||||
|
from vbv_lernwelt.core.admin import (
|
||||||
|
delete_ausbildungsverantwortlicher_relation,
|
||||||
|
)
|
||||||
|
delete_ausbildungsverantwortlicher_relation(self.supervisor, self.organisation)
|
||||||
|
else:
|
||||||
|
from vbv_lernwelt.learning_mentor.services import (
|
||||||
|
delete_berufsbildner_relation,
|
||||||
|
)
|
||||||
|
delete_berufsbildner_relation(self.supervisor, self.organisation)
|
||||||
|
super().delete(*args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import Organisation, User
|
||||||
from vbv_lernwelt.course.consts import (
|
from vbv_lernwelt.course.consts import (
|
||||||
COURSE_UK,
|
COURSE_UK,
|
||||||
COURSE_UK_FR,
|
COURSE_UK_FR,
|
||||||
|
|
@ -18,50 +18,58 @@ logger = structlog.get_logger(__name__)
|
||||||
UK_COURSES = [COURSE_UK, COURSE_UK_FR, COURSE_UK_IT]
|
UK_COURSES = [COURSE_UK, COURSE_UK_FR, COURSE_UK_IT]
|
||||||
|
|
||||||
|
|
||||||
def create_or_sync_berufsbildner(berufsbildner: User) -> bool:
|
def uk_cs_users_by_org(org: Organisation) -> set[CourseSessionUser]:
|
||||||
new_members = set(
|
return set(
|
||||||
CourseSessionUser.objects.filter(user__organisation=berufsbildner.organisation)
|
CourseSessionUser.objects.filter(user__organisation=org)
|
||||||
.filter(course_session__course__configuration__is_uk=True)
|
.filter(course_session__course__configuration__is_uk=True)
|
||||||
.filter(role=CourseSessionUser.Role.MEMBER.value)
|
.filter(role=CourseSessionUser.Role.MEMBER.value)
|
||||||
.filter(course_session__course_id__in=UK_COURSES)
|
.filter(course_session__course_id__in=UK_COURSES)
|
||||||
.exclude(course_session_id__in=[4, 5, 6])
|
.exclude(course_session_id__in=[4, 5, 6])
|
||||||
)
|
)
|
||||||
return create_or_sync_learning_mentor(berufsbildner, new_members)
|
|
||||||
|
|
||||||
|
|
||||||
def create_or_sync_ausbildungsverantwortlicher(
|
def vv_cs_users_by_org(org: Organisation) -> set[CourseSessionUser]:
|
||||||
ausbildungsverantwortlicher: User,
|
return set(
|
||||||
) -> bool:
|
CourseSessionUser.objects.filter(user__organisation=org)
|
||||||
new_members = set(
|
|
||||||
CourseSessionUser.objects.filter(
|
|
||||||
user__organisation=ausbildungsverantwortlicher.organisation
|
|
||||||
)
|
|
||||||
.filter(course_session__course__configuration__is_uk=False)
|
.filter(course_session__course__configuration__is_uk=False)
|
||||||
.filter(role=CourseSessionUser.Role.MEMBER.value)
|
.filter(role=CourseSessionUser.Role.MEMBER.value)
|
||||||
.filter(course_session__course_id__in=VV_COURSE_IDS)
|
.filter(course_session__course_id__in=VV_COURSE_IDS)
|
||||||
)
|
)
|
||||||
return create_or_sync_learning_mentor(ausbildungsverantwortlicher, new_members)
|
|
||||||
|
|
||||||
|
def create_or_sync_berufsbildner(berufsbildner: User, organisation: Organisation) -> bool:
|
||||||
|
org_members = uk_cs_users_by_org(organisation)
|
||||||
|
return create_or_sync_learning_mentor(berufsbildner, org_members)
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_sync_ausbildungsverantwortlicher(
|
||||||
|
ausbildungsverantwortlicher: User,
|
||||||
|
organisation: Organisation
|
||||||
|
) -> bool:
|
||||||
|
org_members = vv_cs_users_by_org(organisation)
|
||||||
|
return create_or_sync_learning_mentor(ausbildungsverantwortlicher, org_members)
|
||||||
|
|
||||||
|
|
||||||
def create_or_sync_learning_mentor(
|
def create_or_sync_learning_mentor(
|
||||||
agent: User, new_members: set[CourseSessionUser]
|
agent: User, org_members: set[CourseSessionUser]
|
||||||
) -> bool:
|
) -> bool:
|
||||||
logger.info(
|
logger.info(
|
||||||
"Creating or syncing berufsbildner",
|
"Creating or syncing berufsbildner/ausbildungsverantwortlicher",
|
||||||
berufsbildner=agent,
|
berufsbildner=agent,
|
||||||
org=agent.organisation.name_de,
|
org=agent.organisation.name_de,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check if it is a valid organisation
|
# Check if it is a valid organisation
|
||||||
if agent.organisation and agent.organisation.organisation_id < 4:
|
if agent.organisation and agent.organisation.organisation_id < 4:
|
||||||
logger.error("Invalid organisation", org=agent.organisation)
|
logger.error("Invalid organisation", org=agent.organisation)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# get existing connections
|
# Get existing connections (full relation objects)
|
||||||
existing_members = set(agent.agentparticipantrelation_set.all())
|
existing_relations = set(agent.agentparticipantrelation_set.all())
|
||||||
|
|
||||||
# add new relations that are not in existing relations
|
# Add new relations that are not in existing relations
|
||||||
for csu in new_members:
|
existing_members = {relation.participant for relation in existing_relations}
|
||||||
|
for csu in org_members:
|
||||||
if csu not in existing_members:
|
if csu not in existing_members:
|
||||||
AgentParticipantRelation.objects.get_or_create(
|
AgentParticipantRelation.objects.get_or_create(
|
||||||
agent=agent,
|
agent=agent,
|
||||||
|
|
@ -69,9 +77,38 @@ def create_or_sync_learning_mentor(
|
||||||
role=AgentParticipantRoleType.BERUFSBILDNER.value,
|
role=AgentParticipantRoleType.BERUFSBILDNER.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
# remove old relations that are not in the new relations
|
# Remove old relations that are not in the new relations
|
||||||
for relation in existing_members:
|
for relation in existing_relations:
|
||||||
if relation.participant not in new_members:
|
if relation.participant not in org_members:
|
||||||
relation.delete()
|
relation.delete()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def delete_berufsbildner_relation(berufsbildner: User, organisation: Organisation) -> bool:
|
||||||
|
org_members = uk_cs_users_by_org(organisation)
|
||||||
|
delete_org_supervisor_relation(berufsbildner, org_members)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_ausbildungsverantwortlicher_relation(ausbildungsverantwortlicher: User, organisation: Organisation) -> bool:
|
||||||
|
org_members = vv_cs_users_by_org(organisation)
|
||||||
|
delete_org_supervisor_relation(ausbildungsverantwortlicher, org_members)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_org_supervisor_relation(
|
||||||
|
agent: User,
|
||||||
|
org_members: set[CourseSessionUser],
|
||||||
|
):
|
||||||
|
# As the key berufsbildner is used in several courses, we use org_members to select the ones from the correct
|
||||||
|
# course sessions
|
||||||
|
relations_to_delete = agent.agentparticipantrelation_set.filter(participant__in=org_members)
|
||||||
|
|
||||||
|
# Bulk delete the identified relations
|
||||||
|
deleted_count, _ = relations_to_delete.delete()
|
||||||
|
|
||||||
|
# Log the result
|
||||||
|
logger.info(
|
||||||
|
"Deleted ausbildungsverantwortlicher relations",
|
||||||
|
agent=agent,
|
||||||
|
deleted_count=deleted_count,
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue