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

116 lines
4.3 KiB
Python

from collections import defaultdict
from typing import Dict, List
from zope import component
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, position_to_usernames: Dict[str, List[str]]):
# 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, usernames in position_to_usernames.items():
if isinstance(usernames, list):
for username in usernames:
self.positions[username].append(position)
else:
raise TypeError("Username(s) under each position must be 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_position_to_usernames = {} # 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:
if position not in new_position_to_usernames:
new_position_to_usernames[position] = []
new_position_to_usernames[position].append(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_position_to_usernames)
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)