Skip to content
Snippets Groups Projects

Greg 95 cerebrum sponsor import

Merged Sivert Kronen Hatteberg requested to merge GREG-95-cerebrum-sponsor-import into master
All threads resolved!
Files
7
"""
Fetch sponsors for all units in GREG.
Uses the members of the adm-leder-<legacy_stedkode> groups to
populate Sponsors and SponsorOrganizationalUnits.
This script does only remove the SponsorOrganizationalUnit.
The Sponsor objects are kept, even with no units
"""
from typing import Optional, Tuple
import cerebrum_client
import structlog
from cerebrum_client import CerebrumClient
from django.conf import settings
from django.core.management.base import BaseCommand
from greg.models import OrganizationalUnit, Sponsor, SponsorOrganizationalUnit
logger = structlog.getLogger(__name__)
class Command(BaseCommand):
help = __doc__
CEREBRUM_SOURCE = "cerebrum"
CEREBRUM_FEIDE_INST = "uio.no"
CEREBRUM_NAME_SOURCE_PRIORITY = ["Cached", "Override", "DFO_SAP", "FS", "Manual"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.client = CerebrumClient(**settings.CEREBRUM_CLIENT)
def _has_active_dfo_aff(self, person_id: str):
"""Check that a person has a valid employee affiliation from DFØ."""
dfo_employee_affs = [
x
for x in self.client.get_person_affiliations(person_id)
if x.source_system == "DFO_SAP" and x.affiliation == "ANSATT"
]
return len(dfo_employee_affs) > 0
def _upsert_sponsor_unit_link(self, sponsor: Sponsor, unit: OrganizationalUnit):
"""Ensure a link between sponsor and unit."""
sunit, created = SponsorOrganizationalUnit.objects.get_or_create(
sponsor=sponsor,
organizational_unit=unit,
source=self.CEREBRUM_SOURCE,
automatic=True,
)
if created:
logger.info("sponsor_ou_link_create", sponsor=sponsor.id, sunit=sunit.id)
else:
logger.info("sponsor_ou_link_found", sponsor=sponsor.id, sunit=sunit.id)
sunit.hierarchical_access = settings.CEREBRUM_HIERARCHICAL_ACCESS
sunit.save()
return SponsorOrganizationalUnit.objects.get(id=sunit.id)
def _remove_sponsor_unit_link(self, sunit: SponsorOrganizationalUnit):
logger.info("sponsor_ou_deleted", sunit=sunit.id)
sunit.delete()
def _get_person_name(
self, person: cerebrum_client.models.Person
) -> Tuple[Optional[str], Optional[str]]:
"""Get a persons chosen name."""
first_names = {x.source_system: x for x in person.names if x.variant == "FIRST"}
last_names = {x.source_system: x for x in person.names if x.variant == "LAST"}
for source_system in self.CEREBRUM_NAME_SOURCE_PRIORITY:
if source_system in first_names and source_system in last_names:
return first_names[source_system].name, last_names[source_system].name
return None, None
def _get_feide_id(self, person_id: str) -> Optional[str]:
"""Infer the feide id from the primary user."""
primary_uname = self.client.get_person_primary_account_name(person_id)
if primary_uname:
return f"{primary_uname}@{self.CEREBRUM_FEIDE_INST}"
return None
def _upsert_sponsor_from_cerebrum(
self, person_id: str, unit: OrganizationalUnit
) -> Optional[Sponsor]:
"""Insert or update a sponsor from Cerebum data."""
person = self.client.get_person(person_id)
if not person:
logger.warning("cerebrum_person_missing", cerebrum_person_id=person_id)
return None
if not self._has_active_dfo_aff(person_id):
logger.warning("cerebrum_not_an_employee", cerebrum_person_id=person_id)
return None
feide_id = self._get_feide_id(person_id)
if not feide_id:
logger.warning("cerebrum_no_primary_account", cerebrum_person_id=person_id)
return None
first_name, last_name = self._get_person_name(person)
if not first_name or not last_name:
logger.warning("cerebrum_no_valid_name", cerebrum_person_id=person_id)
return None
try:
sponsor = Sponsor.objects.get(feide_id=feide_id)
sponsor.first_name = first_name
sponsor.last_name = last_name
sponsor.save()
logger.info(
"sponsor_updated", sponsor=sponsor.id, cerebrum_person_id=person_id
)
except Sponsor.DoesNotExist:
sponsor = Sponsor.objects.create(
first_name=first_name,
last_name=last_name,
feide_id=feide_id,
)
logger.info(
"sponsor_created", sponsor=sponsor.id, cerebrum_person_id=person_id
)
return Sponsor.objects.get(id=sponsor.id)
def handle(self, *args, **options):
"""Import of Sponsors from Cerebrum."""
active_units = OrganizationalUnit.objects.filter(
active=True,
deleted=False,
)
logger.info("import_start", nr_of_units=len(active_units))
for unit in active_units:
logger.bind(unit=unit.id)
sko = unit.identifiers.filter(name="legacy_stedkode").first()
if not sko:
logger.warning("orgreg_unit_missing_legacy_stedkode")
continue
logger.bind(legacy_stedkode=sko.value)
current_sponsors = unit.link_unit.filter(
automatic=True, source=self.CEREBRUM_SOURCE
).all()
group_name = f"adm-leder-{sko.value}"
group = self.client.get_group(group_name)
if not group:
# No group in cererbum, remove sponsors.
logger.info(
"cerebrum_group_not_found",
unit_id=unit.id,
cerebrum_group=group_name,
)
for sponsor in current_sponsors:
self._remove_sponsor_unit_link(sponsor)
continue
if group.expire_date:
# Group is expired, remove sponsors
logger.info(
"cerebrum_group_expired",
unit_id=unit.id,
cerebrum_group=group_name,
)
for sponsor in current_sponsors:
self._remove_sponsor_unit_link(sponsor)
continue
group_members = list(self.client.list_group_members(group_name))
if not group_members:
# No members in group, remove sponsors
logger.info(
"cerebrum_group_empty", unit_id=unit.id, cerebrum_group=group_name
)
for sponsor in current_sponsors:
self._remove_sponsor_unit_link(sponsor)
continue
cerebrum_sponsors = set()
for member in group_members:
if member.type == "person":
sponsor = self._upsert_sponsor_from_cerebrum(member.id, unit)
if sponsor:
sponsor_link = self._upsert_sponsor_unit_link(
sponsor=sponsor, unit=unit
)
cerebrum_sponsors.add(sponsor_link)
for sponsor in set(current_sponsors) - cerebrum_sponsors:
self._remove_sponsor_unit_link(sponsor)
logger.info("import_end")
Loading