from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ImproperlyConfigured
from django.db.models import Subquery
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils import timezone
from accounts.models import User
from cobalt.settings import (
ABF_STATES,
ABF_USER,
GLOBAL_MPSERVER,
GLOBAL_TITLE,
)
from organisations.forms import OrgFormOld
from organisations.models import (
Organisation,
ClubLog,
MemberMembershipType,
OrganisationFrontPage,
MembershipType,
)
from organisations.club_admin_core import (
get_club_emails_for_system_number,
)
from payments.models import OrganisationTransaction
from rbac.core import rbac_user_has_role
from rbac.models import (
RBACGroupRole,
RBACAdminUserGroup,
RBACAdminGroup,
)
from rbac.views import rbac_forbidden
from utils.views.general import masterpoint_query
[docs]
def org_balance(org, text=None):
"""return organisation balance. If balance is zero return 0.0 unless
text is True, then return "Nil" """
# get balance
last_tran = OrganisationTransaction.objects.filter(organisation=org).last()
if last_tran:
return last_tran.balance
else:
return "Nil" if text else 0.0
[docs]
def get_club_data_from_masterpoints_centre(club_number):
"""Get data about a club from the Masterpoints Centre
Args:
club_number: ABF club number
Returns: dictionary of values
"""
# Try to load data from MP Server
qry = f"{GLOBAL_MPSERVER}/clubDetails/{club_number}"
club_details = masterpoint_query(qry)
if len(club_details) > 0:
club_details = club_details[0]
club_data = {}
if club_details:
club_data = {
"name": club_details["ClubName"],
"club_secretary": club_details["ClubSecName"].strip(),
"state": club_details["VenueState"],
"postcode": club_details["VenuePostcode"],
"club_email": club_details["ClubEmail"],
"club_website": club_details["ClubWebsite"],
"address1": club_details["VenueAddress1"],
"address2": club_details["VenueAddress2"],
"suburb": club_details["VenueSuburb"],
"org_id": club_number,
}
return club_data
[docs]
def get_rbac_model_for_state(state):
"""Take in a state name e.g. NSW and return the model that maps to that organisation.
Assumes one state organisation per state."""
state_org = Organisation.objects.filter(state=state).filter(type="State")
if not state_org:
return None
if state_org.count() > 1:
raise ImproperlyConfigured
return state_org.first().id
# TODO: Retire this
[docs]
@login_required()
def org_edit(request, org_id):
"""Edit details about an organisation OLD - REMOVE ONCE NEW CLUB ADMIN IS DONE
Args:
org_id - organisation to edit
Returns:
HttpResponse - page to edit organisation
"""
if not (
rbac_user_has_role(request.user, "orgs.org.%s.edit" % org_id)
or rbac_user_has_role(request.user, "orgs.admin.edit")
):
return rbac_forbidden(request, "orgs.org.%s.edit" % org_id)
org = get_object_or_404(Organisation, pk=org_id)
if request.method == "POST":
form = OrgFormOld(request.POST, instance=org)
if form.is_valid():
org = form.save(commit=False)
org.last_updated_by = request.user
org.last_updated = timezone.localtime()
org.save()
messages.success(
request, "Changes saved", extra_tags="cobalt-message-success"
)
else:
form = OrgFormOld(instance=org)
return render(request, "organisations/edit_org.html", {"form": form})
[docs]
def club_staff(user):
"""Used by dashboard. Returns the first club found that this user is a staff member for or None
Note: Staff with orgs.org.all rather than orgs.org.<model_id>.all will not get an icon (returns None)
Args:
user: User objects
Returns:
model_id: If not None then model_id is the first organisation that this user has access to
"""
access = (
RBACGroupRole.objects.filter(group__rbacusergroup__member=user)
.filter(app="orgs")
.filter(model="org")
.values_list("model_id")
.last()
)
# We return the latest added access, should maybe allow a user preference here
if access:
return access[0] # first item in tuple (model_id)
return None
[docs]
def replace_unregistered_user_with_real_user(real_user: User):
"""All the data is keyed off system_number so all we really need to do is to delete club emails.
The calling function deletes the unregistered user"""
# NOTE: under club admin this is no longer an issue, as clubs can maintain their own
# email address for members.
# Member*Club*Email.objects.filter(system_number=real_user.system_number).delete()
# Logs
clubs = MemberMembershipType.objects.filter(system_number=real_user.system_number)
for club in clubs:
ClubLog(
actor=real_user,
organisation=club.membership_type.organisation,
action=f"{real_user} registered for {GLOBAL_TITLE}. Unregistered user replaced with real user.",
)
[docs]
@login_required()
def org_profile(request, org_id):
"""Show public profile for organisation"""
org = get_object_or_404(Organisation, pk=org_id)
# create or get the front page
front_page, _ = OrganisationFrontPage.objects.get_or_create(organisation=org)
# Replace tokens with code
url = reverse("results:show_results_for_club_htmx")
results = f""" <div hx-post="{url}" hx-vars="club_id:{org.id}" hx-trigger="load" id="club-results"></div> """
front_page.summary = front_page.summary.replace("{{ RESULTS }}", results)
url = reverse("events:show_congresses_for_club_htmx")
congresses = f""" <div hx-post="{url}" hx-vars="club_id:{org.id}" hx-trigger="load" id="club-congresses"></div> """
front_page.summary = front_page.summary.replace("{{ CONGRESSES }}", congresses)
front_page.summary = front_page.summary.replace("{{ CALENDAR }}", congresses)
# See if this user is an admin for this club
is_admin = is_admin_for_organisation(request.user, org)
return render(
request,
"organisations/org_profile.html",
{"org": org, "front_page": front_page, "is_admin": is_admin},
)
[docs]
def is_admin_for_organisation(user, club):
"""Boolean. Does this user have admin access to this club"""
# Check for state level access
rbac_model_for_state = get_rbac_model_for_state(club.state)
state_role = f"orgs.state.{rbac_model_for_state}.edit"
if rbac_user_has_role(user, state_role):
return True
# Check for global role
if rbac_user_has_role(user, "orgs.admin.edit"):
return True
# Check for club level access
club_role = f"orgs.org.{club.id}.view"
if rbac_user_has_role(user, club_role):
return True
return False
[docs]
@login_required()
def generic_org_search_htmx(request):
"""basic search for organisation by name
We accept a few parameters passed in through hx-vars:
hidden_id_field: field to put the org id into
display_name: field to put the name of the org into
return_trigger: trigger to call when we return
"""
search = request.POST.get("org_search_htmx")
if not search:
return HttpResponse("")
org_matches = Organisation.objects.filter(name__istartswith=search)[:11]
# we get 11 but show 10, so we know if there are more
more = len(org_matches) == 11
if org_matches.count() == 1:
print("Unique")
# Get extra values
hidden_id_field = request.POST.get("hidden_id_field")
display_name = request.POST.get("display_name")
select_callback = request.POST.get("select_callback")
hx_target = request.POST.get("hx_target")
return render(
request,
"organisations/htmx_search/search_results_htmx.html",
{
"org_matches": org_matches[:10],
"more": more,
"display_name": display_name,
"hidden_id_field": hidden_id_field,
"select_callback": select_callback,
"hx_target": hx_target,
},
)
[docs]
def get_org_statistics():
"""return stats on organisations. called by utils statistics"""
total_clubs = Organisation.objects.filter(type="Club").count()
total_orgs = Organisation.objects.count()
return {
"total_clubs": total_clubs,
"total_orgs": total_orgs,
}
[docs]
def get_active_club_statistics():
"""Returns active and inactive club counts by state/territory"""
total_inactive = 0
total = 0
state_counts = []
states = [value[1] for value in ABF_STATES.values()]
for state in states:
# count inactive clubs (where ABF user in the admin group)
inner_query = RBACAdminGroup.objects.filter(
name_item="admin",
name_qualifier__startswith=f"admin.clubs.generated.{state.lower()}.",
).values("id")
inactive_count = RBACAdminUserGroup.objects.filter(
member_id=ABF_USER, group_id__in=Subquery(inner_query)
).count()
total_count = Organisation.objects.filter(
state=state,
type__in=["Club", "State"],
).count()
state_counts.append(
{
"label": state,
"inactive": inactive_count,
"active": total_count - inactive_count,
"total": total_count,
}
)
total_inactive += inactive_count
total += total_count
state_counts.append(
{
"label": "TOTAL",
"inactive": total_inactive,
"active": total - total_inactive,
"total": total,
}
)
return state_counts