This commit is contained in:
Daniel Sun 2022-12-23 05:10:57 +00:00
parent 1946422b8a
commit c9c2ce2733
10 changed files with 118 additions and 4 deletions

View File

@ -0,0 +1,24 @@
from ceo.utils import http_get
from .Controller import Controller
from .SyncRequestController import SyncRequestController
from ceo.tui.views import SearchGroupResponseView
class SearchGroupController(SyncRequestController):
def __init__(self, model, app):
super().__init__(model, app)
def get_resp(self):
return http_get(f'/api/groups/search/{self.model.query}/{self.model.count}')
def get_response_view(self):
return SearchGroupResponseView(self.model, self, self.app)
def on_next_button_pressed(self, button):
try:
self.model.query = self.get_username_from_view()
#self.model.count = 10
except Controller.InvalidInput:
return
self.on_confirmation_button_pressed(button)

View File

@ -8,6 +8,7 @@ from .ResetPasswordController import ResetPasswordController
from .ChangeLoginShellController import ChangeLoginShellController
from .AddGroupController import AddGroupController
from .GetGroupController import GetGroupController
from .SearchGroupController import SearchGroupController
from .AddMemberToGroupController import AddMemberToGroupController
from .RemoveMemberFromGroupController import RemoveMemberFromGroupController
from .CreateDatabaseController import CreateDatabaseController

View File

@ -0,0 +1,8 @@
class SearchGroupModel:
name = 'SearchGroup'
title = 'Search groups'
def __init__(self):
self.query = ''
self.count = 10
self.resp_json = None

View File

@ -5,6 +5,7 @@ from .ResetPasswordModel import ResetPasswordModel
from .ChangeLoginShellModel import ChangeLoginShellModel
from .AddGroupModel import AddGroupModel
from .GetGroupModel import GetGroupModel
from .SearchGroupModel import SearchGroupModel
from .AddMemberToGroupModel import AddMemberToGroupModel
from .RemoveMemberFromGroupModel import RemoveMemberFromGroupModel
from .CreateDatabaseModel import CreateDatabaseModel
@ -29,6 +30,7 @@ class WelcomeModel:
'Groups': [
AddGroupModel,
GetGroupModel,
SearchGroupModel,
AddMemberToGroupModel,
RemoveMemberFromGroupModel,
],

View File

@ -6,6 +6,7 @@ from .ResetPasswordModel import ResetPasswordModel
from .ChangeLoginShellModel import ChangeLoginShellModel
from .AddGroupModel import AddGroupModel
from .GetGroupModel import GetGroupModel
from .SearchGroupModel import SearchGroupModel
from .AddMemberToGroupModel import AddMemberToGroupModel
from .RemoveMemberFromGroupModel import RemoveMemberFromGroupModel
from .CreateDatabaseModel import CreateDatabaseModel

View File

@ -25,6 +25,7 @@ def handle_sync_response(resp, controller):
raise Controller.RequestFailed()
# this can probably be simplified with getattr or something
def get_mvc(app, name):
if name == WelcomeModel.name:
model = WelcomeModel()
@ -58,6 +59,10 @@ def get_mvc(app, name):
model = GetGroupModel()
controller = GetGroupController(model, app)
view = GetGroupView(model, controller, app)
elif name == SearchGroupModel.name:
model = SearchGroupModel()
controller = SearchGroupController(model, app)
view = SearchGroupView(model, controller, app)
elif name == AddMemberToGroupModel.name:
model = AddMemberToGroupModel()
controller = AddMemberToGroupController(model, app)

View File

@ -0,0 +1,13 @@
import urwid
from .ColumnResponseView import ColumnResponseView
class SearchGroupResponseView(ColumnResponseView):
def __init__(self, model, controller, app):
super().__init__(model, controller,app)
l = self.model.resp_json.copy()
#rows = [(urwid.Text(str(i) + ':', align='right'), urwid.Text(resp)) for i, resp in enumerate(l)]
rows = [(str(i), resp) for i, resp in enumerate(l)]
self.set_pairs(rows)

View File

@ -0,0 +1,16 @@
import urwid
from .ColumnView import ColumnView
class SearchGroupView(ColumnView):
def __init__(self, model, controller, app):
super().__init__(model, controller, app)
# used for query, consider combining Controller.user_name_from_view and Controller.get_group_name_from_view
self.username_edit = urwid.Edit()
rows = [
(
urwid.Text('Query:', align='right'),
self.username_edit
)
]
self.set_rows(rows)

View File

@ -16,6 +16,8 @@ from .AddGroupView import AddGroupView
from .AddGroupConfirmationView import AddGroupConfirmationView
from .GetGroupView import GetGroupView
from .GetGroupResponseView import GetGroupResponseView
from .SearchGroupView import SearchGroupView
from .SearchGroupResponseView import SearchGroupResponseView
from .AddMemberToGroupView import AddMemberToGroupView
from .AddMemberToGroupConfirmationView import AddMemberToGroupConfirmationView
from .RemoveMemberFromGroupView import RemoveMemberFromGroupView

View File

@ -12,6 +12,8 @@ from ceod.transactions.groups import (
DeleteGroupTransaction,
)
from heapq import heappushpop, nlargest
bp = Blueprint('groups', __name__)
@ -32,12 +34,52 @@ def get_group(group_name):
group = ldap_srv.get_group(group_name)
return group.to_dict()
@bp.route('/clubs')
def get_clubs():
@bp.route('/search/<query>/<count>')
def search_group(query, count):
# compute levenshtein edit distance, adapted from rosetta code
def _fuzzy_match(s1, s2):
if len(s1) == 0: return len(s2)
if len(s2) == 0: return len(s1)
edits = [i for i in range(len(s2) + 1)]
for i in range(len(s1)):
corner = i
edits[0] = i + 1
for j in range(len(s2)):
upper = edits[j + 1]
if s1[i] == s2[j]:
edits[j + 1] = corner
else:
m = min(corner, upper, edits[j])
edits[j + 1] = m + 1
corner = upper
return edits[-1]
class _fuzzy_result:
def __init__(self, string, score):
self.string = string
self.score = score
# consider a score worse if the edit distance is larger
def __lt__(self, other): return self.score > other.score
def __gt__(self, other): return self.score < other.score
def __le__(self, other): return self.score >= other.score
def __ge__(self, other): return self.score <= other.score
def __eq__(self, other): return self.score == other.score
def __ne__(self, other): return self.score != other.score
query = str(query)
count = int(count)
ldap_srv = component.getUtility(ILDAPService)
clubs = ldap_srv.get_clubs()
#return jsonify([club.to_dict() for club in clubs])
return jsonify(clubs)
scores = [_fuzzy_result("", 99999) for _ in range(count)]
for club in clubs:
score = _fuzzy_match(query, str(club.cn))
result = _fuzzy_result(str(club.cn), score)
heappushpop(scores, result)
result = [score.string for score in nlargest(count, scores)]
return jsonify(result)
@bp.route('/<group_name>/members/<username>', methods=['POST'])
@authz_restrict_to_syscom