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

job_provider.py (13334B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\jobboard\client\job_provider.py
      2 import abc
      3 import uthread2
      4 import logging
      5 import gametime
      6 from signals import Signal
      7 from metadata.common.content_tags import get_content_tag_as_object
      8 from jobboard.client import get_job_board_service, job_board_signals
      9 from jobboard.client.provider_type import get_job_id
     10 from jobboard.client.job_board_settings import get_display_feature_setting
     11 from jobboard.client.ui.no_job_container import NoJobContainer
     12 logger = logging.getLogger('job_board')
     13 
     14 class JobProvider(object):
     15     __metaclass__ = abc.ABCMeta
     16     PROVIDER_ID = 'job'
     17     PROVIDER_CONTENT_TAGS = []
     18     FEATURE_PAGE_CLASS = None
     19     NO_JOB_CONTAINER = NoJobContainer
     20     __notifyevents__ = []
     21 
     22     @classmethod
     23     def get_job_id(cls, instance_id):
     24         return get_job_id(cls.PROVIDER_ID, instance_id)
     25 
     26     def __init__(self, job_storage):
     27         self._job_storage = job_storage
     28         self._jobs = {}
     29         self._is_dirty = True
     30         self._is_registered = False
     31         self._fetching = False
     32         self.on_job_added = Signal('{}.on_job_added'.format(self.PROVIDER_ID))
     33         self.on_job_removed = Signal('{}.on_job_removed'.format(self.PROVIDER_ID))
     34         self._enabled = self._should_enable()
     35         self._register_availability()
     36         if self.PROVIDER_CONTENT_TAGS:
     37             self._display_feature_setting = get_display_feature_setting(self.PROVIDER_CONTENT_TAGS[0])
     38             self._display_feature_setting.on_change.connect(self._display_feature_setting_changed)
     39         else:
     40             self._display_feature_setting = None
     41         if self._enabled:
     42             self._register_events()
     43         self._refresh_on_init()
     44 
     45     @property
     46     def is_enabled(self):
     47         return self._enabled
     48 
     49     @property
     50     def is_dirty(self):
     51         return self._is_dirty
     52 
     53     @property
     54     def service(self):
     55         return get_job_board_service()
     56 
     57     @property
     58     def feature_tag(self):
     59         feature_tag_id = self.feature_tag_id
     60         if feature_tag_id:
     61             return get_content_tag_as_object(feature_tag_id)
     62 
     63     @property
     64     def feature_tag_id(self):
     65         if self.PROVIDER_CONTENT_TAGS:
     66             return self.PROVIDER_CONTENT_TAGS[0]
     67 
     68     @property
     69     def is_hidden(self):
     70         if not self.is_enabled:
     71             return True
     72         if self._display_feature_setting:
     73             return not self._display_feature_setting.get()
     74         return False
     75 
     76     @abc.abstractmethod
     77     def _get_instance_id(self, content):
     78         pass
     79 
     80     @abc.abstractmethod
     81     def _get_all_content(self):
     82         pass
     83 
     84     @abc.abstractmethod
     85     def _construct_job(self, job_id, content):
     86         pass
     87 
     88     def close(self):
     89         self._unregister_availability()
     90         self._unregister_events()
     91         if self._display_feature_setting:
     92             self._display_feature_setting.on_change.disconnect(self._display_feature_setting_changed)
     93         self.clear()
     94 
     95     def clear(self):
     96         for job in self._jobs.values():
     97             job.clear()
     98 
     99         self._jobs.clear()
    100         self._is_dirty = True
    101         self._fetching = False
    102         job_board_signals.on_job_provider_cleared(self.PROVIDER_ID)
    103 
    104     def get_jobs(self):
    105         self.wait_for_provider()
    106         return self._jobs
    107 
    108     def get_tracked_jobs(self):
    109         result = []
    110         tracked_job_ids = self.get_tracked_job_ids()
    111         jobs = self.get_jobs()
    112         for tracked_job_id in tracked_job_ids:
    113             job = jobs.get(tracked_job_id, None)
    114             if job:
    115                 result.append(job)
    116 
    117         return result
    118 
    119     def wait_for_provider_by_tags(self, content_tag_ids):
    120         if any((content_tag_id in self.PROVIDER_CONTENT_TAGS for content_tag_id in content_tag_ids)):
    121             self.wait_for_provider()
    122 
    123     def wait_for_provider(self, refresh_jobs = True):
    124         if not self._enabled:
    125             return
    126         if self._is_dirty and refresh_jobs:
    127             self.refresh_jobs()
    128         while self._fetching:
    129             uthread2.Yield()
    130 
    131     def has_active_job_failures(self):
    132         return False
    133 
    134     def has_unclaimed_job_failures(self):
    135         return False
    136 
    137     def try_fetch_job(self, job_id):
    138         self.wait_for_provider()
    139         return self.get_job(job_id)
    140 
    141     def get_job(self, job_id):
    142         return self._jobs.get(job_id, None)
    143 
    144     def set_dirty(self):
    145         self._is_dirty = True
    146 
    147     def get_tracked_job_ids(self):
    148         tracked_jobs = settings.char.ui.Get(self._tracked_setting_id, {})
    149         if isinstance(tracked_jobs, list):
    150             return {job_id:None for job_id in tracked_jobs}
    151         return tracked_jobs
    152 
    153     def toggle_tracked_job_by_player(self, job):
    154         if job.job_id not in self.get_tracked_job_ids():
    155             self.add_tracked_job(job, set_expanded=True)
    156             self.service.notify_of_job_tracked(job)
    157         else:
    158             self.remove_tracked_job(job)
    159             self.service.notify_of_job_untracked(job)
    160 
    161     def add_tracked_job(self, job, set_expanded = True):
    162         job_id = job.job_id
    163         if set_expanded:
    164             sm.GetService('infoPanel').SetExpandedMission('opportunities', job_id)
    165         tracked_job_ids = self.get_tracked_job_ids()
    166         tracked_job_ids[job_id] = gametime.GetWallclockTime()
    167         self._set_tracked_job_ids(tracked_job_ids)
    168         job.on_tracked()
    169 
    170     def remove_tracked_job(self, job):
    171         expanded_mission = sm.GetService('infoPanel').GetExpandedMission()
    172         if expanded_mission and expanded_mission['missionID'] == job.job_id:
    173             sm.GetService('infoPanel').SetExpandedMission('opportunities', -1)
    174         tracked_job_ids = self.get_tracked_job_ids()
    175         tracked_job_ids.pop(job.job_id, None)
    176         self._set_tracked_job_ids(tracked_job_ids)
    177         job.on_untracked()
    178 
    179     def get_tracked_job_timestamp(self, job_id):
    180         tracked_job_ids = self.get_tracked_job_ids()
    181         return tracked_job_ids.get(job_id, None)
    182 
    183     def set_tracked_job_timestamp(self, job_id, timestamp):
    184         tracked_job_ids = self.get_tracked_job_ids()
    185         tracked_job_ids[job_id] = timestamp
    186         settings.char.ui.Set(self._tracked_setting_id, tracked_job_ids)
    187 
    188     def _has_tracked_jobs(self):
    189         return bool(self.get_tracked_job_ids())
    190 
    191     def _update_provider_state(self, enabled):
    192         if self._enabled == enabled:
    193             return
    194         self._enabled = enabled
    195         if enabled:
    196             self._register_events()
    197             self._invalidate_cache()
    198         else:
    199             self._unregister_events()
    200             self._remove_all_jobs()
    201         job_board_signals.on_job_provider_state_changed(self)
    202 
    203     @uthread2.debounce(0.2)
    204     def _update_jobs(self, jobs = None, update_state = False, **kwargs):
    205         for job in jobs or self._jobs.values():
    206             job.update(**kwargs)
    207             if update_state:
    208                 job_board_signals.on_job_state_changed(job)
    209 
    210     def refresh_jobs(self):
    211         if not self._enabled or self._fetching:
    212             return
    213         if not session.charid:
    214             logger.warning('No character logged in when refreshing provider %s', self.PROVIDER_ID)
    215             return
    216         self._fetching = True
    217         uthread2.start_tasklet(self.__refresh_jobs)
    218 
    219     def __refresh_jobs(self):
    220         logger.info('Refreshing provider %s', self.PROVIDER_ID)
    221         job_board_signals.on_job_provider_started_refreshing(self.PROVIDER_ID)
    222         self._fetching = True
    223         valid_job_ids = set()
    224         try:
    225             all_content = self._get_all_content()
    226         except Exception as e:
    227             logger.exception('Failed to fetch jobs for provider %s - e=%s', self.PROVIDER_ID, e)
    228             self._fetching = False
    229             return
    230 
    231         try:
    232             for content in all_content or []:
    233                 job = self._create_job(content)
    234                 if job:
    235                     valid_job_ids.add(job.job_id)
    236 
    237             for job_id in self._jobs.keys():
    238                 if job_id not in valid_job_ids:
    239                     self._remove_job(job_id)
    240 
    241         finally:
    242             self._is_dirty = False
    243             self._fetching = False
    244             self._on_jobs_refreshed()
    245             job_board_signals.on_job_provider_finished_refreshing(self.PROVIDER_ID)
    246 
    247     def _on_jobs_refreshed(self):
    248         self._cleanup_tracked_jobs()
    249 
    250     def _create_job(self, content):
    251         if not self._enabled or not content:
    252             return
    253         job_id = self.get_job_id(self._get_instance_id(content))
    254         if job_id in self._jobs:
    255             job = self._jobs[job_id]
    256             job.update(content)
    257         else:
    258             try:
    259                 job = self._construct_job(job_id, content)
    260             except Exception as e:
    261                 logger.exception('Failed to construct job e=%s', e)
    262                 job = None
    263 
    264             if job:
    265                 self._add_job(job)
    266         return job
    267 
    268     def _add_job(self, job):
    269         self._jobs[job.job_id] = job
    270         self._job_storage.add_job(job)
    271         self.on_job_added(job)
    272 
    273     def _remove_job(self, job_id):
    274         job = self._jobs.pop(job_id, None)
    275         if job:
    276             job.on_remove()
    277             if job_id in self.get_tracked_job_ids():
    278                 self.remove_tracked_job(job)
    279         self._job_storage.remove_job(job_id)
    280         if job:
    281             self.on_job_removed(job)
    282 
    283     def _remove_all_jobs(self):
    284         for job_id in self._jobs.keys():
    285             self._remove_job(job_id)
    286 
    287     def _register_events(self):
    288         if self._is_registered:
    289             return
    290         self._register_slots()
    291         self._is_registered = True
    292 
    293     def _unregister_events(self):
    294         if not self._is_registered:
    295             return
    296         self._unregister_slots()
    297         self._is_registered = False
    298 
    299     def _register_availability(self):
    300         pass
    301 
    302     def _unregister_availability(self):
    303         pass
    304 
    305     def _register_slots(self):
    306         job_board_signals.on_job_viewed.connect(self._on_job_viewed)
    307         job_board_signals.on_job_window_initializing.connect(self._on_window_initializing)
    308         job_board_signals.on_job_window_maximized.connect(self._on_window_maximized)
    309         job_board_signals.on_job_window_minimized.connect(self._on_window_minimized)
    310         job_board_signals.on_job_window_closed.connect(self._on_window_closed)
    311         sm.RegisterNotify(self)
    312 
    313     def _unregister_slots(self):
    314         job_board_signals.on_job_viewed.disconnect(self._on_job_viewed)
    315         job_board_signals.on_job_window_initializing.disconnect(self._on_window_initializing)
    316         job_board_signals.on_job_window_maximized.disconnect(self._on_window_maximized)
    317         job_board_signals.on_job_window_minimized.disconnect(self._on_window_minimized)
    318         job_board_signals.on_job_window_closed.disconnect(self._on_window_closed)
    319         sm.UnregisterNotify(self)
    320 
    321     def _should_enable(self):
    322         return True
    323 
    324     def _display_feature_setting_changed(self, *args, **kwargs):
    325         job_board_signals.on_job_provider_state_changed(self)
    326         self._update_jobs(update_state=True)
    327         if self.is_dirty:
    328             self._invalidate_cache()
    329 
    330     def _on_job_viewed(self, job_id):
    331         if self._is_dirty and job_id in self._jobs:
    332             self.refresh_jobs()
    333 
    334     def _invalidate_cache(self):
    335         if self._should_refresh_on_invalidate():
    336             self.refresh_jobs()
    337         else:
    338             self.set_dirty()
    339 
    340     def _should_refresh_on_invalidate(self):
    341         if self._has_tracked_jobs():
    342             return True
    343         if self.is_hidden:
    344             return False
    345         return self.service.is_open
    346 
    347     def _refresh_on_init(self):
    348         if self._should_refresh_on_init():
    349             self.refresh_jobs()
    350             return True
    351         return False
    352 
    353     def _should_refresh_on_init(self):
    354         return self._has_tracked_jobs()
    355 
    356     def _on_window_initializing(self):
    357         if self._should_refresh_on_window_initializing():
    358             self.refresh_jobs()
    359 
    360     def _should_refresh_on_window_initializing(self):
    361         return self.is_dirty and not self.is_hidden
    362 
    363     def _on_window_maximized(self):
    364         if self._should_refresh_on_window_maximized():
    365             self.refresh_jobs()
    366 
    367     def _should_refresh_on_window_maximized(self):
    368         return self.is_dirty and not self.is_hidden
    369 
    370     def _on_window_minimized(self):
    371         pass
    372 
    373     def _on_window_closed(self):
    374         pass
    375 
    376     def _cleanup_tracked_jobs(self):
    377         tracked_job_ids = self.get_tracked_job_ids()
    378         if not tracked_job_ids:
    379             return
    380         requires_update = False
    381         for job_id in tracked_job_ids.keys():
    382             if job_id in self._jobs and self._jobs[job_id].is_trackable:
    383                 continue
    384             if not job_id.startswith(self.PROVIDER_ID):
    385                 continue
    386             tracked_job_ids.pop(job_id, None)
    387             requires_update = True
    388 
    389         if requires_update:
    390             self._set_tracked_job_ids(tracked_job_ids)
    391         else:
    392             sm.GetService('infoPanel').UpdateJobBoardPanel()
    393 
    394     def _set_tracked_job_ids(self, tracked_job_ids):
    395         settings.char.ui.Set(self._tracked_setting_id, tracked_job_ids)
    396         job_board_signals.on_tracked_jobs_changed()
    397 
    398     @property
    399     def _tracked_setting_id(self):
    400         return 'tracked_opportunities_{}'.format(self.PROVIDER_ID)