notification_state_repository.py (4201B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\store\client\targetaudience\notification_state_repository.py 2 from __future__ import absolute_import, division, print_function 3 import logging 4 from caching.client.storage.disk_storage import BinaryDiskStorage 5 from imagecaching.client.const import user_cache_folder 6 from locks import FifoLock, TempLock 7 from store.client.const import LOG_ENTRY_NAME 8 from store.client.targetaudience.const import TAO_NOTIFICATION_STATE_FOLDER, TAO_STORAGE_FOLDER 9 from store.client.targetaudience.feature_flags import tao_enabled 10 from store.client.targetaudience.notification_state import STATE_DISMISSED, STATE_SEEN, STATE_UNSEEN, states_from_bytes, states_to_bytes 11 logger = logging.getLogger(__name__) 12 _instance = None 13 14 def get_notification_state_repository(): 15 global _instance 16 if not tao_enabled(): 17 logger.info('%s - target audience offers are disabled, returning an empty catalogue', LOG_ENTRY_NAME) 18 return [] 19 if _instance is None: 20 _instance = OfferNotificationStateRepository() 21 return _instance 22 23 24 def clear_notification_state_repository(): 25 global _instance 26 _instance = None 27 28 29 def notification_state_content_id(user_id): 30 return u'%s/%s' % (TAO_STORAGE_FOLDER, user_cache_folder(user_id, TAO_NOTIFICATION_STATE_FOLDER)) 31 32 33 class OfferNotificationStateRepository(object): 34 35 def __init__(self, storage_factory = None): 36 self._storage_factory = storage_factory or BinaryDiskStorage 37 self._storage_by_user = {} 38 self._states_by_user = {} 39 40 def get_state(self, offer_id): 41 return self._states(offer_id.user_id).get(_offer_key(offer_id), STATE_UNSEEN) 42 43 def mark_seen(self, offer_id): 44 self._update(offer_id, _seen_after) 45 46 def mark_dismissed(self, offer_id): 47 self._update(offer_id, _dismissed_after) 48 49 def _update(self, offer_id, transition): 50 user_id = offer_id.user_id 51 offer_key = _offer_key(offer_id) 52 with TempLock(notification_state_content_id(user_id), FifoLock): 53 states = self._load(user_id) 54 self._states_by_user[user_id] = states 55 current_state = states.get(offer_key, STATE_UNSEEN) 56 new_state = transition(current_state) 57 if new_state == current_state: 58 return 59 states[offer_key] = new_state 60 self._save(user_id, states) 61 62 def _states(self, user_id): 63 states = self._states_by_user.get(user_id) 64 if states is not None: 65 return states 66 with TempLock(notification_state_content_id(user_id), FifoLock): 67 states = self._states_by_user.get(user_id) 68 if states is None: 69 states = self._load(user_id) 70 self._states_by_user[user_id] = states 71 return states 72 73 def _load(self, user_id): 74 try: 75 payload_bytes = self._storage(user_id).get() 76 except (OSError, IOError): 77 logger.exception('%s - could not read the target audience notification state of user %s', LOG_ENTRY_NAME, user_id) 78 return {} 79 80 try: 81 return states_from_bytes(payload_bytes) 82 except Exception: 83 logger.exception('%s - discarding the unreadable target audience notification state of user %s', LOG_ENTRY_NAME, user_id) 84 return {} 85 86 def _save(self, user_id, states): 87 try: 88 self._storage(user_id).set(states_to_bytes(states)) 89 except (OSError, IOError): 90 logger.exception('%s - could not persist the target audience notification state of user %s', LOG_ENTRY_NAME, user_id) 91 92 def _storage(self, user_id): 93 storage = self._storage_by_user.get(user_id) 94 if storage is None: 95 storage = self._storage_factory(notification_state_content_id(user_id)) 96 self._storage_by_user[user_id] = storage 97 return storage 98 99 100 def _offer_key(offer_id): 101 return u'%s' % offer_id.catalogue_entry_name 102 103 104 def _seen_after(current_state): 105 if current_state == STATE_DISMISSED: 106 return current_state 107 return STATE_SEEN 108 109 110 def _dismissed_after(_current_state): 111 return STATE_DISMISSED