pyceo/ceod/transactions/members/UpdateMemberPositionsTransa...

117 lines
4.3 KiB
Python
Raw Normal View History

from collections import defaultdict
from typing import Dict
from zope import component
from typing import Union
from ..AbstractTransaction import AbstractTransaction
from ceo_common.interfaces import ILDAPService, IConfig, IUser
from ceo_common.errors import UserAlreadySubscribedError, UserNotSubscribedError
from ceo_common.logger_factory import logger_factory
logger = logger_factory(__name__)
class UpdateMemberPositionsTransaction(AbstractTransaction):
"""Transaction to update the CSC's executive positions."""
operations = [
'update_positions_ldap',
'update_exec_group_ldap',
'subscribe_to_mailing_lists',
]
def __init__(self, positions_reversed: Dict[str, Union[str,list]]):
# positions_reversed is position -> username
super().__init__()
self.ldap_srv = component.getUtility(ILDAPService)
# Reverse the dict so it's easier to use (username -> positions)
self.positions = defaultdict(list)
for position, username in positions_reversed.items():
if isinstance(username, str):
self.positions[username].append(position)
elif isinstance(username, list):
for user in username:
self.positions[user].append(position)
else:
raise TypeError("Username(s) under each position must either be a string or a list")
# a cached Dict of the Users who need to be modified (username -> User)
self.users: Dict[str, IUser] = {}
# for rollback purposes
self.old_positions = {} # username -> positions
self.old_execs = []
def child_execute_iter(self):
cfg = component.getUtility(IConfig)
mailing_lists = cfg.get('auxiliary mailing lists_exec')
# position -> username
new_positions_reversed = {} # For returning result
# retrieve User objects and cache them
for username in self.positions:
user = self.ldap_srv.get_user(username)
self.users[user.uid] = user
# Remove positions for old users
for user in self.ldap_srv.get_users_with_positions():
if user.uid not in self.positions:
self.positions[user.uid] = []
self.users[user.uid] = user
# Update positions in LDAP
for username, new_positions in self.positions.items():
user = self.users[username]
old_positions = user.positions[:]
user.set_positions(new_positions)
self.old_positions[username] = old_positions
for position in new_positions:
new_positions_reversed[position] = username
yield 'update_positions_ldap'
# update exec group in LDAP
exec_group = self.ldap_srv.get_group('exec')
self.old_execs = exec_group.members[:]
new_execs = [
username for username, new_positions in self.positions.items()
if len(new_positions) > 0
]
exec_group.set_members(new_execs)
yield 'update_exec_group_ldap'
# Update mailing list subscriptions
subscription_failed = False
for username, new_positions in self.positions.items():
user = self.users[username]
for mailing_list in mailing_lists:
try:
if len(new_positions) > 0:
user.subscribe_to_mailing_list(mailing_list)
else:
user.unsubscribe_from_mailing_list(mailing_list)
except (UserAlreadySubscribedError, UserNotSubscribedError):
pass
except Exception:
logger.warning(f'Failed to update mailing list for {user.uid}')
subscription_failed = True
if subscription_failed:
yield 'failed_to_subscribe_to_mailing_lists'
else:
yield 'subscribe_to_mailing_lists'
self.finish(new_positions_reversed)
def rollback(self):
if 'update_exec_group_ldap' in self.finished_operations:
exec_group = self.ldap_srv.get_group('exec')
exec_group.set_members(self.old_execs)
for username, positions in self.old_positions.items():
user = self.users[username]
user.set_positions(positions)