People Finder
The DIT's people and team directory. Written as a Django application inside of Digital Workspace.
Design decisions
- Make extensive use of many-to-many fields for multiple choice reference data, e.g. country.
Soft Delete
When a profile is deleted by an admin, they are marked as inactive. If the user connected to the profile attempts to access the system, they will be redirected to a page explaining they have been deactivated. Inactive profiles will only be visible to admins and can only be reactivated by admins.
To support this feature in the codebase, the Person
model and the TeamMember
model
both have an active
manager which filters for active people. Both models retain
the unfiltered objects
manager as their default, so that the system can access
inactive profiles for admins to manage. To make things easier, both models' objects
manager have an active()
queryset method to filter for active profiles. This is useful
when you are performing queries across related managers which use the default objects
manager.
Here are some examples:
# All users.
Person.objects.all()
# Active users.
Person.active.all()
Person.objects.active()
# All team members.
TeamMember.objects.all()
# Active team members.
TeamMember.active.all()
TeamMember.objects.active()
# All team members through a related team.
team.members.all()
# Active team members through a related team.
team.members.active()
# https://docs.djangoproject.com/en/4.0/topics/db/queries/#using-a-custom-reverse-manager
team.members(manager="active").all()
Please take care to make sure any code you write uses the correct manager, or applies the appropriate filters for your use case.
Integrations
- GOV.UK Notify
- Notify users about changes made to their profile
- Zendesk (via email)
- Creates a ticket when a person is marked as having left the DIT
APIs
- Data Workspace
- API for people data
Audit log
Please remember to use the audit log service after you have made model instance changes that you wish to be tracked!
After evaluating a number of packages, we decided to write our own audit log system. The main reason for doing this is because no package would track changes to many-to-many fields without us having to make changes to how the app is written. Therefore, we decided it was best to keep the implementation of the app simple and write our own audit log system.
The audit log system we have written does not make use of django signals. This is because that approach has difficulties handling many-to-many fields. Instead, we have a simple and explicit approach where you must call the audit log service after you have made changes for them to be tracked.
For this approach to work you will need to provide a flat, serialized and denormalized
"view" of the model you wish to be tracked. This allows us to avoid the complexity of
tracking many-to-many fields by denormalizing them. This is often achieved using
ArrayAgg
which provides an array representation of the many-to-many relationship. For
an example of this in action, please take a look at the PersonAuditLogSerializer
class
in peoplefinder/services/person.py
.
One downside to this approach is that if you do not call the audit log service after making a change, for example modifying a model instance in a shell, then the changes will not be tracked.
For more information, please read through the docstrings and code in
peoplefinder/services/audit_log.py
and check out an example of how to use the service
from a view in the ProfileEditView
class in peoplefinder/views/profile.py
.