NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

catalogue_svc.py (6239B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\store\client\targetaudience\catalogue_svc.py
      2 from __future__ import absolute_import, division, print_function
      3 import logging
      4 from collections import namedtuple
      5 import evetypes
      6 import uthread
      7 from caching.memoize import MemoizeWithLock
      8 from imagecaching.client.api.error import ImageCacheError
      9 from imagecaching.client.api.repository import get_repository
     10 from imagecaching.client.const import user_cache_folder
     11 from publicGateway.grpc.exceptions import GenericException
     12 from stackless_response_router.exceptions import TimeoutException
     13 from store.client.const import LOG_ENTRY_NAME
     14 from store.client.targetaudience.const import MAX_OFFER_LIST_PAGES, OFFER_CACHE_TTL_MINUTES, OFFER_LIST_PAGE_SIZE, TAO_IMAGE_CACHE_FOLDER
     15 from store.client.targetaudience.feature_flags import tao_enabled
     16 from store.client.targetaudience.messenger.request_messenger import PublicTargetAudienceOfferRequestMessenger
     17 logger = logging.getLogger(__name__)
     18 CatalogueOffer = namedtuple('CatalogueOffer', ['offer_id', 'offer', 'thumbnail_res_path'])
     19 _instance = None
     20 
     21 @MemoizeWithLock(OFFER_CACHE_TTL_MINUTES)
     22 def get_catalogue():
     23     return get_catalogue_svc().fetch_catalogue()
     24 
     25 
     26 def get_catalogue_svc():
     27     global _instance
     28     if not tao_enabled():
     29         logger.info('%s - target audience offers are disabled, returning no service', LOG_ENTRY_NAME)
     30         return
     31     if _instance is None:
     32         _instance = _CatalogueSvc(request_messenger=PublicTargetAudienceOfferRequestMessenger(sm.GetService('publicGatewaySvc')), image_repository=get_repository(), service_manager=sm)
     33     return _instance
     34 
     35 
     36 def clear_catalogue_svc():
     37     global _instance
     38     if _instance is not None:
     39         _instance.shutdown()
     40     _instance = None
     41     get_catalogue.clear_memoized()
     42 
     43 
     44 class _CatalogueSvc(object):
     45     __startupdependencies__ = ['publicGatewaySvc']
     46     __notifyevents__ = ['OnSessionChanged']
     47 
     48     def __init__(self, request_messenger, image_repository, service_manager):
     49         self._request_messenger = request_messenger
     50         self._image_repository = image_repository
     51         self._service_manager = service_manager
     52         self._service_manager.RegisterNotify(self)
     53 
     54     def shutdown(self):
     55         self._service_manager.UnregisterNotify(self)
     56 
     57     def OnSessionChanged(self, _is_remote, _session, change):
     58         if 'userid' in change or 'charid' in change:
     59             get_catalogue.clear_memoized()
     60 
     61     def fetch_catalogue(self):
     62         if not tao_enabled():
     63             logger.info('%s - target audience offers are disabled, returning an empty catalogue', LOG_ENTRY_NAME)
     64             return []
     65         cache_folder = _thumbnail_cache_folder()
     66         if cache_folder is None:
     67             logger.info('%s - no logged-in user, returning an empty target audience catalogue', LOG_ENTRY_NAME)
     68             return []
     69         offer_ids = self._list_offer_ids()
     70         offers = self._get_offers(offer_ids)
     71         catalogue = []
     72         for offer_id in offer_ids:
     73             offer = offers.get(offer_id)
     74             if offer is None:
     75                 continue
     76             if not self._has_known_type_ids(offer_id, offer):
     77                 continue
     78             catalogue.append(CatalogueOffer(offer_id=offer_id, offer=offer, thumbnail_res_path=self._cache_thumbnail(cache_folder, offer_id, offer)))
     79 
     80         self._prune_thumbnails(cache_folder, [ x.offer_id for x in catalogue ])
     81         logger.info('%s - target audience catalogue fetched: %s of %s offers usable', LOG_ENTRY_NAME, len(catalogue), len(offer_ids))
     82         return catalogue
     83 
     84     def _list_offer_ids(self):
     85         offer_ids = []
     86         page_token = None
     87         for _page in range(MAX_OFFER_LIST_PAGES):
     88             page_offer_ids, page_token = self._request_messenger.list_offers_request(page_size=OFFER_LIST_PAGE_SIZE, page_token=page_token)
     89             offer_ids.extend(page_offer_ids)
     90             if page_token is None:
     91                 break
     92         else:
     93             logger.error('%s - target audience offers pages exceed the maximum amount of %s pages', LOG_ENTRY_NAME, MAX_OFFER_LIST_PAGES)
     94 
     95         return offer_ids
     96 
     97     def _get_offers(self, offer_ids):
     98         offers = {}
     99         calls = [ (self._get_offer, (offer_id, offers)) for offer_id in offer_ids ]
    100         if calls:
    101             uthread.parallel(calls)
    102         return offers
    103 
    104     def _get_offer(self, offer_id, offers):
    105         try:
    106             offer = self._request_messenger.get_offer_request(offer_id)
    107         except (GenericException, TimeoutException):
    108             logger.exception('%s - could not fetch target audience offer %s', LOG_ENTRY_NAME, offer_id)
    109             return
    110 
    111         if offer is not None:
    112             offers[offer_id] = offer
    113 
    114     @staticmethod
    115     def _has_known_type_ids(offer_id, offer):
    116         for type_id in offer.get_type_ids():
    117             if not evetypes.Exists(type_id):
    118                 logger.warning('%s - dropping target audience offer %s, it grants unknown item type id %s', LOG_ENTRY_NAME, offer_id, type_id)
    119                 return False
    120 
    121         return True
    122 
    123     def _cache_thumbnail(self, cache_folder, offer_id, offer):
    124         if not offer.thumbnail_url:
    125             return None
    126         try:
    127             return self._image_repository.cache_image(_image_id(offer_id), offer.thumbnail_url, _thumbnail_cache_path(cache_folder, offer_id))
    128         except ImageCacheError:
    129             logger.exception('%s - could not cache the thumbnail of target audience offer %s', LOG_ENTRY_NAME, offer_id)
    130             return None
    131 
    132     def _prune_thumbnails(self, cache_folder, offer_ids):
    133         deleted_image_ids = self._image_repository.prune_folder(cache_folder, [ _image_id(x) for x in offer_ids ])
    134         if deleted_image_ids:
    135             logger.info('%s - pruned %s stale target audience thumbnails', LOG_ENTRY_NAME, len(deleted_image_ids))
    136 
    137 
    138 def _image_id(offer_id):
    139     return u'%s' % offer_id.catalogue_entry_name
    140 
    141 
    142 def _thumbnail_cache_folder():
    143     user_id = getattr(session, 'userid', None)
    144     if user_id is None:
    145         return
    146     return user_cache_folder(user_id, TAO_IMAGE_CACHE_FOLDER)
    147 
    148 
    149 def _thumbnail_cache_path(cache_folder, offer_id):
    150     return u'%s/%s' % (cache_folder, _image_id(offer_id))