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

goalsController.py (26654B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\corporation\client\goals\goalsController.py
      2 import corporation.client.goals.goalMessengerSignals as messenger_signals
      3 import gametime
      4 import logging
      5 import threadutils
      6 import uthread2
      7 from assetholding.client import assetholdingSignals
      8 from assetholding.client.assetholdingController import AssetHoldingController
      9 from carbonui import uiconst
     10 from collections import defaultdict
     11 from corporation.client.goals import goalSignals
     12 from corporation.client.goals.errors import AtGoalCapacity, BadRequestToReserveAsset, WalletAccessForbidden, InsufficientFunds, InvalidExpirationTime
     13 from corporation.client.goals.featureFlag import are_corporation_goals_enabled
     14 from corporation.client.goals.goal import CorporationGoal
     15 from corporation.client.goals.goalMessenger import GoalMessenger
     16 from corporation.client.goals.goalsSettings import qa_allow_short_corp_projects
     17 from corporation.client.goals.goalsUtil import get_timespan_by_option
     18 from eve.common.script.sys.idCheckers import IsNPCCorporation
     19 from goals.common.goalConst import ContributionMethodTypes, GoalState
     20 from localization import GetByLabel
     21 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog
     22 logger = logging.getLogger('corporation_goals')
     23 
     24 class CorpGoalsController(object):
     25     __notifyevents__ = ['OnCharacterSelected', 'OnSessionChanged']
     26     _instance = None
     27 
     28     def __init__(self):
     29         self._capacity = None
     30         self._messenger = None
     31         self._goals = {}
     32         self._fetching_data_for_goal_ids = set()
     33         self._active_goal_ids = set()
     34         self._is_active_dirty = True
     35         self._fetching_active = False
     36         self._unclaimed_goal_ids = set()
     37         self._is_unclaimed_dirty = True
     38         self._fetching_unclaimed = False
     39         self._has_tried_fetching_any_unclaimed_ids = False
     40         self._inactive_goal_ids = defaultdict(set)
     41         self._is_inactive_dirty = defaultdict(lambda : True)
     42         self._fetching_inactive = defaultdict(lambda : False)
     43         sm.RegisterNotify(self)
     44         self._connect_to_messenger_signals()
     45         AssetHoldingController.get_instance()
     46 
     47     def __del__(self):
     48         sm.UnregisterNotify(self)
     49         self._disconnect_from_messenger_signals()
     50 
     51     def _connect_to_messenger_signals(self):
     52         messenger_signals.on_goal_created_internal.connect(self._on_goal_created)
     53         messenger_signals.on_goal_deleted_internal.connect(self._on_goal_deleted)
     54         messenger_signals.on_goal_closed_internal.connect(self._on_goal_closed)
     55         messenger_signals.on_goal_completed_internal.connect(self._on_goal_completed)
     56         messenger_signals.on_goal_contribution_internal.connect(self._on_goal_contribution)
     57         messenger_signals.on_goal_progress_set_internal.connect(self._on_goal_progress_set)
     58         messenger_signals.on_goal_redeemed_internal.connect(self._on_goal_redeemed)
     59         messenger_signals.on_goal_name_changed_internal.connect(self._on_goal_name_changed)
     60         messenger_signals.on_goal_description_changed_internal.connect(self._on_goal_description_changed)
     61         messenger_signals.on_goal_expired_internal.connect(self._on_goal_expired)
     62         assetholdingSignals.on_entitlement_redeemed.connect(self._on_goal_redeemed)
     63 
     64     def _disconnect_from_messenger_signals(self):
     65         messenger_signals.on_goal_created_internal.disconnect(self._on_goal_created)
     66         messenger_signals.on_goal_deleted_internal.disconnect(self._on_goal_deleted)
     67         messenger_signals.on_goal_closed_internal.disconnect(self._on_goal_closed)
     68         messenger_signals.on_goal_completed_internal.disconnect(self._on_goal_completed)
     69         messenger_signals.on_goal_contribution_internal.disconnect(self._on_goal_contribution)
     70         messenger_signals.on_goal_progress_set_internal.disconnect(self._on_goal_progress_set)
     71         messenger_signals.on_goal_redeemed_internal.disconnect(self._on_goal_redeemed)
     72         messenger_signals.on_goal_name_changed_internal.disconnect(self._on_goal_name_changed)
     73         messenger_signals.on_goal_description_changed_internal.disconnect(self._on_goal_description_changed)
     74         messenger_signals.on_goal_expired_internal.disconnect(self._on_goal_expired)
     75         assetholdingSignals.on_entitlement_redeemed.disconnect(self._on_goal_redeemed)
     76 
     77     @classmethod
     78     def get_instance(cls):
     79         if not cls._instance:
     80             cls._instance = CorpGoalsController()
     81         return cls._instance
     82 
     83     @property
     84     def messenger(self):
     85         if self._messenger is None:
     86             self._messenger = GoalMessenger.get_instance(sm.GetService('publicGatewaySvc'))
     87         return self._messenger
     88 
     89     def redeem_reward(self, goal_id):
     90         try:
     91             self.messenger.redeem_reward(goal_id)
     92         except Exception as e:
     93             logger.warning('Failed to redeem goal with id %s - %s', goal_id, str(e))
     94             goalSignals.on_goal_redeem_failed(goal_id)
     95 
     96     def get_active_goals(self, fetch_contribution = True, wait = True):
     97         self.fetch_active_goals(fetch_contribution=fetch_contribution)
     98         if wait:
     99             self.wait_for_active()
    100         return self.get_cached_active_goals()
    101 
    102     def get_cached_active_goals(self):
    103         return [ self._goals[goal_id] for goal_id in self._active_goal_ids if self._goals[goal_id].has_data() ]
    104 
    105     def fetch_active_goals(self, fetch_contribution = True):
    106         if not self._should_fetch_goals():
    107             return
    108         if self._fetching_active:
    109             return
    110         if self._is_active_dirty:
    111             self._fetching_active = True
    112             uthread2.start_tasklet(self._fetch_active_goal_ids)
    113         uthread2.start_tasklet(self._fetch_missing_active_goals_data, fetch_contribution)
    114 
    115     def wait_for_active(self):
    116         while self._fetching_active:
    117             uthread2.Yield()
    118 
    119     def get_unclaimed_goals(self, wait = True):
    120         self.fetch_unclaimed_goals()
    121         if wait:
    122             self.wait_for_unclaimed()
    123         return self.get_cached_unclaimed_goals()
    124 
    125     def get_cached_unclaimed_goals(self):
    126         return [ self._goals[goal_id] for goal_id in self._unclaimed_goal_ids if self._goals[goal_id].has_data() ]
    127 
    128     def fetch_unclaimed_goals(self):
    129         if not self._should_fetch_goals():
    130             return
    131         if self._fetching_unclaimed:
    132             return
    133         if self._is_unclaimed_dirty:
    134             self._fetching_unclaimed = True
    135             uthread2.start_tasklet(self._fetch_unclaimed_goal_ids)
    136         uthread2.start_tasklet(self._fetch_missing_unclaimed_goals_data)
    137 
    138     def wait_for_unclaimed(self):
    139         while self._fetching_unclaimed:
    140             uthread2.Yield()
    141 
    142     def get_inactive_goals(self, months_since = None, fetch_contribution = True, wait = True):
    143         self.fetch_inactive_goals(months_since, fetch_contribution)
    144         if wait:
    145             self.wait_for_inactive(months_since)
    146         return self.get_cached_inactive_goals(months_since)
    147 
    148     def get_cached_inactive_goals(self, months_since = None):
    149         return [ self._goals[goal_id] for goal_id in self._inactive_goal_ids[months_since] if self._goals[goal_id].has_data() ]
    150 
    151     def fetch_inactive_goals(self, months_since = None, fetch_contribution = True):
    152         if not self._should_fetch_goals():
    153             return
    154         if self._fetching_inactive[months_since]:
    155             return
    156         if self._is_inactive_dirty[months_since]:
    157             self._fetching_inactive[months_since] = True
    158             uthread2.start_tasklet(self._fetch_inactive_goal_ids, months_since)
    159         uthread2.start_tasklet(self._fetch_missing_inactive_goals_data, months_since, fetch_contribution)
    160 
    161     def wait_for_inactive(self, months_since = None):
    162         while self._fetching_inactive[months_since]:
    163             uthread2.Yield()
    164 
    165     def is_inactive_goal(self, goal_id, months_since = None):
    166         return goal_id in self._inactive_goal_ids[months_since] and self._goals[goal_id].has_data()
    167 
    168     def get_goal(self, goal_id):
    169         goal = self._goals.get(goal_id, None)
    170         if goal and goal.has_data():
    171             return goal
    172         if not self._should_fetch_goals():
    173             return
    174         if not goal:
    175             logger.info('Creating goal from get_goal %s', goal_id)
    176             goal = CorporationGoal(goal_id)
    177             self._goals[goal_id] = goal
    178         self._fetch_missing_goal_data(goal, wait_while_fetching=True)
    179         if not goal.has_data():
    180             return
    181         return goal
    182 
    183     def has_unclaimed_rewards(self):
    184         if not bool(self._unclaimed_goal_ids) and not self._has_tried_fetching_any_unclaimed_ids and self._is_unclaimed_dirty and not self._fetching_unclaimed and are_corporation_goals_enabled():
    185             self._has_tried_fetching_any_unclaimed_ids = True
    186             uthread2.start_tasklet(self._fetch_unclaimed_page, None)
    187         return bool(self._unclaimed_goal_ids)
    188 
    189     def _should_fetch_goals(self):
    190         return session.corpid and not IsNPCCorporation(session.corpid) and are_corporation_goals_enabled()
    191 
    192     def _fetch_active_goal_ids(self):
    193         self._fetching_active = True
    194         next_token = None
    195         has_exception = False
    196         while next_token != '':
    197             try:
    198                 next_token = self._fetch_active_page(token=next_token)
    199             except Exception as e:
    200                 logger.warning('Unable to fetch active goal ids with token %s - %s', next_token, str(e))
    201                 has_exception = True
    202                 break
    203 
    204         self._fetching_active = False
    205         self._is_active_dirty = has_exception
    206 
    207     def _fetch_active_page(self, token):
    208         if token is None:
    209             token = ''
    210         next_token, goal_ids = self.messenger.get_active_goal_ids(token=token)
    211         for goal_id in goal_ids:
    212             if goal_id not in self._goals:
    213                 self._goals[goal_id] = CorporationGoal(goal_id)
    214             self._active_goal_ids.add(goal_id)
    215 
    216         return next_token
    217 
    218     def _fetch_missing_active_goals_data(self, fetch_contribution = True):
    219         self.wait_for_active()
    220         for goal_id in self._active_goal_ids:
    221             self._fetch_missing_goal_data(self._goals.get(goal_id, None), fetch_contribution)
    222 
    223     def _fetch_unclaimed_goal_ids(self):
    224         self._fetching_unclaimed = True
    225         next_token = None
    226         has_exception = False
    227         while next_token != '':
    228             try:
    229                 next_token = self._fetch_unclaimed_page(token=next_token)
    230             except Exception as e:
    231                 logger.warning('Unable to fetch unclaimed goal ids with token %s - %s', next_token, str(e))
    232                 has_exception = True
    233                 break
    234 
    235         self._fetching_unclaimed = False
    236         self._is_unclaimed_dirty = has_exception
    237 
    238     def _fetch_unclaimed_page(self, token):
    239         if token is None:
    240             token = ''
    241         next_token, goal_ids = self.messenger.get_unclaimed_goal_ids(token=token)
    242         self._has_tried_fetching_any_unclaimed_ids = True
    243         for goal_id in goal_ids:
    244             if goal_id not in self._goals:
    245                 self._goals[goal_id] = CorporationGoal(goal_id)
    246             self._unclaimed_goal_ids.add(goal_id)
    247 
    248         if bool(goal_ids):
    249             goalSignals.on_unclaimed_goal_ids_changed()
    250         return next_token
    251 
    252     def _fetch_missing_unclaimed_goals_data(self):
    253         self.wait_for_unclaimed()
    254         for goal_id in self._unclaimed_goal_ids:
    255             self._fetch_missing_goal_data(self._goals.get(goal_id, None))
    256 
    257     def _fetch_inactive_goal_ids(self, months_since):
    258         self._fetching_inactive[months_since] = True
    259         next_token = None
    260         has_exception = False
    261         while next_token != '':
    262             try:
    263                 next_token = self._fetch_inactive_page(months_since, token=next_token)
    264             except Exception as e:
    265                 logger.warning('Unable to fetch inactive goal ids with token %s - %s', next_token, str(e))
    266                 has_exception = True
    267                 break
    268 
    269         self._fetching_inactive[months_since] = False
    270         self._is_inactive_dirty[months_since] = has_exception
    271 
    272     def _fetch_inactive_page(self, months_since, token):
    273         if token is None:
    274             token = ''
    275         start_time, duration = get_timespan_by_option(months_since)
    276         next_token, goal_ids = self.messenger.get_inactive_goal_ids(start_time, duration, token=token)
    277         for goal_id in goal_ids:
    278             if goal_id not in self._goals:
    279                 self._goals[goal_id] = CorporationGoal(goal_id)
    280             self._inactive_goal_ids[months_since].add(goal_id)
    281 
    282         return next_token
    283 
    284     def _fetch_missing_inactive_goals_data(self, months_since, fetch_contribution = True):
    285         self.wait_for_inactive(months_since)
    286         for goal_id in self._inactive_goal_ids[months_since]:
    287             self._fetch_missing_goal_data(self._goals.get(goal_id, None), fetch_contribution)
    288 
    289     def _fetch_missing_goal_data(self, goal, fetch_contribution = True, wait_while_fetching = False):
    290         if not goal:
    291             return
    292         if goal.has_data() and (not fetch_contribution or goal.has_fetched_contribution()):
    293             return
    294         goal_id = goal.goal_id
    295         if goal_id not in self._fetching_data_for_goal_ids:
    296             self._fetching_data_for_goal_ids.add(goal_id)
    297             self.__fetch_missing_goal_data(goal, fetch_contribution)
    298         if wait_while_fetching:
    299             while goal_id in self._fetching_data_for_goal_ids:
    300                 uthread2.Yield()
    301 
    302     @threadutils.threaded
    303     def __fetch_missing_goal_data(self, goal, fetch_contribution):
    304         goal_id = goal.get_id()
    305         updated = False
    306         if not goal.has_data():
    307             try:
    308                 data = self.messenger.get_goal(goal_id)
    309             except Exception as e:
    310                 logger.warning('Unable to fetch goal %s - %s', goal_id, str(e))
    311                 data = None
    312 
    313             if data:
    314                 goal.set_data(data)
    315                 updated = True
    316             else:
    317                 fetch_contribution = False
    318         if fetch_contribution and not goal.has_fetched_contribution():
    319             if goal.get_current_progress() == 0:
    320                 summary = {'progress': 0,
    321                  'rewards_unclaimed': 0}
    322             else:
    323                 try:
    324                     summary = self.messenger.get_my_contributor_summary(goal_id)
    325                 except Exception as e:
    326                     logger.warning('Unable to fetch character contribution summary for goal %s - %s', goal_id, str(e))
    327                     summary = None
    328 
    329             if summary:
    330                 goal.set_contribution(summary['progress'], summary['rewards_unclaimed'])
    331                 if goal_id not in self._unclaimed_goal_ids and goal.has_unclaimed_reward():
    332                     self._unclaimed_goal_ids.add(goal_id)
    333                     goalSignals.on_unclaimed_goal_ids_changed()
    334                 updated = True
    335         self._fetching_data_for_goal_ids.discard(goal_id)
    336         if updated:
    337             goalSignals.on_goal_data_fetched(goal)
    338 
    339     def _is_fetching_data_for_goal(self, goal):
    340         return goal.goal_id in self._fetching_data_for_goal_ids
    341 
    342     def _is_fetching_any_active_goal_data(self):
    343         return not self._active_goal_ids.isdisjoint(self._fetching_data_for_goal_ids)
    344 
    345     def _is_fetching_any_unclaimed_goal_data(self):
    346         return not self._unclaimed_goal_ids.isdisjoint(self._fetching_data_for_goal_ids)
    347 
    348     def _is_fetching_any_inactive_goal_data(self, months_since = None):
    349         return not self._inactive_goal_ids[months_since].isdisjoint(self._fetching_data_for_goal_ids)
    350 
    351     def flush_cache(self):
    352         self._capacity = None
    353         self._goals.clear()
    354         self._active_goal_ids.clear()
    355         self._is_active_dirty = True
    356         self._unclaimed_goal_ids.clear()
    357         self._is_unclaimed_dirty = True
    358         self._has_tried_fetching_any_unclaimed_ids = False
    359         self._inactive_goal_ids.clear()
    360         self._is_inactive_dirty.clear()
    361         goalSignals.on_cache_invalidated()
    362         logger.info('GoalsController::flush_cache')
    363 
    364     def get_all_goal_contributor_summaries(self, goal_id):
    365         goal = self._goals.get(goal_id, None)
    366         if not goal:
    367             return []
    368         cached = goal.all_contributor_summaries
    369         if cached is not None and gametime.GetSecondsSinceWallclockTime(goal.all_contributor_summaries_fetch_time) < 60:
    370             return cached
    371         goal.all_contributor_summaries_fetch_time = gametime.GetWallclockTime()
    372         result = []
    373         next_token = None
    374         has_exception = False
    375         while next_token != '':
    376             try:
    377                 if next_token is None:
    378                     next_token = ''
    379                 next_token, summaries = self.messenger.get_all_contributor_summaries_for_goal(goal_id=goal_id, token=next_token)
    380                 result.extend(summaries)
    381             except Exception as e:
    382                 logger.warning('Unable to fetch contributor summaries for goal %s with token %s - %s', goal_id, next_token, str(e))
    383                 has_exception = True
    384                 break
    385 
    386         if not has_exception:
    387             goal.all_contributor_summaries = result
    388         return result
    389 
    390     def _has_funds(self, amount):
    391         wallet = sm.GetService('wallet')
    392         wealth = wallet.GetCorpWealth(accountKey=session.corpAccountKey)
    393         return wealth >= amount
    394 
    395     def create_goal(self, name, description, desired_progress, method_type, contribution_fields = None, career_path = None, payment_per_contribution = None, end_time = None, participation_limit = None, coverage_limit = None, multiplier = None):
    396         if payment_per_contribution and not self._has_funds(payment_per_contribution * desired_progress):
    397             message_player(GetByLabel('UI/Corporations/Goals/InsufficientFunds'))
    398             raise InsufficientFunds('Funds in the selected wallet division do not suffice to create project')
    399         try:
    400             self.messenger.create_goal(name=name, description=description, desired_progress=desired_progress, method_id=method_type, contribution_fields=contribution_fields or {}, career_path=career_path, payment=payment_per_contribution, expiration=end_time, validate_end_date=not qa_allow_short_corp_projects.get(), participation_limit=participation_limit, coverage_limit=coverage_limit, multiplier=multiplier)
    401         except AtGoalCapacity:
    402             max_active = self.get_active_capacity()
    403             message_player(GetByLabel('UI/Corporations/Goals/AtGoalsCapacity', numMax=max_active))
    404             raise
    405         except BadRequestToReserveAsset:
    406             message_player(GetByLabel('UI/Corporations/Goals/InsufficientFunds'))
    407             raise
    408         except WalletAccessForbidden:
    409             message_player(GetByLabel('UI/Corporations/Wallet/WalletDivisionAccessDenied'))
    410             raise
    411         except InvalidExpirationTime:
    412             message_player(GetByLabel('UI/Corporations/Goals/InvalidDuration'))
    413             raise
    414         except Exception:
    415             message_player(GetByLabel('UI/Corporations/Goals/FailedToConnect'))
    416             self.flush_cache()
    417             raise
    418 
    419     def delete_goal(self, goal_id, show_prompt = True):
    420         try:
    421             if show_prompt and prompt_player_fsd_dialog('DeleteCorpProject', buttons=uiconst.YESNO) != uiconst.ID_YES:
    422                 return
    423             self.messenger.delete_goal(goal_id)
    424         except Exception:
    425             message_player(GetByLabel('UI/Corporations/Goals/FailedToConnect'))
    426             self.flush_cache()
    427             raise
    428 
    429     def complete_goal(self, goal_id):
    430         try:
    431             if prompt_player_fsd_dialog('CompleteCorpProject', buttons=uiconst.YESNO) != uiconst.ID_YES:
    432                 return
    433             goal = self.get_goal(goal_id)
    434             current_progress = goal.get_current_progress()
    435             desired_progress = goal.get_desired_progress()
    436             updated_progress = desired_progress
    437             self._set_goal_progress(goal_id, current_progress, updated_progress, desired_progress)
    438         except Exception:
    439             message_player(GetByLabel('UI/Corporations/Goals/FailedToConnect'))
    440             self.flush_cache()
    441             raise
    442 
    443     def close_goal(self, goal_id):
    444         try:
    445             if prompt_player_fsd_dialog('CloseCorpProject', buttons=uiconst.YESNO) != uiconst.ID_YES:
    446                 return
    447             self.messenger.close_goal(goal_id)
    448         except Exception:
    449             message_player(GetByLabel('UI/Corporations/Goals/FailedToConnect'))
    450             self.flush_cache()
    451             raise
    452 
    453     def set_current_progress(self, goal_id, updated_progress):
    454         goal = self._goals.get(goal_id, None)
    455         if not goal:
    456             return
    457         current_progress = goal.get_current_progress()
    458         desired_progress = goal.get_desired_progress()
    459         self._set_goal_progress(goal_id, current_progress, updated_progress, desired_progress)
    460 
    461     def get_items_pending_delivery(self, office_id):
    462         if not are_corporation_goals_enabled():
    463             return {}
    464         try:
    465             goals = self.get_active_goals(wait=False, fetch_contribution=False)
    466         except (NotImplementedError, RuntimeError):
    467             return {}
    468 
    469         quantity_by_type = defaultdict(int)
    470         for goal in goals:
    471             if goal.has_reached_participation_limit():
    472                 continue
    473             contribution_method = goal.get_contribution_method()
    474             if contribution_method.method_id == ContributionMethodTypes.DELIVER_ITEM and contribution_method.office in (None, office_id):
    475                 quantity_by_type[contribution_method.item_type] += goal.get_my_remaining_progress()
    476 
    477         return quantity_by_type
    478 
    479     def get_active_capacity(self):
    480         if self._capacity is None:
    481             try:
    482                 self._capacity = self.messenger.get_capacity()
    483             except Exception:
    484                 return 0
    485 
    486         return self._capacity
    487 
    488     def qa_delete_all_goals(self):
    489         for goal in self.get_cached_active_goals():
    490             if not goal.get_current_progress():
    491                 self.delete_goal(goal.get_id(), show_prompt=False)
    492 
    493     def _set_goal_progress(self, goal_id, current_progress, updated_progress, desired_progress):
    494         updated_progress = min(updated_progress, desired_progress)
    495         try:
    496             self.messenger.set_manual_goal_progress(goal_id=goal_id, current_progress=current_progress, updated_progress=updated_progress)
    497         except Exception:
    498             message_player(GetByLabel('UI/Corporations/Goals/FailedToConnect'))
    499             self.flush_cache()
    500             raise
    501 
    502     def OnSessionChanged(self, _isRemote, _session, change):
    503         if 'corpid' in change:
    504             self.flush_cache()
    505 
    506     def OnCharacterSelected(self, *args, **kwargs):
    507         self.flush_cache()
    508 
    509     def _on_goal_created(self, goal_id, goal_data):
    510         goal = CorporationGoal(goal_id)
    511         goal.set_data(goal_data)
    512         self._goals[goal_id] = goal
    513         self._active_goal_ids.add(goal.goal_id)
    514         goalSignals.on_created(goal_id)
    515 
    516     def _on_goal_deleted(self, goal_id):
    517         goal = self._goals.pop(goal_id, None)
    518         self._active_goal_ids.discard(goal_id)
    519         for months_since in self._inactive_goal_ids:
    520             self._inactive_goal_ids[months_since].discard(goal_id)
    521 
    522         if goal:
    523             goalSignals.on_deleted(goal_id)
    524 
    525     def _on_goal_closed(self, goal_id):
    526         goal = self._goals.get(goal_id, None)
    527         self._active_goal_ids.discard(goal_id)
    528         self._inactive_goal_ids[None].add(goal_id)
    529         if goal:
    530             goal.close()
    531             goalSignals.on_closed(goal_id)
    532             goalSignals.on_state_changed(goal_id, state=GoalState.CLOSED)
    533 
    534     def _on_goal_completed(self, goal_id):
    535         goal = self._goals.get(goal_id, None)
    536         self._active_goal_ids.discard(goal_id)
    537         self._inactive_goal_ids[None].add(goal_id)
    538         if goal:
    539             desired_progress = goal.get_desired_progress()
    540             goal.set_current_progress(desired_progress)
    541             goal.complete()
    542             goalSignals.on_completed(goal_id)
    543             goalSignals.on_state_changed(goal_id, state=GoalState.COMPLETED)
    544 
    545     def _on_goal_expired(self, goal_id):
    546         goal = self._goals.get(goal_id, None)
    547         self._active_goal_ids.discard(goal_id)
    548         self._inactive_goal_ids[None].add(goal_id)
    549         if goal:
    550             goal.expire()
    551             goalSignals.on_expired(goal_id)
    552             goalSignals.on_state_changed(goal_id, state=GoalState.EXPIRED)
    553 
    554     def _on_goal_progress_set(self, goal_id, progress):
    555         logger.info('GoalsController::_on_goal_progress_set: goal_id=%s, progress=%s', goal_id, progress)
    556         goal = self._goals.get(goal_id, None)
    557         if not goal:
    558             logger.info('Goal not found, not setting progress: goal_id=%s, progress=%s', goal_id, progress)
    559             return
    560         goal.set_current_progress(progress)
    561         goalSignals.on_progress_changed(goal_id, progress, state_changed=False)
    562 
    563     def _on_goal_contribution(self, goal_id, progress_added, current_progress):
    564         goal = self._goals.get(goal_id, None)
    565         if not goal:
    566             return
    567         state_changed = goal.update_my_progress(progress_added)
    568         goal.set_current_progress(current_progress)
    569         if goal_id not in self._unclaimed_goal_ids and goal.has_unclaimed_reward():
    570             self._unclaimed_goal_ids.add(goal_id)
    571             goalSignals.on_unclaimed_goal_ids_changed()
    572         goalSignals.on_progress_changed(goal_id, current_progress, state_changed=state_changed)
    573 
    574     def _on_goal_redeemed(self, goal_id, quantity):
    575         goal = self._goals.get(goal_id, None)
    576         if goal:
    577             goal.update_redeemed_capacity(quantity)
    578             if not goal.has_unclaimed_reward() and goal_id in self._unclaimed_goal_ids:
    579                 self._unclaimed_goal_ids.discard(goal_id)
    580                 goalSignals.on_unclaimed_goal_ids_changed()
    581 
    582     def _on_goal_name_changed(self, goal_id, new_name):
    583         goal = self._goals.get(goal_id, None)
    584         if goal:
    585             goal.set_name(new_name)
    586             goalSignals.on_goal_name_changed(goal_id)
    587 
    588     def _on_goal_description_changed(self, goal_id, new_description):
    589         goal = self._goals.get(goal_id, None)
    590         if goal:
    591             goal.set_description(new_description)
    592             goalSignals.on_goal_description_changed(goal_id)
    593 
    594 
    595 def __SakeReloadHook():
    596     try:
    597         CorpGoalsController.get_instance().flush_cache()
    598     except Exception:
    599         import log
    600         log.LogException()