Skip to content

Commit

Permalink
Merge pull request #73 from mome-borogove/rework-acl-ui
Browse files Browse the repository at this point in the history
Rework ACL UI
  • Loading branch information
Deadevilgrounds authored Oct 9, 2024
2 parents 6317998 + 62106a2 commit 9540812
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 151 deletions.
77 changes: 26 additions & 51 deletions whctools/templates/whctools/list_acl_members.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ <h3 class="panel-title">Members of the "{{ acl_name }}" ACL</h3>
<div id="all_members" class="panel panel-default tab-pane {% if not date_selected %} active {% endif %}">
<div class="panel-body">
<div class="pull-left">
{{ total_chars }} total characters ({{ total_mains }} mains)
{{ total_chars }} total characters ({{ total_players }} people)
</div>
<div class="pull-right">
<button id="copyAclListButton" class="btn btn-primary whcbutton">Copy ACL as text</button>
Expand All @@ -36,58 +36,33 @@ <h3 class="panel-title">Members of the "{{ acl_name }}" ACL</h3>
<table class="table table-hover whctools-table whctools-table-staff">
<thead>
<tr>
<th>{% comment %}Alt indicator column{% endcomment %}</th>
<th>Character</th>
<th>Alt</th>
<th>Main</th>
<th>Corp</th>
<th>Alliance</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for orphan in orphans %}
<tr class="whctools-main-row whctools-error-row" data-member-id="">
<td></td>
<td>{{orphan.character_name}}</td>
<td><b>No main found</b></td>
<td>{{orphan.corporation_name}}</td>
<td>{{orphan.alliance_name}}</td>
<td><a href="/whctools/staff/action/{{orphan.character_id}}/reject/other/{{reject_timers.medium_reject}}/acl" class="whcbutton btn btn-warning" role="button" id="remove-alt">Remove Character</a></td>
</tr>
{% endfor %}
{% for member in members %}
<tr class="whctools-main-row" data-member-id="{{ member.main.character_id }}">
{% with member.alts|length as alts_length %}
<td>{% if alts_length > 0 %}<i class="alt-caret fas fa-solid fa-caret-right"></i>{% endif %}</td>
{% endwith %}
<td>{{member.main.name}}</td>
<td><i>Main</i></td>
<td>{{member.main.corp}}</td>
<td>{{member.main.alliance}}</td>
{% for character in characters %}
<tr class="whctools-main-row {% if character.error %}whctools-error-row{% else %}whctools-row{% endif %}" data-member-id="">
<td>
<a href="/whctools/staff/action/{{member.main.character_id}}/reject/removed/{{reject_timers.large_reject}}/acl" class="whcbutton btn btn-danger" role="button" id="kick-all">Kick All</a>
{% if character.is_main %}<b>{{character.name}}</b>
{% else %}⤷ {{character.name}}
{% endif %}
</td>
<td>{% if not character.error and not character.main_in_acl %}<span class="whctools-warning"><i class="fa fas fa-exclamation-triangle"></i> {{character.main}}</span>
{% else %}{{character.main}}
{% endif %}
</td>
<td>{{character.corp}}</td>
<td>{{character.alliance}}</td>
{% if character.is_main %}
<td><a href="/whctools/staff/action/{{character.id}}/reject/removed/{{reject_timers.large_reject}}/acl" class="whcbutton btn btn-danger" role="button" id="kick-all">Kick All</a></td>
{% else %}
<td><a href="/whctools/staff/action/{{character.id}}/reject/other/{{reject_timers.medium_reject}}/acl" class="whcbutton btn btn-warning" role="button" id="remove-alt">Remove Alt</a></td>
{% endif %}
</tr>
{% for alt in member.alts %}
<tr class="whctools-alt-row" data-parent-id="{{ member.main.character_id }}" style="display:none;">
<td></td>
<td><div class="whctools-alt-tag whctools-green-bg">Member</div></td>
<td>{{alt.name}}</td>
<td>{{alt.corp}}</td>
<td>{{alt.alliance}}</td>
<td><a href="/whctools/staff/action/{{alt.character_id}}/reject/other/{{reject_timers.medium_reject}}/acl" class="whcbutton btn btn-warning" role="button" id="remove-alt">Remove Alt</a></td>
</tr>
{% endfor %}
{% for alt in member.complete_alts %}
<tr class="whc-alt-row" data-parent-id="{{ member.main.character_id }}" style="display:none;">
<td></td>
<td><div class="whctools-alt-tag whctools-red-bg">NOT ON ACL</div></td>
<td>{{alt.name}}</td>
<td>{{alt.corp}}</td>
<td>{{alt.alliance}}</td>
<td></td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
Expand Down Expand Up @@ -250,15 +225,15 @@ <h2>Copy {{ acl_name }} ACL List</h2>
});

var removeAltButtons = document.querySelectorAll('#remove-alt');
removeAltButtons.forEach(function(button) {
button.addEventListener('click', function(event) {
event.preventDefault(); // Prevent the default action (navigation)
var confirmAction = confirm("Are you sure you want to remove this alt?");
if (confirmAction) {
window.location.href = button.getAttribute('href'); // Proceed with the action
}
});
removeAltButtons.forEach(function(button) {
button.addEventListener('click', function(event) {
event.preventDefault(); // Prevent the default action (navigation)
var confirmAction = confirm("Are you sure you want to remove this alt?");
if (confirmAction) {
window.location.href = button.getAttribute('href'); // Proceed with the action
}
});
});

</script>
{% endblock %}
Expand Down
15 changes: 6 additions & 9 deletions whctools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,16 +242,13 @@ def remove_in_process_application(user, application_details):
)


def generate_raw_copy_for_acl(acl_characters: dict):
def generate_raw_copy_for_acl(sorted_char_list: list):
output = []
for user in acl_characters.values():
main_character_name = user["main"]["name"]
output.append(f"Main: {main_character_name}")
alts = user["alts"]
for char in alts:
output.append(f"Alt: {char['name']}")

output.append("----")
for character in sorted_char_list:
if character['is_main']:
output.append(f"Main: {character['name']}")
else:
output.append(f"Alt: {character['name']}")

return "\n".join(output)

Expand Down
197 changes: 106 additions & 91 deletions whctools/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,20 @@ def accept(request, char_id, acl_name="WHC"):
@login_required
@permission_required("whctools.whc_officer")
def reject(request, char_id, reason, days, source="staff", acl_name="WHC"):
logger.debug(f"Attempting to delete character with char_id: {char_id}, reason: {reason}, days: {days}")

logger.debug(f"char_id: {char_id}, reason {reason}, days {days}")
whcapplication = Applications.objects.filter(eve_character_id=char_id)
if source == "acl":
redirect_target = redirect(f"/whctools/staff/action/{acl_name}/view")
else:
redirect_target = redirect("/whctools/staff/open")

logger.debug(whcapplication)
whcapplication = Applications.objects.filter(eve_character__character_id=char_id)

if whcapplication.exists():
logger.debug(whcapplication)
else:
logger.error(f"Cannot find character {char_id} to delete.")
return redirect_target

if whcapplication: # @@@ move this into template
member_application = whcapplication[0]
Expand Down Expand Up @@ -313,11 +322,8 @@ def reject(request, char_id, reason, days, source="staff", acl_name="WHC"):
f"Your application to the {acl_name} Community on {notification_names} has been rejected.\n\n\t* Reason: {member_application.get_reject_reason_display()}"
+ "\n\nIf you have any questions about this action, please contact WHC Community Coordinators on discord.",
)
if source == "acl":
return redirect(f"/whctools/staff/action/{acl_name}/view")
else:
return redirect("/whctools/staff/open")

return redirect_target

@login_required
@permission_required("whctools.whc_officer")
Expand Down Expand Up @@ -401,98 +407,107 @@ def list_acl_members(request, acl_pk=""):
num_acl_changes = len(acl_changes)

# ACL
mains_and_alts = {}
orphaned_members = []
char_list = []
sentinel_user = get_sentinel_user()
for memb in members_on_acl:
user_obj = get_user_from_evecharacter(memb)
if user_obj == sentinel_user:
orphaned_members.append(memb)
logger.info(f"WHC ACL '{acl_pk}' has orphaned member '{memb}'")
continue

mains_and_alts.setdefault(user_obj.id, {})
if "main" not in mains_and_alts[user_obj.id].keys():
mains_and_alts[user_obj.id]["main"] = get_main_character_from_evecharacter(
memb
)

if mains_and_alts[user_obj.id]["main"] is None:
logger.error(f"Unable to retrieve main character for '{str(memb)}' of user '{str(user_obj)}'")
orphaned_members.append(memb)
continue

mains_and_alts[user_obj.id].setdefault("alts", []).append(memb)
mains_and_alts[user_obj.id].setdefault(
"complete_alts", get_all_characters_from_user(user_obj)
)

alphabetical_orphans = sorted(orphaned_members, key=lambda x: x.character_name)

# note to self - x[1] is not a list index, but a tuple index, becaus its .items(), returning (key, value)
# and it has to be for it to remain a dict after sorting
alphabetical_mains = dict(
sorted(mains_and_alts.items(), key=lambda x: x[1]["main"].character_name)
)

for character in alphabetical_mains.values():
character["main"] = {
"name": character["main"].character_name,
"corp": character["main"].corporation_name,
"alliance": character["main"].alliance_name,
"portrait_url": character["main"].portrait_url(32),
"character_id": character["main"].id,
}
character["alts"] = list(
sorted(
[
{
"name": m.character_name,
"corp": m.corporation_name,
"alliance": m.alliance_name,
"portrait_url": m.portrait_url(32),
"character_id": m.id,
}
for m in character["alts"]
if m.character_name != character["main"]["name"]
],
key=lambda x: x["name"],
)
)

acl_alt_names = [alt["name"] for alt in character["alts"]]

character["complete_alts"] = list(
sorted(
[
{
"name": m.character_name,
"corp": m.corporation_name,
"alliance": m.alliance_name,
"portrait_url": m.portrait_url(32),
}
for m in character["complete_alts"]
if m.character_name not in acl_alt_names
and m.character_name != character["main"]["name"]
],
key=lambda x: x["name"],
)
)

# Pre-compute aggregates
total_mains = len(alphabetical_mains)
total_chars = acl_obj.characters.count()
mains_set = set([]) # just mains in ACL
players_set = set([]) # includes mains in and not in ACL
for member in members_on_acl:
name = member.character_name
char_id = member.character_id
main = None
corp = member.corporation_name
alliance = member.alliance_name
error = None

main_character = get_main_character_from_evecharacter(member)
if main_character is None:
main = '?'
error = 'Orphaned character'
logger.info(f"WHC ACL '{acl_pk}': character '{name}' is an orphan with no main")
else:
main = main_character.character_name
players_set.add(main)

if name == main:
mains_set.add(main)

if not is_character_in_allowed_corp(member):
logger.info(f"WHC ACL '{acl_pk}': character '{name}' is in an invalid corp or alliance")
error = 'Disallowed corp/alliance'
# I believe this isn't possible, since a user has to swap Uni mains in
# order for them to remain in the Uni.
if main_character is not None and not is_character_in_allowed_corp(main_character):
logger.info(f"WHC ACL '{acl_pk}': character '{name}' has a main '{main}' in an invalid corp or alliance")
error = 'Main in disallowed corp/alliance'

char_list.append({
"name": name,
"id": char_id,
"main": main,
"corp": corp,
"alliance": alliance,
"error": error,
"is_main": (name==main),
"main_in_acl": False, # We'll backfill this
})

# Backfill mains
for char in char_list:
if char["main"] in mains_set:
char["main_in_acl"] = True

# sorted() Key function
class ACLSorter(object):
__slots__ = ['obj']
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return self.obj[key]
def __lt__(self, rhs):
lhs = self.obj
# Always float errors to the top
# If they're both errors, revert to normal behavior
if lhs['error'] is not None and rhs['error'] is None:
return True
if rhs['error'] is not None and lhs['error'] is None:
return False
# Sort by main first
if lhs['main'] != rhs['main']:
return lhs['main'] < rhs['main']
else: # both are alts of the same main
# Then float the main to the top of the alt group
if lhs['name'] == lhs['main']:
return True
if rhs['name'] == rhs['main']:
return False
return lhs['name'] < rhs['name']
def __gt__(self, rhs):
return not self.__lt__(rhs)
def __eq__(self, rhs):
return False
def __le__(self, rhs):
return self.__lt__(rhs)
def __ge__(self, rhs):
return self.__gt__(rhs)
__hash__ = None

sorted_char_list = sorted(char_list, key=ACLSorter)
total_mains = len(mains_set)
total_players = len(players_set)
total_chars = len(char_list)

context = {
"acl_name": acl_pk,

"total_mains": total_mains,
"total_chars": total_chars,
"members": alphabetical_mains.values(),
"orphans": alphabetical_orphans,
"acl_name": acl_pk,
"total_players": total_players,
"characters": sorted_char_list,

"date_selected": date_selected,
"acl_changes": acl_changes,
"num_acl_changes": num_acl_changes,
"raw_acl_copy_text": generate_raw_copy_for_acl(alphabetical_mains),
"raw_acl_copy_text": generate_raw_copy_for_acl(sorted_char_list),
"acl_history_request": acl_history_request,
"reject_timers": {
"large_reject": LARGE_REJECT,
Expand Down

0 comments on commit 9540812

Please sign in to comment.