def _switch(self, item: Optional[Dict[str, Any]], source: Listbox, target: Listbox) -> None: if item and item in source.data: source.data.remove(item) source.connect() if item and item not in target.data: target.data.insert(0, item) target.connect()
class UsersSelectionModal(Modal): def setup(self, **context) -> 'RoleDetailsModal': self.injector = context['injector'] self.authark_informer = self.injector['StandardInformer'] self.management_manager = self.injector['ManagementManager'] self.role = context['role'] self.focused: Dict = {} self.available_domain: List = [] self.chosen_domain: List = [] return super().setup(**context) and self def build(self) -> None: super().build() self.modal = None available_frame = Frame(self, title='Available').weight( 5, 4).grid(0, 0) self.available_search = Entry( available_frame, command=self.on_available_search).listen( 'keydown', self.on_available_search, True).weight( col=2).grid(0, 0) self.available_total = Label( available_frame, content='Total: 0').grid(0, 1) self.available = Listbox( available_frame, command=self.on_select).weight( 9).span(col=2).grid(1) switchers = Frame(self).title_style( Color.SUCCESS()).style(border=[]).weight(5).grid(0, 1) Button(switchers, content='\U000025B6', command=self.on_choose).style( Color.SUCCESS(), border=[], template='{}').grid(0) Button(switchers, content='\U000025C0', command=self.on_clear).style( Color.SUCCESS(), border=[], template='{}').grid(1) Button(switchers, content='\U000023E9', command=self.on_choose_all).style( Color.SUCCESS(), border=[], template='{}').grid(2) Button(switchers, content='\U000023EA', command=self.on_clear_all).style( Color.SUCCESS(), border=[], template='{}').grid(3) chosen_frame = Frame(self, title='Chosen').weight( 5, 4).grid(0, 2) self.chosen_search = Entry(chosen_frame).listen( 'keydown', self.on_chosen_search, True).weight(col=2).grid(0, 0) self.chosen_total = Label( chosen_frame, content='Total: 0').grid(0, 1) self.chosen = Listbox( chosen_frame, command=self.on_select).weight( 9).span(col=2).grid(1) actions = Frame( self, title='Actions').title_style( Color.WARNING()).grid(1).span(col=3) Label(actions, content=f'Role: {self.role["name"]}').grid(0, 2) Spacer(actions).grid(0, 3).weight(col=1) Button(actions, content='Save', command=self.on_save ).style(Color.SUCCESS()).grid(0, 4) Button(actions, content='Cancel', command=self.on_cancel ).style(Color.WARNING()).grid(0, 5) async def on_select(self, event: Event) -> None: item = getattr(event.target.parent, 'item', None) event.target.focus() self.focused = item if isinstance(item, dict) else {} async def on_available_search(self, event: Event) -> None: if event.key == '\n': event.stop = True text = self.available_search.text.strip() self.available.offset = 0 self.available_domain = [] if not text else [ '|', ('name', 'ilike', f'%{text}%'), ('email', 'ilike', f'%{text}%')] await self.load() self.available_search.focus() async def on_chosen_search(self, event: Event) -> None: if event.key == '\n': event.stop = True text = self.chosen_search.text.strip() self.chosen.offset = 0 self.chosen_domain = [] if not text else [ '|', ('name', 'ilike', f'%{text}%'), ('email', 'ilike', f'%{text}%')] await self.load() self.chosen_search.focus() async def load(self) -> None: current_user_ids = [ranking['user_id'] for ranking in (await self.authark_informer.search({ "meta": { "model": "ranking", "domain": [ ('role_id', '=', self.role['id'])] } }))['data'] ] available_users = (await self.authark_informer.search({ "meta": { "model": "user", "domain": [ '!', ('id', 'in', current_user_ids)] + self.available_domain } }))['data'] chosen_users = (await self.authark_informer.search({ "meta": { "model": "user", "domain": [ ('id', 'in', current_user_ids)] + self.chosen_domain } }))['data'] self.available.setup(data=available_users, fields=['username', 'email'], limit=20).connect() self.chosen.setup(data=chosen_users, fields=['username', 'email'], limit=20).connect() self._update_totals() async def on_choose(self, event: Event) -> None: self._switch(self.focused, self.available, self.chosen) self._update_totals() async def on_choose_all(self, event: Event) -> None: self.chosen.data = list(self.available.data + self.chosen.data) self.available.data = [] self.chosen.connect() self.available.connect() self._update_totals() async def on_clear(self, event: Event) -> None: self._switch(self.focused, self.chosen, self.available) self._update_totals() async def on_clear_all(self, event: Event) -> None: self.available.data = list(self.available.data + self.chosen.data) self.chosen.data = [] self.available.connect() self.chosen.connect() self._update_totals() def _update_totals(self) -> None: self.available_total.setup( content=f'Total: {len(self.available.data)}').render() self.chosen_total.setup( content=f'Total: {len(self.chosen.data)}').render() def _switch(self, item: Optional[Dict[str, Any]], source: Listbox, target: Listbox) -> None: if item and item in source.data: source.data.remove(item) source.connect() if item and item not in target.data: target.data.insert(0, item) target.connect() async def on_save(self, event: Event) -> None: removing_ranking_ids = [ ranking['id'] for ranking in (await self.authark_informer.search({ "meta": { "model": "ranking", "domain": [('role_id', '=', self.role['id']), ('user_id', 'in', [user['id'] for user in self.available.data])] } }))['data'] ] await self.management_manager.deassign_role(removing_ranking_ids) adding_rankings = [{'role_id': self.role['id'], 'user_id': user['id']} for user in self.chosen.data] await self.management_manager.assign_role({ "meta": {}, "data": adding_rankings }) await self.done({'result': 'saved'}) async def on_cancel(self, event: Event) -> None: await self.done({'result': 'cancelled'})