def team_updated(self, team: Team, updated_by: User) -> None: """A method to be called after a team has been updated. Always call this after you update a team, unless you need to bypass the hook. Args: team: The team that was updated. updated_by: Who updated the team. """ AuditLogService.log(AuditLog.Action.UPDATE, updated_by, team)
def team_deleted(self, team: Team, deleted_by: User) -> None: """A method to be called after a team has been deleted. Always call this after you delete a team, unless you need to bypass the hook. Args: team: The team that was deleted. deleted_by: Who deleted the team. """ AuditLogService.log(AuditLog.Action.DELETE, deleted_by, team)
def team_created(self, team: Team, created_by: Optional[User]) -> None: """A method to be called after a team has been created. Always call this after you create a team, unless you need to bypass the hook. Args: team: The team that was created. created_by: Who created the team. """ AuditLogService.log(AuditLog.Action.CREATE, created_by, team)
def test_update_log_with_changes(self, db): person = Person.objects.get(user__email="*****@*****.**") person.first_name = "Joe" person.last_name = "Doe" person.save() log = AuditLogService.log(AuditLog.Action.UPDATE, person.user, person) assert log.content_object == person assert log.actor == person.user assert log.action == AuditLog.Action.UPDATE.value assert log.object_repr["first_name"] == "Joe" assert log.object_repr["last_name"] == "Doe" assert { "action": "change", "key": "first_name", "from_value": "John", "to_value": "Joe", } in log.diff assert { "action": "change", "key": "last_name", "from_value": "Smith", "to_value": "Doe", } in log.diff
def profile_updated(self, request: Optional[HttpRequest], person: Person, updated_by: User) -> None: """A method to be called after a profile has been updated. Please don't forget to call method this unless you need to bypass it. This is the main hook for calling out to other processes which need to happen after a profile has been updated. Only pass `None` for `request` when the action is made outside a web request, i.e, in a management command. Args: request: The HTTP request related to the action. person: The person behind the profile. updated_by: The user which updated the profile. """ AuditLogService().log(AuditLog.Action.UPDATE, updated_by, person) if request: person.edited_or_confirmed_at = timezone.now() person.save() self.notify_about_changes(request, person) # Notify external services person_update_notifier.delay(person.id)
def get_context_data(self, **kwargs: dict) -> dict: context = super().get_context_data(**kwargs) profile = context["profile"] roles = profile.roles.select_related("team").all() context["roles"] = roles context["title"] = profile.full_name if roles: # TODO: How do we know which team to select as the main one? team = roles[0].team context["team"] = team # TODO: `parent_teams` is common to all views. Perhaps we should # refactor this into a common base view or mixin? context["parent_teams"] = list(TeamService().get_all_parent_teams(team)) + [ team ] if self.request.user == profile.user or self.request.user.has_perm( "peoplefinder.view_auditlog" ): context["profile_audit_log"] = AuditLogService.get_audit_log(profile) context["profile_audit_log_excluded_keys"] = [ "user_id", "manager_id", "created_at", "updated_at", "edited_or_confirmed_at", ] return context
def test_create_log_with_changes(self, db): person = Person.objects.get(user__email="*****@*****.**") person.first_name = "Joe" person.last_name = "Doe" person.save() log = AuditLogService.log(AuditLog.Action.CREATE, person.user, person) assert log.object_repr["first_name"] == "Joe" # Even if there is a difference, a create log always has an empty diff. assert log.diff == []
def test_delete_log_with_changes(self, db): person = Person.objects.get(user__email="*****@*****.**") person.first_name = "Joe" person.last_name = "Doe" person.save() log = AuditLogService.log(AuditLog.Action.DELETE, person.user, person) # A delete log always has an empty object repr. assert log.object_repr == {} # Even if there is a difference, a delete log always has an empty diff. assert log.diff == []
def profile_created(self, person: Person, created_by: Optional[User]) -> None: """A method to be called after a profile has been created. Please don't forget to call method this unless you need to bypass it. This is the main hook for calling out to other processes which need to happen after a profile has been created. Args: person: The person behind the profile. created_by: The user which created the profile. """ AuditLogService().log(AuditLog.Action.CREATE, created_by, person)
def django_db_setup(django_db_setup, django_db_blocker): with django_db_blocker.unblock(): # John Smith - normal user user_john_smith, _ = User.objects.get_or_create( username="******", first_name="John", last_name="Smith", email="*****@*****.**", legacy_sso_user_id="john-smith-sso-user-id", is_staff=False, is_superuser=False, ) if hasattr(user_john_smith, "profile"): # We need to delete the profile's audit log separately because the primary # key is always the same when using a one-to-one relationship. AuditLogService.get_audit_log(user_john_smith.profile).delete() user_john_smith.profile.delete() call_command("loaddata", "countries.json") call_command("create_test_teams") call_command("create_user_profiles") call_command("create_people_finder_groups") user_john_smith.refresh_from_db() team_software = Team.objects.get(slug="software") user_john_smith.profile.roles.get_or_create( team=team_software, job_title="Software Engineer", ) PersonService().profile_updated(None, user_john_smith.profile, user_john_smith) # Leave this here to check we have reset the db into a known state. assert AuditLogService.get_audit_log(user_john_smith.profile).count() == 2
def profile_deleted(self, request: Optional[HttpRequest], person: Person, deleted_by: User) -> None: """ This is the main hook for calling out to other processes which need to happen after a profile has been deleted. Please don't forget to call this method unless you need to bypass it. Args: request: The HTTP request related to the action. person: The person behind the profile. deleted_by: The user which deleted the profile. """ person.is_active = False person.became_inactive = timezone.now() person.save() AuditLogService().log(AuditLog.Action.DELETE, deleted_by, person) if request: self.notify_about_deletion(person, deleted_by)
def get_context_data(self, **kwargs: dict) -> dict: context = super().get_context_data(**kwargs) team = context["team"] team_service = TeamService() context["parent_teams"] = team_service.get_all_parent_teams(team) context["sub_teams"] = team_service.get_immediate_child_teams(team) if self.request.user.has_perm("peoplefinder.delete_team"): ( context["can_team_be_deleted"], context["reasons_team_cannot_be_deleted"], ) = team_service.can_team_be_deleted(team) # Must be a leaf team. if not context["sub_teams"]: context["members"] = team.members.all().order_by( "person__first_name", "person__last_name") else: context[ "people_outside_subteams_count"] = TeamMember.objects.filter( team=team).count() # Warning: Multiple requests per sub-team. This might need optimising in the # future. for sub_team in context["sub_teams"]: sub_team.avg_profile_completion = ( Person.objects.with_profile_completion().filter(teams__in=[ sub_team, *team_service.get_all_child_teams(sub_team), ]).aggregate( Avg("profile_completion"))["profile_completion__avg"]) if self.request.user.has_perms( ["peoplefinder.change_team", "peoplefinder.view_auditlog"]): context["team_audit_log"] = AuditLogService.get_audit_log(team) return context