talentmap_api/user_profile/models.py
import json
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import models
from django.contrib.auth.models import User
from jsonfield import JSONField
from talentmap_api.common.models import StaticRepresentationModel
from talentmap_api.common.common_helpers import get_filtered_queryset, resolve_path_to_view, format_filter, get_avatar_url
from talentmap_api.common.permissions import in_group_or_403
from talentmap_api.messaging.models import Notification
class UserProfile(StaticRepresentationModel):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
emp_id = models.CharField(max_length=255, null=False, help_text="The user's employee id")
def __str__(self):
return f"{self.user.first_name} {self.user.last_name}"
@property
def avatar(self):
return get_avatar_url(self.user.email)
@property
def display_name(self):
'''
Returns the user's display name, derived from first name, username or e-mail
'''
display_name = ""
if self.user.first_name:
display_name = self.user.first_name
elif self.user.username:
display_name = self.user.username
else:
display_name = self.user.email
return display_name
@property
def initials(self):
'''
Returns the user's initials, derived from first name/last name or e-mail
'''
initials = ""
if self.user.first_name and self.user.last_name:
initials = f"{self.user.first_name[0]}{self.user.last_name[0]}"
if len(initials) == 0:
# No first name/last name on user object, derive from email
# Example email: StateJB@state.gov
# [x for x in self.user.email if x.isupper()] - get all capitals
# [:2] - get the first two
# [::-1] - reverse the list
initials = "".join([x for x in self.user.email if x.isupper()][:2][::-1])
return initials
@property
def is_cdo(self):
'''
Represents if the user is a CDO (Career development officer) or not.
'''
try:
in_group_or_403(self.user, 'cdo')
return True
except BaseException:
return False
class Meta:
managed = True
ordering = ['user__last_name']
class SavedSearch(models.Model):
'''
Represents a saved search.
'''
owner = models.ForeignKey(UserProfile, on_delete=models.DO_NOTHING, related_name="saved_searches")
name = models.CharField(max_length=255, null=False, default="Saved Search", help_text="The name of the saved search")
endpoint = models.TextField(help_text="The endpoint for this search and filter")
'''
Filters should be a JSON object of filters representing a search. Generally, the values
should be stored in a list.
For example, suppose our user preferred posts with post danger pay >= 20 and with grade = 05
{
"post__danger_pay__gte": ["20"],
"grade__code": ["05"]
}
'''
filters = JSONField(default={}, help_text="JSON object containing filters representing the saved search")
count = models.IntegerField(default=0, help_text="Current count of search results for this search")
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
is_bureau = models.BooleanField(default=False, help_text="Whether this search is for Bureau/AO")
def get_queryset(self):
return get_filtered_queryset(resolve_path_to_view(self.endpoint).filter_class, self.filters)
def update_count(self, created=False, jwt_token=''):
filter_class = resolve_path_to_view(self.endpoint).filter_class
query_params = format_filter(self.filters)
if getattr(filter_class, "use_api", False):
count = int(filter_class.get_count(query_params, jwt_token).get('count', 0))
else:
count = self.get_queryset().count()
if self.count != count:
# Create a notification for this saved search's owner if the amount has increased
diff = count - self.count
if diff > 0 and not created:
Notification.objects.create(
owner=self.owner,
tags=['saved_search'],
message=f"Saved search {self.name} has {diff} new results available",
meta=json.dumps({"count": diff, "search": {"filters": self.filters, "endpoint": self.endpoint}})
)
self.count = count
# Do not trigger signals for this save
self._disable_signals = True
self.save()
self._disable_signals = False
@staticmethod
def update_counts_for_endpoint(endpoint=None, contains=False, jwt_token='', user=''):
'''
Update all saved searches counts whose endpoint matches the specified endpoint.
If the endpoint is omitted, updates all saved search counts.
Args:
- endpoint (string) - Endpoint to updated saved searches for
'''
queryset = SavedSearch.objects.all()
if endpoint:
if contains:
queryset = SavedSearch.objects.filter(endpoint__icontains=endpoint)
else:
queryset = SavedSearch.objects.filter(endpoint=endpoint)
for search in queryset:
if search.owner == user or user == '':
search.update_count(jwt_token=jwt_token)
class Meta:
managed = True
ordering = ["date_created"]
# Signal listeners
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
'''
This listener creates a user profile for every created user.
'''
if created:
UserProfile.objects.create(user=instance)