mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-08 20:10:17 +02:00
remove trailing whitespaces
This commit is contained in:
parent
10bd77d761
commit
8c3df89d52
@ -1,4 +1,5 @@
|
||||
stages:
|
||||
- pre-commit
|
||||
- gitlab
|
||||
- test
|
||||
- deploy
|
||||
@ -13,6 +14,18 @@ before_script:
|
||||
- python -V
|
||||
- pip install wheel tox
|
||||
|
||||
pre-commit-check:
|
||||
stage: pre-commit
|
||||
image: python:3.6-buster
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
paths:
|
||||
- ${PRE_COMMIT_HOME}
|
||||
script:
|
||||
- pip install pre-commit
|
||||
- pre-commit run --all-files
|
||||
|
||||
sast:
|
||||
stage: gitlab
|
||||
before_script: []
|
||||
@ -27,7 +40,7 @@ dependency_scanning:
|
||||
|
||||
test-3.7-core:
|
||||
image: python:3.7-buster
|
||||
script:
|
||||
script:
|
||||
- tox -e py37-core
|
||||
artifacts:
|
||||
when: always
|
||||
@ -36,7 +49,7 @@ test-3.7-core:
|
||||
|
||||
test-3.8-core:
|
||||
image: python:3.8-buster
|
||||
script:
|
||||
script:
|
||||
- tox -e py38-core
|
||||
artifacts:
|
||||
when: always
|
||||
@ -45,7 +58,7 @@ test-3.8-core:
|
||||
|
||||
test-3.9-core:
|
||||
image: python:3.9-buster
|
||||
script:
|
||||
script:
|
||||
- tox -e py39-core
|
||||
artifacts:
|
||||
when: always
|
||||
@ -54,7 +67,7 @@ test-3.9-core:
|
||||
|
||||
test-3.7-all:
|
||||
image: python:3.7-buster
|
||||
script:
|
||||
script:
|
||||
- tox -e py37-all
|
||||
artifacts:
|
||||
when: always
|
||||
@ -63,7 +76,7 @@ test-3.7-all:
|
||||
|
||||
test-3.8-all:
|
||||
image: python:3.8-buster
|
||||
script:
|
||||
script:
|
||||
- tox -e py38-all
|
||||
artifacts:
|
||||
when: always
|
||||
@ -72,7 +85,7 @@ test-3.8-all:
|
||||
|
||||
test-3.9-all:
|
||||
image: python:3.9-buster
|
||||
script:
|
||||
script:
|
||||
- tox -e py39-all
|
||||
artifacts:
|
||||
when: always
|
||||
@ -91,4 +104,4 @@ deploy_production:
|
||||
- twine upload dist/*
|
||||
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
- if: $CI_COMMIT_TAG
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Bug
|
||||
|
||||
- I have searched [issues](https://gitlab.com/allianceauth/allianceauth/issues?scope=all&utf8=%E2%9C%93&state=all) (Y/N):
|
||||
- What Version of Alliance Auth:
|
||||
- What Operating System:
|
||||
- What Version of Alliance Auth:
|
||||
- What Operating System:
|
||||
- Version of other components relevant to issue eg. Service, Database:
|
||||
|
||||
Please include a brief description of your issue here.
|
||||
|
16
.pre-commit-config.yaml
Normal file
16
.pre-commit-config.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
# Apply to all files without committing:
|
||||
# pre-commit run --all-files
|
||||
# Update this file:
|
||||
# pre-commit autoupdate
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.4.0
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-json
|
||||
- id: check-xml
|
||||
- id: check-yaml
|
||||
- id: fix-byte-order-marker
|
||||
- id: trailing-whitespace
|
||||
exclude: \.(min\.css|min\.js|po|mo)$
|
@ -84,7 +84,7 @@ class UserProfileInline(admin.StackedInline):
|
||||
if request.user.is_superuser:
|
||||
query |= Q(userprofile__isnull=True)
|
||||
else:
|
||||
query |= Q(character_ownership__user=obj)
|
||||
query |= Q(character_ownership__user=obj)
|
||||
formset = super().get_formset(request, obj=obj, **kwargs)
|
||||
|
||||
def get_kwargs(self, index):
|
||||
@ -123,26 +123,26 @@ def user_username(obj):
|
||||
|
||||
works for both User objects and objects with `user` as FK to User
|
||||
To be used for all user based admin lists
|
||||
"""
|
||||
"""
|
||||
link = reverse(
|
||||
'admin:{}_{}_change'.format(
|
||||
obj._meta.app_label,
|
||||
type(obj).__name__.lower()
|
||||
),
|
||||
),
|
||||
args=(obj.pk,)
|
||||
)
|
||||
user_obj = obj.user if hasattr(obj, 'user') else obj
|
||||
if user_obj.profile.main_character:
|
||||
return format_html(
|
||||
'<strong><a href="{}">{}</a></strong><br>{}',
|
||||
link,
|
||||
link,
|
||||
user_obj.username,
|
||||
user_obj.profile.main_character.character_name
|
||||
)
|
||||
else:
|
||||
return format_html(
|
||||
'<strong><a href="{}">{}</a></strong>',
|
||||
link,
|
||||
link,
|
||||
user_obj.username,
|
||||
)
|
||||
|
||||
@ -160,16 +160,16 @@ def user_main_organization(obj):
|
||||
user_obj = obj.user if hasattr(obj, 'user') else obj
|
||||
if not user_obj.profile.main_character:
|
||||
result = None
|
||||
else:
|
||||
else:
|
||||
corporation = user_obj.profile.main_character.corporation_name
|
||||
if user_obj.profile.main_character.alliance_id:
|
||||
if user_obj.profile.main_character.alliance_id:
|
||||
result = format_html(
|
||||
'{}<br>{}',
|
||||
corporation,
|
||||
corporation,
|
||||
user_obj.profile.main_character.alliance_name
|
||||
)
|
||||
else:
|
||||
result = corporation
|
||||
result = corporation
|
||||
return result
|
||||
|
||||
|
||||
@ -200,7 +200,7 @@ class MainCorporationsFilter(admin.SimpleListFilter):
|
||||
def queryset(self, request, qs):
|
||||
if self.value() is None:
|
||||
return qs.all()
|
||||
else:
|
||||
else:
|
||||
if qs.model == User:
|
||||
return qs.filter(
|
||||
profile__main_character__corporation_id=self.value()
|
||||
@ -209,7 +209,7 @@ class MainCorporationsFilter(admin.SimpleListFilter):
|
||||
return qs.filter(
|
||||
user__profile__main_character__corporation_id=self.value()
|
||||
)
|
||||
|
||||
|
||||
|
||||
class MainAllianceFilter(admin.SimpleListFilter):
|
||||
"""Custom filter to filter on alliances from mains only
|
||||
@ -234,16 +234,16 @@ class MainAllianceFilter(admin.SimpleListFilter):
|
||||
def queryset(self, request, qs):
|
||||
if self.value() is None:
|
||||
return qs.all()
|
||||
else:
|
||||
else:
|
||||
if qs.model == User:
|
||||
return qs.filter(profile__main_character__alliance_id=self.value())
|
||||
return qs.filter(profile__main_character__alliance_id=self.value())
|
||||
else:
|
||||
return qs.filter(
|
||||
user__profile__main_character__alliance_id=self.value()
|
||||
)
|
||||
|
||||
|
||||
def update_main_character_model(modeladmin, request, queryset):
|
||||
|
||||
def update_main_character_model(modeladmin, request, queryset):
|
||||
tasks_count = 0
|
||||
for obj in queryset:
|
||||
if obj.profile.main_character:
|
||||
@ -251,7 +251,7 @@ def update_main_character_model(modeladmin, request, queryset):
|
||||
tasks_count += 1
|
||||
|
||||
modeladmin.message_user(
|
||||
request,
|
||||
request,
|
||||
'Update from ESI started for {} characters'.format(tasks_count)
|
||||
)
|
||||
|
||||
@ -262,7 +262,7 @@ update_main_character_model.short_description = \
|
||||
|
||||
class UserAdmin(BaseUserAdmin):
|
||||
"""Extending Django's UserAdmin model
|
||||
|
||||
|
||||
Behavior of groups and characters columns can be configured via settings
|
||||
"""
|
||||
|
||||
@ -270,7 +270,7 @@ class UserAdmin(BaseUserAdmin):
|
||||
css = {
|
||||
"all": ("authentication/css/admin.css",)
|
||||
}
|
||||
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.prefetch_related("character_ownerships__character", "groups")
|
||||
@ -279,8 +279,8 @@ class UserAdmin(BaseUserAdmin):
|
||||
actions = super(BaseUserAdmin, self).get_actions(request)
|
||||
|
||||
actions[update_main_character_model.__name__] = (
|
||||
update_main_character_model,
|
||||
update_main_character_model.__name__,
|
||||
update_main_character_model,
|
||||
update_main_character_model.__name__,
|
||||
update_main_character_model.short_description
|
||||
)
|
||||
|
||||
@ -290,21 +290,21 @@ class UserAdmin(BaseUserAdmin):
|
||||
if svc.update_groups.__module__ != ServicesHook.update_groups.__module__:
|
||||
action = make_service_hooks_update_groups_action(svc)
|
||||
actions[action.__name__] = (
|
||||
action,
|
||||
action,
|
||||
action.__name__,
|
||||
action.short_description
|
||||
)
|
||||
|
||||
|
||||
# Create sync nickname action if service implements it
|
||||
if svc.sync_nickname.__module__ != ServicesHook.sync_nickname.__module__:
|
||||
action = make_service_hooks_sync_nickname_action(svc)
|
||||
actions[action.__name__] = (
|
||||
action, action.__name__,
|
||||
action, action.__name__,
|
||||
action.short_description
|
||||
)
|
||||
return actions
|
||||
|
||||
def _list_2_html_w_tooltips(self, my_items: list, max_items: int) -> str:
|
||||
def _list_2_html_w_tooltips(self, my_items: list, max_items: int) -> str:
|
||||
"""converts list of strings into HTML with cutoff and tooltip"""
|
||||
items_truncated_str = ', '.join(my_items[:max_items])
|
||||
if not my_items:
|
||||
@ -320,27 +320,27 @@ class UserAdmin(BaseUserAdmin):
|
||||
items_truncated_str
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
inlines = BaseUserAdmin.inlines + [UserProfileInline]
|
||||
ordering = ('username', )
|
||||
ordering = ('username', )
|
||||
list_select_related = ('profile__state', 'profile__main_character')
|
||||
show_full_result_count = True
|
||||
show_full_result_count = True
|
||||
list_display = (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
'_state',
|
||||
user_username,
|
||||
'_state',
|
||||
'_groups',
|
||||
user_main_organization,
|
||||
'_characters',
|
||||
'is_active',
|
||||
'date_joined',
|
||||
'_role'
|
||||
)
|
||||
)
|
||||
list_display_links = None
|
||||
list_filter = (
|
||||
list_filter = (
|
||||
'profile__state',
|
||||
'groups',
|
||||
MainCorporationsFilter,
|
||||
'groups',
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter,
|
||||
'is_active',
|
||||
'date_joined',
|
||||
@ -348,32 +348,32 @@ class UserAdmin(BaseUserAdmin):
|
||||
'is_superuser'
|
||||
)
|
||||
search_fields = (
|
||||
'username',
|
||||
'username',
|
||||
'character_ownerships__character__character_name'
|
||||
)
|
||||
|
||||
|
||||
def _characters(self, obj):
|
||||
character_ownerships = list(obj.character_ownerships.all())
|
||||
characters = [obj.character.character_name for obj in character_ownerships]
|
||||
return self._list_2_html_w_tooltips(
|
||||
sorted(characters),
|
||||
sorted(characters),
|
||||
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
|
||||
)
|
||||
|
||||
|
||||
_characters.short_description = 'characters'
|
||||
|
||||
|
||||
def _state(self, obj):
|
||||
return obj.profile.state.name
|
||||
|
||||
|
||||
_state.short_description = 'state'
|
||||
_state.admin_order_field = 'profile__state'
|
||||
|
||||
def _groups(self, obj):
|
||||
def _groups(self, obj):
|
||||
my_groups = sorted([group.name for group in list(obj.groups.all())])
|
||||
return self._list_2_html_w_tooltips(
|
||||
my_groups, AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
|
||||
)
|
||||
|
||||
|
||||
_groups.short_description = 'groups'
|
||||
|
||||
def _role(self, obj):
|
||||
@ -382,11 +382,11 @@ class UserAdmin(BaseUserAdmin):
|
||||
elif obj.is_staff:
|
||||
role = 'Staff'
|
||||
else:
|
||||
role = 'User'
|
||||
role = 'User'
|
||||
return role
|
||||
|
||||
|
||||
_role.short_description = 'role'
|
||||
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return request.user.has_perm('auth.change_user')
|
||||
|
||||
@ -404,10 +404,10 @@ class UserAdmin(BaseUserAdmin):
|
||||
|
||||
|
||||
@admin.register(State)
|
||||
class StateAdmin(admin.ModelAdmin):
|
||||
class StateAdmin(admin.ModelAdmin):
|
||||
list_select_related = True
|
||||
list_display = ('name', 'priority', '_user_count')
|
||||
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
return qs.annotate(user_count=Count("userprofile__id"))
|
||||
@ -423,17 +423,17 @@ class StateAdmin(admin.ModelAdmin):
|
||||
}),
|
||||
('Membership', {
|
||||
'fields': (
|
||||
'public',
|
||||
'member_characters',
|
||||
'member_corporations',
|
||||
'public',
|
||||
'member_characters',
|
||||
'member_corporations',
|
||||
'member_alliances'
|
||||
),
|
||||
})
|
||||
)
|
||||
filter_horizontal = [
|
||||
'member_characters',
|
||||
'member_corporations',
|
||||
'member_alliances',
|
||||
'member_characters',
|
||||
'member_corporations',
|
||||
'member_alliances',
|
||||
'permissions'
|
||||
]
|
||||
|
||||
@ -463,16 +463,16 @@ class StateAdmin(admin.ModelAdmin):
|
||||
}),
|
||||
)
|
||||
return super(StateAdmin, self).get_fieldsets(request, obj=obj)
|
||||
|
||||
|
||||
|
||||
class BaseOwnershipAdmin(admin.ModelAdmin):
|
||||
class Media:
|
||||
css = {
|
||||
"all": ("authentication/css/admin.css",)
|
||||
}
|
||||
|
||||
|
||||
list_select_related = (
|
||||
'user__profile__state', 'user__profile__main_character', 'character')
|
||||
'user__profile__state', 'user__profile__main_character', 'character')
|
||||
list_display = (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
@ -480,13 +480,13 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
|
||||
'character',
|
||||
)
|
||||
search_fields = (
|
||||
'user__username',
|
||||
'character__character_name',
|
||||
'character__corporation_name',
|
||||
'user__username',
|
||||
'character__character_name',
|
||||
'character__corporation_name',
|
||||
'character__alliance_name'
|
||||
)
|
||||
list_filter = (
|
||||
MainCorporationsFilter,
|
||||
list_filter = (
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter,
|
||||
)
|
||||
|
||||
|
@ -2,14 +2,14 @@ from django.conf import settings
|
||||
|
||||
|
||||
def _clean_setting(
|
||||
name: str,
|
||||
default_value: object,
|
||||
name: str,
|
||||
default_value: object,
|
||||
min_value: int = None,
|
||||
max_value: int = None,
|
||||
required_type: type = None
|
||||
):
|
||||
"""cleans the input for a custom setting
|
||||
|
||||
|
||||
Will use `default_value` if settings does not exit or has the wrong type
|
||||
or is outside define boundaries (for int only)
|
||||
|
||||
@ -18,22 +18,22 @@ def _clean_setting(
|
||||
Will assume `min_value` of 0 for int (can be overriden)
|
||||
|
||||
Returns cleaned value for setting
|
||||
"""
|
||||
"""
|
||||
if default_value is None and not required_type:
|
||||
raise ValueError('You must specify a required_type for None defaults')
|
||||
|
||||
|
||||
if not required_type:
|
||||
required_type = type(default_value)
|
||||
|
||||
if min_value is None and required_type == int:
|
||||
min_value = 0
|
||||
|
||||
min_value = 0
|
||||
|
||||
if (hasattr(settings, name)
|
||||
and isinstance(getattr(settings, name), required_type)
|
||||
and (min_value is None or getattr(settings, name) >= min_value)
|
||||
and (max_value is None or getattr(settings, name) <= max_value)
|
||||
):
|
||||
return getattr(settings, name)
|
||||
):
|
||||
return getattr(settings, name)
|
||||
else:
|
||||
return default_value
|
||||
|
||||
|
@ -12,9 +12,9 @@ logger = logging.getLogger(__name__)
|
||||
class StateBackend(ModelBackend):
|
||||
@staticmethod
|
||||
def _get_state_permissions(user_obj):
|
||||
"""returns permissions for state of given user object"""
|
||||
"""returns permissions for state of given user object"""
|
||||
if hasattr(user_obj, "profile") and user_obj.profile:
|
||||
return Permission.objects.filter(state=user_obj.profile.state)
|
||||
return Permission.objects.filter(state=user_obj.profile.state)
|
||||
else:
|
||||
return Permission.objects.none()
|
||||
|
||||
|
@ -17,7 +17,7 @@ def create_permissions(apps, schema_editor):
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ct = ContentType.objects.get_for_model(User)
|
||||
Permission.objects.get_or_create(codename="member", content_type=ct, name="member")
|
||||
Permission.objects.get_or_create(codename="blue_member", content_type=ct, name="blue_member")
|
||||
Permission.objects.get_or_create(codename="blue_member", content_type=ct, name="blue_member")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -74,7 +74,7 @@ class UserProfile(models.Model):
|
||||
logger.info('Updating {} state to {}'.format(self.user, self.state))
|
||||
self.save(update_fields=['state'])
|
||||
notify(
|
||||
self.user,
|
||||
self.user,
|
||||
_('State changed to: %s' % state),
|
||||
_('Your user\'s state is now: %(state)s')
|
||||
% ({'state': state}),
|
||||
|
@ -1,12 +1,12 @@
|
||||
/*
|
||||
CSS for allianceauth admin site
|
||||
/*
|
||||
CSS for allianceauth admin site
|
||||
*/
|
||||
|
||||
/* styling for profile pic */
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.column-user_profile_pic {
|
||||
.column-user_profile_pic {
|
||||
width: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -26,4 +26,4 @@ CSS for allianceauth admin site
|
||||
color: black ;
|
||||
background-color: rgb(255, 255, 204) ;
|
||||
z-index: 1 ;
|
||||
}
|
||||
}
|
@ -63,7 +63,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table visible-xs-block">
|
||||
<div class="table visible-xs-block">
|
||||
<p>
|
||||
<img class="ra-avatar" src="{{ main.portrait_url_64 }}">
|
||||
<img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}">
|
||||
@ -74,7 +74,7 @@
|
||||
{{ main.corporation_name }}<br>
|
||||
{{ main.alliance_name }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
@ -121,7 +121,7 @@
|
||||
{% trans 'Characters' %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="panel-body">
|
||||
<table class="table table-aa hidden-xs">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -132,20 +132,20 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for char in characters %}
|
||||
{% for char in characters %}
|
||||
<tr>
|
||||
<td class="text-center"><img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
||||
</td>
|
||||
<td class="text-center">{{ char.character_name }}</td>
|
||||
<td class="text-center">{{ char.corporation_name }}</td>
|
||||
<td class="text-center">{{ char.alliance_name }}</td>
|
||||
</tr>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="table table-aa visible-xs-block" style="width: 100%">
|
||||
<tbody>
|
||||
{% for char in characters %}
|
||||
{% for char in characters %}
|
||||
<tr>
|
||||
<td class="text-center" style="vertical-align: middle">
|
||||
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
|
||||
@ -154,7 +154,7 @@
|
||||
<strong>{{ char.character_name }}</strong><br>
|
||||
{{ char.corporation_name }}<br>
|
||||
{{ char.alliance_name|default:"" }}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% extends 'public/middle_box.html' %}
|
||||
{% load static %}
|
||||
{% block page_title %}Login{% endblock %}
|
||||
{% block middle_box_content %}
|
||||
{% block middle_box_content %}
|
||||
<a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}">
|
||||
<img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
|
||||
</a>
|
||||
|
@ -16,15 +16,15 @@ from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from ..admin import (
|
||||
BaseUserAdmin,
|
||||
CharacterOwnershipAdmin,
|
||||
CharacterOwnershipAdmin,
|
||||
StateAdmin,
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter,
|
||||
OwnershipRecordAdmin,
|
||||
User,
|
||||
UserAdmin,
|
||||
UserAdmin,
|
||||
user_main_organization,
|
||||
user_profile_pic,
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
update_main_character_model,
|
||||
make_service_hooks_update_groups_action,
|
||||
@ -36,7 +36,7 @@ from . import get_admin_change_view_url, get_admin_search_url
|
||||
MODULE_PATH = 'allianceauth.authentication.admin'
|
||||
|
||||
|
||||
class MockRequest(object):
|
||||
class MockRequest(object):
|
||||
def __init__(self, user=None):
|
||||
self.user = user
|
||||
|
||||
@ -51,7 +51,7 @@ class TestCaseWithTestData(TestCase):
|
||||
EveAllianceInfo, EveCorporationInfo, EveCharacter, Group, User
|
||||
]:
|
||||
MyModel.objects.all().delete()
|
||||
|
||||
|
||||
# groups
|
||||
cls.group_1 = Group.objects.create(
|
||||
name='Group 1'
|
||||
@ -84,16 +84,16 @@ class TestCaseWithTestData(TestCase):
|
||||
alliance = EveAllianceInfo.objects.create(
|
||||
alliance_id=3001,
|
||||
alliance_name='Wayne Enterprises',
|
||||
alliance_ticker='WE',
|
||||
alliance_ticker='WE',
|
||||
executor_corp_id=2001
|
||||
)
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2001,
|
||||
corporation_name='Wayne Technologies',
|
||||
corporation_ticker='WT',
|
||||
corporation_ticker='WT',
|
||||
member_count=42,
|
||||
alliance=alliance
|
||||
)
|
||||
)
|
||||
cls.user_1 = User.objects.create_user(
|
||||
character_1.character_name.replace(' ', '_'),
|
||||
'abc@example.com',
|
||||
@ -111,7 +111,7 @@ class TestCaseWithTestData(TestCase):
|
||||
)
|
||||
cls.user_1.profile.main_character = character_1
|
||||
cls.user_1.profile.save()
|
||||
cls.user_1.groups.add(cls.group_1)
|
||||
cls.user_1.groups.add(cls.group_1)
|
||||
|
||||
# user 2 - corp only, staff
|
||||
character_2 = EveCharacter.objects.create(
|
||||
@ -125,7 +125,7 @@ class TestCaseWithTestData(TestCase):
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2002,
|
||||
corporation_name='Daily Plane',
|
||||
corporation_ticker='DP',
|
||||
corporation_ticker='DP',
|
||||
member_count=99,
|
||||
alliance=None
|
||||
)
|
||||
@ -144,7 +144,7 @@ class TestCaseWithTestData(TestCase):
|
||||
cls.user_2.groups.add(cls.group_2)
|
||||
cls.user_2.is_staff = True
|
||||
cls.user_2.save()
|
||||
|
||||
|
||||
# user 3 - no main, no group, superuser
|
||||
character_3 = EveCharacter.objects.create(
|
||||
character_id=1101,
|
||||
@ -157,7 +157,7 @@ class TestCaseWithTestData(TestCase):
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2101,
|
||||
corporation_name='Lex Corp',
|
||||
corporation_ticker='LC',
|
||||
corporation_ticker='LC',
|
||||
member_count=666,
|
||||
alliance=None
|
||||
)
|
||||
@ -186,25 +186,25 @@ def make_generic_search_request(ModelClass: type, search_term: str):
|
||||
username='superuser', password='secret', email='admin@example.com'
|
||||
)
|
||||
c = Client()
|
||||
c.login(username='superuser', password='secret')
|
||||
c.login(username='superuser', password='secret')
|
||||
return c.get(
|
||||
'%s?q=%s' % (get_admin_search_url(ModelClass), quote(search_term))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.modeladmin = CharacterOwnershipAdmin(
|
||||
model=User, admin_site=AdminSite()
|
||||
)
|
||||
|
||||
def test_change_view_loads_normally(self):
|
||||
def test_change_view_loads_normally(self):
|
||||
User.objects.create_superuser(
|
||||
username='superuser', password='secret', email='admin@example.com'
|
||||
)
|
||||
c = Client()
|
||||
c.login(username='superuser', password='secret')
|
||||
c.login(username='superuser', password='secret')
|
||||
ownership = self.user_1.character_ownerships.first()
|
||||
response = c.get(get_admin_change_view_url(ownership))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@ -219,18 +219,18 @@ class TestCharacterOwnershipAdmin(TestCaseWithTestData):
|
||||
|
||||
|
||||
class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.modeladmin = OwnershipRecordAdmin(
|
||||
model=User, admin_site=AdminSite()
|
||||
)
|
||||
|
||||
def test_change_view_loads_normally(self):
|
||||
def test_change_view_loads_normally(self):
|
||||
User.objects.create_superuser(
|
||||
username='superuser', password='secret', email='admin@example.com'
|
||||
)
|
||||
c = Client()
|
||||
c.login(username='superuser', password='secret')
|
||||
c.login(username='superuser', password='secret')
|
||||
ownership_record = OwnershipRecord.objects\
|
||||
.filter(user=self.user_1)\
|
||||
.first()
|
||||
@ -245,23 +245,23 @@ class TestOwnershipRecordAdmin(TestCaseWithTestData):
|
||||
|
||||
|
||||
class TestStateAdmin(TestCaseWithTestData):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.modeladmin = StateAdmin(
|
||||
model=User, admin_site=AdminSite()
|
||||
)
|
||||
|
||||
def test_change_view_loads_normally(self):
|
||||
def test_change_view_loads_normally(self):
|
||||
User.objects.create_superuser(
|
||||
username='superuser', password='secret', email='admin@example.com'
|
||||
)
|
||||
c = Client()
|
||||
c.login(username='superuser', password='secret')
|
||||
|
||||
c.login(username='superuser', password='secret')
|
||||
|
||||
guest_state = AuthUtils.get_guest_state()
|
||||
response = c.get(get_admin_change_view_url(guest_state))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
member_state = AuthUtils.get_member_state()
|
||||
response = c.get(get_admin_change_view_url(member_state))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@ -281,12 +281,12 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
model=User, admin_site=AdminSite()
|
||||
)
|
||||
self.character_1 = self.user_1.character_ownerships.first().character
|
||||
|
||||
|
||||
def test_user_profile_pic_u1(self):
|
||||
expected = (
|
||||
'<img src="https://images.evetech.net/characters/1001/'
|
||||
'portrait?size=32" class="img-circle">'
|
||||
)
|
||||
)
|
||||
self.assertEqual(user_profile_pic(self.user_1), expected)
|
||||
|
||||
def test_user_profile_pic_u3(self):
|
||||
@ -332,18 +332,18 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
expected = 'Lex Luthor'
|
||||
result = self.modeladmin._characters(self.user_3)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_groups_u1(self):
|
||||
|
||||
def test_groups_u1(self):
|
||||
expected = 'Group 1'
|
||||
result = self.modeladmin._groups(self.user_1)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_groups_u2(self):
|
||||
def test_groups_u2(self):
|
||||
expected = 'Group 2'
|
||||
result = self.modeladmin._groups(self.user_2)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_groups_u3(self):
|
||||
|
||||
def test_groups_u3(self):
|
||||
result = self.modeladmin._groups(self.user_3)
|
||||
self.assertIsNone(result)
|
||||
|
||||
@ -387,7 +387,7 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
expected = None
|
||||
result = self.modeladmin._list_2_html_w_tooltips(items, 5)
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
|
||||
# actions
|
||||
|
||||
@patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True)
|
||||
@ -401,14 +401,14 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
)
|
||||
self.assertEqual(mock_task.delay.call_count, 2)
|
||||
self.assertTrue(mock_message_user.called)
|
||||
|
||||
|
||||
# filters
|
||||
|
||||
|
||||
def test_filter_main_corporations(self):
|
||||
|
||||
class UserAdminTest(BaseUserAdmin):
|
||||
|
||||
class UserAdminTest(BaseUserAdmin):
|
||||
list_filter = (MainCorporationsFilter,)
|
||||
|
||||
|
||||
my_modeladmin = UserAdminTest(User, AdminSite())
|
||||
|
||||
# Make sure the lookups are correct
|
||||
@ -417,7 +417,7 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
filters = changelist.get_filters(request)
|
||||
filterspec = filters[0][0]
|
||||
expected = [
|
||||
expected = [
|
||||
(2002, 'Daily Planet'),
|
||||
(2001, 'Wayne Technologies'),
|
||||
]
|
||||
@ -425,20 +425,20 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
|
||||
# Make sure the correct queryset is returned
|
||||
request = self.factory.get(
|
||||
'/',
|
||||
'/',
|
||||
{'main_corporation_id__exact': self.character_1.corporation_id}
|
||||
)
|
||||
request.user = self.user_1
|
||||
request.user = self.user_1
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
queryset = changelist.get_queryset(request)
|
||||
expected = [self.user_1]
|
||||
self.assertSetEqual(set(queryset), set(expected))
|
||||
|
||||
def test_filter_main_alliances(self):
|
||||
|
||||
class UserAdminTest(BaseUserAdmin):
|
||||
|
||||
class UserAdminTest(BaseUserAdmin):
|
||||
list_filter = (MainAllianceFilter,)
|
||||
|
||||
|
||||
my_modeladmin = UserAdminTest(User, AdminSite())
|
||||
|
||||
# Make sure the lookups are correct
|
||||
@ -447,17 +447,17 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
filters = changelist.get_filters(request)
|
||||
filterspec = filters[0][0]
|
||||
expected = [
|
||||
(3001, 'Wayne Enterprises'),
|
||||
expected = [
|
||||
(3001, 'Wayne Enterprises'),
|
||||
]
|
||||
self.assertEqual(filterspec.lookup_choices, expected)
|
||||
|
||||
# Make sure the correct queryset is returned
|
||||
request = self.factory.get(
|
||||
'/',
|
||||
'/',
|
||||
{'main_alliance_id__exact': self.character_1.alliance_id}
|
||||
)
|
||||
request.user = self.user_1
|
||||
request.user = self.user_1
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
queryset = changelist.get_queryset(request)
|
||||
expected = [self.user_1]
|
||||
@ -468,7 +468,7 @@ class TestUserAdmin(TestCaseWithTestData):
|
||||
username='superuser', password='secret', email='admin@example.com'
|
||||
)
|
||||
c = Client()
|
||||
c.login(username='superuser', password='secret')
|
||||
c.login(username='superuser', password='secret')
|
||||
response = c.get(get_admin_change_view_url(self.user_1))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@ -485,8 +485,8 @@ class TestMakeServicesHooksActions(TestCaseWithTestData):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'My Service A'
|
||||
|
||||
self.name = 'My Service A'
|
||||
|
||||
def update_groups(self, user):
|
||||
pass
|
||||
|
||||
@ -498,7 +498,7 @@ class TestMakeServicesHooksActions(TestCaseWithTestData):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'My Service B'
|
||||
|
||||
|
||||
def update_groups(self, user):
|
||||
pass
|
||||
|
||||
@ -510,32 +510,32 @@ class TestMakeServicesHooksActions(TestCaseWithTestData):
|
||||
|
||||
def sync_nicknames_bulk(self, user):
|
||||
pass
|
||||
|
||||
def test_service_has_update_groups_only(self):
|
||||
|
||||
def test_service_has_update_groups_only(self):
|
||||
service = self.MyServicesHookTypeA()
|
||||
mock_service = MagicMock(spec=service)
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_update_groups_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertTrue(mock_service.update_groups.called)
|
||||
|
||||
def test_service_has_update_groups_bulk(self):
|
||||
def test_service_has_update_groups_bulk(self):
|
||||
service = self.MyServicesHookTypeB()
|
||||
mock_service = MagicMock(spec=service)
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_update_groups_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertFalse(mock_service.update_groups.called)
|
||||
self.assertTrue(mock_service.update_groups_bulk.called)
|
||||
|
||||
def test_service_has_sync_nickname_only(self):
|
||||
def test_service_has_sync_nickname_only(self):
|
||||
service = self.MyServicesHookTypeA()
|
||||
mock_service = MagicMock(spec=service)
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_sync_nickname_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertTrue(mock_service.sync_nickname.called)
|
||||
|
||||
def test_service_has_sync_nicknames_bulk(self):
|
||||
def test_service_has_sync_nicknames_bulk(self):
|
||||
service = self.MyServicesHookTypeB()
|
||||
mock_service = MagicMock(spec=service)
|
||||
mock_service = MagicMock(spec=service)
|
||||
action = make_service_hooks_sync_nickname_action(mock_service)
|
||||
action(MagicMock(), MagicMock(), [self.user_1])
|
||||
self.assertFalse(mock_service.sync_nickname.called)
|
||||
|
@ -9,19 +9,19 @@ MODULE_PATH = 'allianceauth.authentication'
|
||||
class TestSetAppSetting(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.app_settings.settings')
|
||||
def test_default_if_not_set(self, mock_settings):
|
||||
def test_default_if_not_set(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
)
|
||||
self.assertEqual(result, False)
|
||||
|
||||
@patch(MODULE_PATH + '.app_settings.settings')
|
||||
def test_default_if_not_set_for_none(self, mock_settings):
|
||||
def test_default_if_not_set_for_none(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
None,
|
||||
required_type=int
|
||||
)
|
||||
@ -31,8 +31,8 @@ class TestSetAppSetting(TestCase):
|
||||
def test_true_stays_true(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = True
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
@ -40,7 +40,7 @@ class TestSetAppSetting(TestCase):
|
||||
def test_false_stays_false(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = False
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
False
|
||||
)
|
||||
self.assertEqual(result, False)
|
||||
@ -49,7 +49,7 @@ class TestSetAppSetting(TestCase):
|
||||
def test_default_for_invalid_type_bool(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
False
|
||||
)
|
||||
self.assertEqual(result, False)
|
||||
@ -58,7 +58,7 @@ class TestSetAppSetting(TestCase):
|
||||
def test_default_for_invalid_type_int(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
50
|
||||
)
|
||||
self.assertEqual(result, 50)
|
||||
@ -67,7 +67,7 @@ class TestSetAppSetting(TestCase):
|
||||
def test_default_if_below_minimum_1(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = -5
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=50
|
||||
)
|
||||
self.assertEqual(result, 50)
|
||||
@ -76,7 +76,7 @@ class TestSetAppSetting(TestCase):
|
||||
def test_default_if_below_minimum_2(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = -50
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=50,
|
||||
min_value=-10
|
||||
)
|
||||
@ -86,7 +86,7 @@ class TestSetAppSetting(TestCase):
|
||||
def test_default_for_invalid_type_int(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = 1000
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=50,
|
||||
max_value=100
|
||||
)
|
||||
@ -97,6 +97,6 @@ class TestSetAppSetting(TestCase):
|
||||
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||
with self.assertRaises(ValueError):
|
||||
result = app_settings._clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=None
|
||||
)
|
||||
|
@ -23,27 +23,27 @@ class TestStatePermissions(TestCase):
|
||||
self.permission_2 = AuthUtils.get_permission_by_name(PERMISSION_2)
|
||||
|
||||
# group
|
||||
self.group_1 = Group.objects.create(name="Group 1")
|
||||
self.group_1 = Group.objects.create(name="Group 1")
|
||||
self.group_2 = Group.objects.create(name="Group 2")
|
||||
|
||||
# state
|
||||
self.state_1 = AuthUtils.get_member_state()
|
||||
self.state_1 = AuthUtils.get_member_state()
|
||||
self.state_2 = AuthUtils.create_state("Other State", 75)
|
||||
|
||||
|
||||
# user
|
||||
self.user = AuthUtils.create_user("Bruce Wayne")
|
||||
self.main = AuthUtils.add_main_character_2(self.user, self.user.username, 123)
|
||||
|
||||
def test_user_has_user_permissions(self):
|
||||
self.user.user_permissions.add(self.permission_1)
|
||||
|
||||
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
def test_user_has_group_permissions(self):
|
||||
def test_user_has_group_permissions(self):
|
||||
self.group_1.permissions.add(self.permission_1)
|
||||
self.user.groups.add(self.group_1)
|
||||
|
||||
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
@ -55,7 +55,7 @@ class TestStatePermissions(TestCase):
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
|
||||
def test_when_user_changes_state_perms_change_accordingly(self):
|
||||
self.state_1.permissions.add(self.permission_1)
|
||||
self.state_1.permissions.add(self.permission_1)
|
||||
self.state_1.member_characters.add(self.main)
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
self.assertTrue(user.has_perm(PERMISSION_1))
|
||||
@ -68,16 +68,16 @@ class TestStatePermissions(TestCase):
|
||||
self.assertTrue(user.has_perm(PERMISSION_2))
|
||||
|
||||
def test_state_permissions_are_returned_for_current_user_object(self):
|
||||
# verify state permissions are returns for the current user object
|
||||
# and not for it's instance in the database, which might be outdated
|
||||
# verify state permissions are returns for the current user object
|
||||
# and not for it's instance in the database, which might be outdated
|
||||
self.state_1.permissions.add(self.permission_1)
|
||||
self.state_2.permissions.add(self.permission_2)
|
||||
self.state_2.permissions.add(self.permission_2)
|
||||
self.state_1.member_characters.add(self.main)
|
||||
user = User.objects.get(pk=self.user.pk)
|
||||
user.profile.state = self.state_2
|
||||
self.assertFalse(user.has_perm(PERMISSION_1))
|
||||
self.assertTrue(user.has_perm(PERMISSION_2))
|
||||
|
||||
|
||||
|
||||
class TestAuthenticate(TestCase):
|
||||
@classmethod
|
||||
|
@ -10,9 +10,9 @@ from django.test import TestCase
|
||||
from allianceauth.templatetags.admin_status import (
|
||||
status_overview,
|
||||
_fetch_list_from_gitlab,
|
||||
_current_notifications,
|
||||
_current_version_summary,
|
||||
_fetch_notification_issues_from_gitlab,
|
||||
_current_notifications,
|
||||
_current_version_summary,
|
||||
_fetch_notification_issues_from_gitlab,
|
||||
_latests_versions
|
||||
)
|
||||
|
||||
@ -58,10 +58,10 @@ class TestStatusOverviewTag(TestCase):
|
||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||
@patch(MODULE_PATH + '.admin_status._fetch_celery_queue_length')
|
||||
@patch(MODULE_PATH + '.admin_status._current_version_summary')
|
||||
@patch(MODULE_PATH + '.admin_status._current_notifications')
|
||||
@patch(MODULE_PATH + '.admin_status._current_notifications')
|
||||
def test_status_overview(
|
||||
self,
|
||||
mock_current_notifications,
|
||||
self,
|
||||
mock_current_notifications,
|
||||
mock_current_version_info,
|
||||
mock_fetch_celery_queue_length
|
||||
):
|
||||
@ -82,7 +82,7 @@ class TestStatusOverviewTag(TestCase):
|
||||
}
|
||||
mock_current_version_info.return_value = version_info
|
||||
mock_fetch_celery_queue_length.return_value = 3
|
||||
|
||||
|
||||
result = status_overview()
|
||||
expected = {
|
||||
'notifications': GITHUB_NOTIFICATION_ISSUES[:5],
|
||||
@ -111,7 +111,7 @@ class TestNotifications(TestCase):
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/issues'
|
||||
'?labels=announcement'
|
||||
)
|
||||
)
|
||||
requests_mocker.get(url, json=GITHUB_NOTIFICATION_ISSUES)
|
||||
# when
|
||||
result = _fetch_notification_issues_from_gitlab()
|
||||
@ -127,13 +127,13 @@ class TestNotifications(TestCase):
|
||||
# then
|
||||
self.assertEqual(result['notifications'], GITHUB_NOTIFICATION_ISSUES[:5])
|
||||
|
||||
@requests_mock.mock()
|
||||
@requests_mock.mock()
|
||||
def test_current_notifications_failed(self, requests_mocker):
|
||||
# given
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/issues'
|
||||
'?labels=announcement'
|
||||
)
|
||||
)
|
||||
requests_mocker.get(url, status_code=404)
|
||||
# when
|
||||
result = _current_notifications()
|
||||
@ -163,7 +163,7 @@ class TestVersionTags(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
|
||||
@patch(MODULE_PATH + '.admin_status.cache')
|
||||
def test_current_version_info_normal(self, mock_cache):
|
||||
def test_current_version_info_normal(self, mock_cache):
|
||||
# given
|
||||
mock_cache.get_or_set.return_value = GITHUB_TAGS
|
||||
# when
|
||||
@ -184,7 +184,7 @@ class TestVersionTags(TestCase):
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
|
||||
'/repository/tags'
|
||||
)
|
||||
)
|
||||
requests_mocker.get(url, status_code=500)
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
@ -197,7 +197,7 @@ class TestVersionTags(TestCase):
|
||||
url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
|
||||
'/repository/tags'
|
||||
)
|
||||
)
|
||||
requests_mocker.get(url, json=GITHUB_TAGS)
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
@ -208,7 +208,7 @@ class TestVersionTags(TestCase):
|
||||
@patch(MODULE_PATH + '.admin_status.cache')
|
||||
def test_current_version_info_return_no_data(self, mock_cache):
|
||||
# given
|
||||
mock_cache.get_or_set.return_value = None
|
||||
mock_cache.get_or_set.return_value = None
|
||||
# when
|
||||
result = _current_version_summary()
|
||||
# then
|
||||
@ -218,7 +218,7 @@ class TestVersionTags(TestCase):
|
||||
class TestLatestsVersion(TestCase):
|
||||
|
||||
def test_all_version_types_defined(self):
|
||||
|
||||
|
||||
tags = create_tags_list(
|
||||
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
|
||||
)
|
||||
@ -229,7 +229,7 @@ class TestLatestsVersion(TestCase):
|
||||
self.assertEqual(beta, Pep440Version('2.1.1a1'))
|
||||
|
||||
def test_major_and_minor_not_defined_with_zero(self):
|
||||
|
||||
|
||||
tags = create_tags_list(
|
||||
['2.1.2', '2.1.1', '2.0.1', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
|
||||
)
|
||||
@ -238,9 +238,9 @@ class TestLatestsVersion(TestCase):
|
||||
self.assertEqual(minor, Pep440Version('2.1.1'))
|
||||
self.assertEqual(patch, Pep440Version('2.1.2'))
|
||||
self.assertEqual(beta, Pep440Version('2.1.1a1'))
|
||||
|
||||
|
||||
def test_can_ignore_invalid_versions(self):
|
||||
|
||||
|
||||
tags = create_tags_list(
|
||||
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', 'invalid']
|
||||
)
|
||||
@ -252,9 +252,9 @@ class TestLatestsVersion(TestCase):
|
||||
|
||||
|
||||
class TestFetchListFromGitlab(TestCase):
|
||||
|
||||
|
||||
page_size = 2
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.url = (
|
||||
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
|
||||
@ -266,8 +266,8 @@ class TestFetchListFromGitlab(TestCase):
|
||||
page = int(request.qs['page'][0])
|
||||
start = (page - 1) * cls.page_size
|
||||
end = start + cls.page_size
|
||||
return GITHUB_TAGS[start:end]
|
||||
|
||||
return GITHUB_TAGS[start:end]
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_one_page_with_header(self, requests_mocker):
|
||||
headers = {
|
||||
@ -279,7 +279,7 @@ class TestFetchListFromGitlab(TestCase):
|
||||
self.assertEqual(requests_mocker.call_count, 1)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_one_page_wo_header(self, requests_mocker):
|
||||
def test_can_fetch_one_page_wo_header(self, requests_mocker):
|
||||
requests_mocker.get(self.url, json=GITHUB_TAGS)
|
||||
result = _fetch_list_from_gitlab(self.url)
|
||||
self.assertEqual(result, GITHUB_TAGS)
|
||||
@ -296,7 +296,7 @@ class TestFetchListFromGitlab(TestCase):
|
||||
self.assertEqual(requests_mocker.call_count, 1)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_multiple_pages(self, requests_mocker):
|
||||
def test_can_fetch_multiple_pages(self, requests_mocker):
|
||||
total_pages = ceil(len(GITHUB_TAGS) / self.page_size)
|
||||
headers = {
|
||||
'x-total-pages': str(total_pages)
|
||||
@ -307,7 +307,7 @@ class TestFetchListFromGitlab(TestCase):
|
||||
self.assertEqual(requests_mocker.call_count, total_pages)
|
||||
|
||||
@requests_mock.mock()
|
||||
def test_can_fetch_given_number_of_pages_only(self, requests_mocker):
|
||||
def test_can_fetch_given_number_of_pages_only(self, requests_mocker):
|
||||
total_pages = ceil(len(GITHUB_TAGS) / self.page_size)
|
||||
headers = {
|
||||
'x-total-pages': str(total_pages)
|
||||
|
@ -9,8 +9,8 @@ app_name = 'authentication'
|
||||
urlpatterns = [
|
||||
url(r'^$', views.index, name='index'),
|
||||
url(
|
||||
r'^account/login/$',
|
||||
TemplateView.as_view(template_name='public/login.html'),
|
||||
r'^account/login/$',
|
||||
TemplateView.as_view(template_name='public/login.html'),
|
||||
name='login'
|
||||
),
|
||||
url(
|
||||
@ -19,9 +19,9 @@ urlpatterns = [
|
||||
name='change_main_character'
|
||||
),
|
||||
url(
|
||||
r'^account/characters/add/$',
|
||||
views.add_character,
|
||||
r'^account/characters/add/$',
|
||||
views.add_character,
|
||||
name='add_character'
|
||||
),
|
||||
),
|
||||
url(r'^dashboard/$', views.dashboard, name='dashboard'),
|
||||
]
|
||||
|
@ -16,8 +16,8 @@ from esi.decorators import token_required
|
||||
from esi.models import Token
|
||||
|
||||
from django_registration.backends.activation.views import (
|
||||
RegistrationView as BaseRegistrationView,
|
||||
ActivationView as BaseActivationView,
|
||||
RegistrationView as BaseRegistrationView,
|
||||
ActivationView as BaseActivationView,
|
||||
REGISTRATION_SALT
|
||||
)
|
||||
from django_registration.signals import user_registered
|
||||
@ -52,7 +52,7 @@ def dashboard(request):
|
||||
.filter(character_ownership__user=request.user)\
|
||||
.select_related()\
|
||||
.order_by('character_name')
|
||||
|
||||
|
||||
context = {
|
||||
'groups': groups,
|
||||
'characters': characters
|
||||
@ -71,7 +71,7 @@ def main_character_change(request, token):
|
||||
co = CharacterOwnership.objects.create_by_token(token)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
request,
|
||||
_('Cannot change main character to %(char)s: character owned by a different account.') % ({'char': token.character_name})
|
||||
)
|
||||
co = None
|
||||
@ -138,7 +138,7 @@ class RegistrationView(BaseRegistrationView):
|
||||
template_name = "public/register.html"
|
||||
email_body_template = "registration/activation_email.txt"
|
||||
email_subject_template = "registration/activation_email_subject.txt"
|
||||
success_url = reverse_lazy('registration_complete')
|
||||
success_url = reverse_lazy('registration_complete')
|
||||
|
||||
def get_success_url(self, user):
|
||||
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
|
||||
@ -181,8 +181,8 @@ class RegistrationView(BaseRegistrationView):
|
||||
# Step 3
|
||||
class ActivationView(BaseActivationView):
|
||||
template_name = "registration/activate.html"
|
||||
success_url = reverse_lazy('registration_activation_complete')
|
||||
|
||||
success_url = reverse_lazy('registration_activation_complete')
|
||||
|
||||
def validate_key(self, activation_key):
|
||||
try:
|
||||
dump = signing.loads(activation_key, salt=REGISTRATION_SALT,
|
||||
|
@ -37,7 +37,7 @@ class CorpStatsQuerySet(models.QuerySet):
|
||||
return self.filter(query)
|
||||
except AssertionError:
|
||||
logger.debug('User %s has no main character. No corpstats visible.' % user)
|
||||
return self.none()
|
||||
return self.none()
|
||||
|
||||
|
||||
class CorpStatsManager(models.Manager):
|
||||
|
@ -97,7 +97,7 @@ class EveAllianceForm(EveEntityForm):
|
||||
@admin.register(EveCorporationInfo)
|
||||
class EveCorporationInfoAdmin(admin.ModelAdmin):
|
||||
search_fields = ['corporation_name']
|
||||
list_display = ('corporation_name', 'alliance')
|
||||
list_display = ('corporation_name', 'alliance')
|
||||
list_select_related = ('alliance',)
|
||||
list_filter = (('alliance', admin.RelatedOnlyFieldListFilter),)
|
||||
ordering = ('corporation_name',)
|
||||
@ -114,9 +114,9 @@ class EveCorporationInfoAdmin(admin.ModelAdmin):
|
||||
@admin.register(EveAllianceInfo)
|
||||
class EveAllianceInfoAdmin(admin.ModelAdmin):
|
||||
search_fields = ['alliance_name']
|
||||
list_display = ('alliance_name',)
|
||||
list_display = ('alliance_name',)
|
||||
ordering = ('alliance_name',)
|
||||
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
@ -129,9 +129,9 @@ class EveAllianceInfoAdmin(admin.ModelAdmin):
|
||||
@admin.register(EveCharacter)
|
||||
class EveCharacterAdmin(admin.ModelAdmin):
|
||||
search_fields = [
|
||||
'character_name',
|
||||
'corporation_name',
|
||||
'alliance_name',
|
||||
'character_name',
|
||||
'corporation_name',
|
||||
'alliance_name',
|
||||
'character_ownership__user__username'
|
||||
]
|
||||
list_display = (
|
||||
@ -141,10 +141,10 @@ class EveCharacterAdmin(admin.ModelAdmin):
|
||||
'character_ownership', 'character_ownership__user__profile__main_character'
|
||||
)
|
||||
list_filter = (
|
||||
'corporation_name',
|
||||
'alliance_name',
|
||||
'corporation_name',
|
||||
'alliance_name',
|
||||
(
|
||||
'character_ownership__user__profile__main_character',
|
||||
'character_ownership__user__profile__main_character',
|
||||
admin.RelatedOnlyFieldListFilter
|
||||
),
|
||||
)
|
||||
|
@ -44,7 +44,7 @@ class AutogroupsConfigManagerTestCase(TestCase):
|
||||
with patch('.models.AutogroupsConfig.update_group_membership_for_user') \
|
||||
as update_group_membership_for_user:
|
||||
AutogroupsConfig.objects.update_groups_for_user(
|
||||
user=member,
|
||||
user=member,
|
||||
state=member.profile.state
|
||||
)
|
||||
|
||||
|
@ -52,8 +52,8 @@ class AutogroupsConfigTestCase(TestCase):
|
||||
@patch('.models.AutogroupsConfig.update_alliance_group_membership')
|
||||
@patch('.models.AutogroupsConfig.update_corp_group_membership')
|
||||
def test_update_group_membership_for_user(
|
||||
self,
|
||||
update_corp,
|
||||
self,
|
||||
update_corp,
|
||||
update_alliance
|
||||
):
|
||||
agc = AutogroupsConfig.objects.create()
|
||||
@ -123,9 +123,9 @@ class AutogroupsConfigTestCase(TestCase):
|
||||
alliance_ticker='alliance_ticker',
|
||||
executor_corp_id='2345'
|
||||
)
|
||||
|
||||
|
||||
mock_create_alliance.side_effect = mock_create_alliance_side_effect
|
||||
|
||||
|
||||
obj = AutogroupsConfig.objects.create(alliance_groups=True)
|
||||
obj.states.add(AuthUtils.get_member_state())
|
||||
char = EveCharacter.objects.create(
|
||||
@ -140,7 +140,7 @@ class AutogroupsConfigTestCase(TestCase):
|
||||
self.member.profile.main_character = char
|
||||
self.member.profile.save()
|
||||
|
||||
# Act
|
||||
# Act
|
||||
obj.update_alliance_group_membership(self.member)
|
||||
|
||||
group = obj.get_alliance_group(self.alliance)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# this package generates profile URL for eve entities
|
||||
# this package generates profile URL for eve entities
|
||||
# on 3rd party websites like evewho and zKillboard
|
||||
#
|
||||
# It contains of modules for views and templatetags for templates
|
||||
|
@ -3,9 +3,9 @@
|
||||
from urllib.parse import urljoin, quote
|
||||
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_REGION,
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_REGION,
|
||||
_ESI_CATEGORY_SOLARSYSTEM
|
||||
)
|
||||
|
||||
@ -15,28 +15,28 @@ _BASE_URL = 'http://evemaps.dotlan.net'
|
||||
|
||||
def _build_url(category: str, name: str) -> str:
|
||||
"""return url to profile page for an eve entity"""
|
||||
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
partial = 'alliance'
|
||||
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
partial = 'corp'
|
||||
|
||||
elif category == _ESI_CATEGORY_REGION:
|
||||
partial = 'map'
|
||||
|
||||
|
||||
elif category == _ESI_CATEGORY_SOLARSYSTEM:
|
||||
partial = 'system'
|
||||
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Not implemented yet for category:" + category
|
||||
)
|
||||
|
||||
|
||||
url = urljoin(
|
||||
_BASE_URL,
|
||||
'{}/{}'.format(partial, quote(str(name).replace(" ", "_")))
|
||||
|
||||
|
||||
)
|
||||
return url
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_INVENTORYTYPE
|
||||
)
|
||||
|
||||
@ -10,7 +10,7 @@ _EVE_IMAGE_SERVER_URL = 'https://images.evetech.net'
|
||||
_DEFAULT_IMAGE_SIZE = 32
|
||||
|
||||
|
||||
def _eve_entity_image_url(
|
||||
def _eve_entity_image_url(
|
||||
category: str,
|
||||
entity_id: int,
|
||||
size: int = 32,
|
||||
@ -19,7 +19,7 @@ def _eve_entity_image_url(
|
||||
) -> str:
|
||||
"""returns image URL for an Eve Online ID.
|
||||
Supported categories: alliance, corporation, character, inventory_type
|
||||
|
||||
|
||||
Arguments:
|
||||
- category: category of the ID, see ESI category constants
|
||||
- entity_id: Eve ID of the entity
|
||||
@ -33,7 +33,7 @@ def _eve_entity_image_url(
|
||||
Exceptions:
|
||||
- Throws ValueError on invalid input
|
||||
"""
|
||||
|
||||
|
||||
# input validations
|
||||
categories = {
|
||||
_ESI_CATEGORY_ALLIANCE: {
|
||||
@ -54,15 +54,15 @@ def _eve_entity_image_url(
|
||||
}
|
||||
}
|
||||
tenants = ['tranquility', 'singularity']
|
||||
|
||||
|
||||
if not entity_id:
|
||||
raise ValueError('Invalid entity_id: {}'.format(entity_id))
|
||||
else:
|
||||
entity_id = int(entity_id)
|
||||
|
||||
|
||||
if not size or size < 32 or size > 1024 or (size & (size - 1) != 0):
|
||||
raise ValueError('Invalid size: {}'.format(size))
|
||||
|
||||
|
||||
if category not in categories:
|
||||
raise ValueError('Invalid category {}'.format(category))
|
||||
else:
|
||||
@ -79,7 +79,7 @@ def _eve_entity_image_url(
|
||||
|
||||
if tenant and tenant not in tenants:
|
||||
raise ValueError('Invalid tenant {}'.format(tenant))
|
||||
|
||||
|
||||
# compose result URL
|
||||
result = '{}/{}/{}/{}?size={}'.format(
|
||||
_EVE_IMAGE_SERVER_URL,
|
||||
@ -90,7 +90,7 @@ def _eve_entity_image_url(
|
||||
)
|
||||
if tenant:
|
||||
result += '&tenant={}'.format(tenant)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
)
|
||||
|
||||
|
||||
@ -14,21 +14,21 @@ _BASE_URL = 'https://evewho.com'
|
||||
|
||||
def _build_url(category: str, eve_id: int) -> str:
|
||||
"""return url to profile page for an eve entity"""
|
||||
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
partial = 'alliance'
|
||||
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
partial = 'corporation'
|
||||
|
||||
elif category == _ESI_CATEGORY_CHARACTER:
|
||||
partial = 'character'
|
||||
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Not implemented yet for category:" + category
|
||||
)
|
||||
|
||||
|
||||
url = urljoin(
|
||||
_BASE_URL,
|
||||
'{}/{}'.format(partial, int(eve_id))
|
||||
|
@ -12,12 +12,12 @@ class TestEveWho(TestCase):
|
||||
evewho.alliance_url(12345678),
|
||||
'https://evewho.com/alliance/12345678'
|
||||
)
|
||||
|
||||
|
||||
def test_corporation_url(self):
|
||||
self.assertEqual(
|
||||
evewho.corporation_url(12345678),
|
||||
'https://evewho.com/corporation/12345678'
|
||||
)
|
||||
)
|
||||
|
||||
def test_character_url(self):
|
||||
self.assertEqual(
|
||||
@ -49,7 +49,7 @@ class TestDotlan(TestCase):
|
||||
dotlan.region_url('Black Rise'),
|
||||
'http://evemaps.dotlan.net/map/Black_Rise'
|
||||
)
|
||||
|
||||
|
||||
def test_solar_system_url(self):
|
||||
self.assertEqual(
|
||||
dotlan.solar_system_url('Jita'),
|
||||
@ -69,14 +69,14 @@ class TestZkillboard(TestCase):
|
||||
self.assertEqual(
|
||||
zkillboard.corporation_url(12345678),
|
||||
'https://zkillboard.com/corporation/12345678/'
|
||||
)
|
||||
)
|
||||
|
||||
def test_character_url(self):
|
||||
self.assertEqual(
|
||||
zkillboard.character_url(12345678),
|
||||
'https://zkillboard.com/character/12345678/'
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_region_url(self):
|
||||
self.assertEqual(
|
||||
@ -93,34 +93,34 @@ class TestZkillboard(TestCase):
|
||||
|
||||
class TestEveImageServer(TestCase):
|
||||
"""unit test for eveimageserver"""
|
||||
|
||||
def test_sizes(self):
|
||||
|
||||
def test_sizes(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42),
|
||||
eveimageserver._eve_entity_image_url('character', 42),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=32),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=32),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=64),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=64),
|
||||
'https://images.evetech.net/characters/42/portrait?size=64'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=128),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=128),
|
||||
'https://images.evetech.net/characters/42/portrait?size=128'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=256),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=256),
|
||||
'https://images.evetech.net/characters/42/portrait?size=256'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=512),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=512),
|
||||
'https://images.evetech.net/characters/42/portrait?size=512'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=1024),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=1024),
|
||||
'https://images.evetech.net/characters/42/portrait?size=1024'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
@ -128,10 +128,10 @@ class TestEveImageServer(TestCase):
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=0)
|
||||
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=31)
|
||||
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, size=1025)
|
||||
|
||||
@ -141,28 +141,28 @@ class TestEveImageServer(TestCase):
|
||||
|
||||
def test_variant(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, variant='portrait'),
|
||||
eveimageserver._eve_entity_image_url('character', 42, variant='portrait'),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('alliance', 42, variant='logo'),
|
||||
eveimageserver._eve_entity_image_url('alliance', 42, variant='logo'),
|
||||
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('character', 42, variant='logo')
|
||||
|
||||
|
||||
|
||||
def test_alliance(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('alliance', 42),
|
||||
eveimageserver._eve_entity_image_url('alliance', 42),
|
||||
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('corporation', 42),
|
||||
eveimageserver._eve_entity_image_url('corporation', 42),
|
||||
'https://images.evetech.net/corporations/42/logo?size=32'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42),
|
||||
eveimageserver._eve_entity_image_url('character', 42),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
@ -171,16 +171,16 @@ class TestEveImageServer(TestCase):
|
||||
|
||||
def test_tenants(self):
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='tranquility'),
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='tranquility'),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32&tenant=tranquility'
|
||||
)
|
||||
self.assertEqual(
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='singularity'),
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='singularity'),
|
||||
'https://images.evetech.net/characters/42/portrait?size=32&tenant=singularity'
|
||||
)
|
||||
with self.assertRaises(ValueError):
|
||||
eveimageserver._eve_entity_image_url('character', 42, tenant='xxx')
|
||||
|
||||
|
||||
def test_alliance_logo_url(self):
|
||||
expected = 'https://images.evetech.net/alliances/42/logo?size=128'
|
||||
self.assertEqual(eveimageserver.alliance_logo_url(42, 128), expected)
|
||||
|
@ -38,15 +38,15 @@ class TestTemplateTags(TestCase):
|
||||
member_count=42,
|
||||
alliance=self.my_alliance
|
||||
)
|
||||
|
||||
|
||||
self.my_region_id = 8001
|
||||
self.my_region_name = 'Southpark'
|
||||
|
||||
|
||||
self.my_solar_system_id = 9001
|
||||
self.my_solar_system_name = 'Gotham'
|
||||
|
||||
|
||||
def test_evewho_character_url(self):
|
||||
|
||||
def test_evewho_character_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.evewho_character_url(self.my_character),
|
||||
evewho.character_url(self.my_character.character_id),
|
||||
@ -59,9 +59,9 @@ class TestTemplateTags(TestCase):
|
||||
evelinks.evewho_character_url(self.my_character.character_id),
|
||||
evewho.character_url(self.my_character.character_id),
|
||||
)
|
||||
|
||||
|
||||
def test_evewho_corporation_url(self):
|
||||
|
||||
|
||||
def test_evewho_corporation_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.evewho_corporation_url(self.my_character),
|
||||
evewho.corporation_url(self.my_character.corporation_id),
|
||||
@ -80,7 +80,7 @@ class TestTemplateTags(TestCase):
|
||||
)
|
||||
|
||||
|
||||
def test_evewho_alliance_url(self):
|
||||
def test_evewho_alliance_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.evewho_alliance_url(self.my_character),
|
||||
evewho.alliance_url(self.my_character.alliance_id),
|
||||
@ -100,12 +100,12 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.evewho_alliance_url(self.my_character.alliance_id),
|
||||
evewho.alliance_url(self.my_character.alliance_id),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
# dotlan
|
||||
|
||||
def test_dotlan_corporation_url(self):
|
||||
|
||||
def test_dotlan_corporation_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_corporation_url(self.my_character),
|
||||
dotlan.corporation_url(self.my_character.corporation_name),
|
||||
@ -121,10 +121,10 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_corporation_url(self.my_character.corporation_name),
|
||||
dotlan.corporation_url(self.my_character.corporation_name),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_dotlan_alliance_url(self):
|
||||
def test_dotlan_alliance_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_alliance_url(self.my_character),
|
||||
dotlan.alliance_url(self.my_character.alliance_name),
|
||||
@ -144,32 +144,32 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_alliance_url(self.my_character.alliance_name),
|
||||
dotlan.alliance_url(self.my_character.alliance_name),
|
||||
)
|
||||
)
|
||||
|
||||
def test_dotlan_region_url(self):
|
||||
def test_dotlan_region_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_region_url(self.my_region_name),
|
||||
dotlan.region_url(self.my_region_name),
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_region_url(None),
|
||||
''
|
||||
)
|
||||
)
|
||||
|
||||
def test_dotlan_solar_system_url(self):
|
||||
def test_dotlan_solar_system_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_solar_system_url(self.my_solar_system_name),
|
||||
dotlan.solar_system_url(self.my_solar_system_name),
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.dotlan_solar_system_url(None),
|
||||
''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
# zkillboard
|
||||
|
||||
def test_zkillboard_character_url(self):
|
||||
def test_zkillboard_character_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_character_url(self.my_character),
|
||||
zkillboard.character_url(self.my_character.character_id),
|
||||
@ -182,9 +182,9 @@ class TestTemplateTags(TestCase):
|
||||
evelinks.zkillboard_character_url(self.my_character.character_id),
|
||||
zkillboard.character_url(self.my_character.character_id),
|
||||
)
|
||||
|
||||
|
||||
def test_zkillboard_corporation_url(self):
|
||||
|
||||
|
||||
def test_zkillboard_corporation_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_corporation_url(self.my_character),
|
||||
zkillboard.corporation_url(self.my_character.corporation_id),
|
||||
@ -200,10 +200,10 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_corporation_url(self.my_character.corporation_id),
|
||||
zkillboard.corporation_url(self.my_character.corporation_id),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_zkillboard_alliance_url(self):
|
||||
def test_zkillboard_alliance_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_alliance_url(self.my_character),
|
||||
zkillboard.alliance_url(self.my_character.alliance_id),
|
||||
@ -223,29 +223,29 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_alliance_url(self.my_character.alliance_id),
|
||||
zkillboard.alliance_url(self.my_character.alliance_id),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_zkillboard_region_url(self):
|
||||
def test_zkillboard_region_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_region_url(self.my_region_id),
|
||||
zkillboard.region_url(self.my_region_id),
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_region_url(None),
|
||||
''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_zkillboard_solar_system_url(self):
|
||||
def test_zkillboard_solar_system_url(self):
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_solar_system_url(self.my_solar_system_id),
|
||||
zkillboard.solar_system_url(self.my_solar_system_id),
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.zkillboard_solar_system_url(None),
|
||||
''
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# image URLs
|
||||
@ -254,12 +254,12 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.character_portrait_url(123),
|
||||
EveCharacter.generic_portrait_url(123)
|
||||
|
||||
|
||||
),
|
||||
self.assertEqual(
|
||||
evelinks.character_portrait_url(123, 128),
|
||||
EveCharacter.generic_portrait_url(123, 128)
|
||||
|
||||
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.character_portrait_url(123, 99),
|
||||
@ -267,7 +267,7 @@ class TestTemplateTags(TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.character_portrait_url(self.my_character),
|
||||
self.my_character.portrait_url()
|
||||
self.my_character.portrait_url()
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.character_portrait_url(None),
|
||||
@ -286,7 +286,7 @@ class TestTemplateTags(TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.corporation_logo_url(123, 99),
|
||||
''
|
||||
''
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.corporation_logo_url(self.my_corporation),
|
||||
@ -303,7 +303,7 @@ class TestTemplateTags(TestCase):
|
||||
|
||||
|
||||
def test_alliance_logo_url(self):
|
||||
self.assertEqual(
|
||||
self.assertEqual(
|
||||
evelinks.alliance_logo_url(123),
|
||||
EveAllianceInfo.generic_logo_url(123)
|
||||
),
|
||||
@ -314,7 +314,7 @@ class TestTemplateTags(TestCase):
|
||||
self.assertEqual(
|
||||
evelinks.alliance_logo_url(123, 99),
|
||||
''
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
evelinks.alliance_logo_url(self.my_alliance),
|
||||
self.my_alliance.logo_url()
|
||||
@ -338,10 +338,10 @@ class TestTemplateTags(TestCase):
|
||||
|
||||
expected = eveimageserver.type_icon_url(123, 128)
|
||||
self.assertEqual(evelinks.type_icon_url(123, 128), expected)
|
||||
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_icon_url(123, 99), expected)
|
||||
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_icon_url(None), expected)
|
||||
|
||||
@ -351,9 +351,9 @@ class TestTemplateTags(TestCase):
|
||||
|
||||
expected = eveimageserver.type_render_url(123, 128)
|
||||
self.assertEqual(evelinks.type_render_url(123, 128), expected)
|
||||
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_render_url(123, 99), expected)
|
||||
|
||||
|
||||
expected = ''
|
||||
self.assertEqual(evelinks.type_render_url(None), expected)
|
@ -3,10 +3,10 @@
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from . import (
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_ALLIANCE,
|
||||
_ESI_CATEGORY_CORPORATION,
|
||||
_ESI_CATEGORY_CHARACTER,
|
||||
_ESI_CATEGORY_REGION,
|
||||
_ESI_CATEGORY_REGION,
|
||||
_ESI_CATEGORY_SOLARSYSTEM
|
||||
)
|
||||
|
||||
@ -16,11 +16,11 @@ _BASE_URL = 'https://zkillboard.com'
|
||||
|
||||
def _build_url(category: str, eve_id: int) -> str:
|
||||
"""return url to profile page for an eve entity"""
|
||||
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
|
||||
if category == _ESI_CATEGORY_ALLIANCE:
|
||||
partial = 'alliance'
|
||||
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
elif category == _ESI_CATEGORY_CORPORATION:
|
||||
partial = 'corporation'
|
||||
|
||||
elif category == _ESI_CATEGORY_CHARACTER:
|
||||
@ -31,12 +31,12 @@ def _build_url(category: str, eve_id: int) -> str:
|
||||
|
||||
elif category == _ESI_CATEGORY_SOLARSYSTEM:
|
||||
partial = 'system'
|
||||
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Not implemented yet for category:" + category
|
||||
)
|
||||
|
||||
|
||||
url = urljoin(
|
||||
_BASE_URL,
|
||||
'{}/{}/'.format(partial, int(eve_id))
|
||||
|
@ -51,7 +51,7 @@ class EveAllianceInfo(models.Model):
|
||||
) -> str:
|
||||
"""image URL for the given alliance ID"""
|
||||
return eveimageserver.alliance_logo_url(alliance_id, size)
|
||||
|
||||
|
||||
def logo_url(self, size: int = _DEFAULT_IMAGE_SIZE) -> str:
|
||||
"""image URL of this alliance"""
|
||||
return self.generic_logo_url(self.alliance_id, size)
|
||||
@ -224,7 +224,7 @@ class EveCharacter(models.Model):
|
||||
def portrait_url_128(self) -> str:
|
||||
"""image URL for this character"""
|
||||
return self.portrait_url(128)
|
||||
|
||||
|
||||
@property
|
||||
def portrait_url_256(self) -> str:
|
||||
"""image URL for this character"""
|
||||
@ -275,7 +275,7 @@ class EveCharacter(models.Model):
|
||||
def alliance_logo_url_128(self) -> str:
|
||||
"""image URL for alliance of this character or empty string"""
|
||||
return self.alliance_logo_url(128)
|
||||
|
||||
|
||||
@property
|
||||
def alliance_logo_url_256(self) -> str:
|
||||
"""image URL for alliance of this character or empty string"""
|
||||
|
@ -159,7 +159,7 @@ class EveProvider(object):
|
||||
|
||||
|
||||
class EveSwaggerProvider(EveProvider):
|
||||
def __init__(self, token=None, adapter=None):
|
||||
def __init__(self, token=None, adapter=None):
|
||||
if settings.DEBUG:
|
||||
self._client = None
|
||||
logger.info(
|
||||
|
@ -40,7 +40,7 @@ def update_character(character_id):
|
||||
@shared_task
|
||||
def run_model_update():
|
||||
"""Update all alliances, corporations and characters from ESI"""
|
||||
|
||||
|
||||
# update existing corp models
|
||||
for corp in EveCorporationInfo.objects.all().values('corporation_id'):
|
||||
update_corp.apply_async(
|
||||
@ -54,7 +54,7 @@ def run_model_update():
|
||||
)
|
||||
|
||||
# update existing character models
|
||||
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
||||
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
|
||||
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
|
||||
affiliations_raw = providers.provider.client.Character\
|
||||
.post_characters_affiliation(characters=character_ids_chunk).result()
|
||||
@ -62,39 +62,39 @@ def run_model_update():
|
||||
.post_universe_names(ids=character_ids_chunk).result()
|
||||
|
||||
affiliations = {
|
||||
affiliation.get('character_id'): affiliation
|
||||
affiliation.get('character_id'): affiliation
|
||||
for affiliation in affiliations_raw
|
||||
}
|
||||
# add character names to affiliations
|
||||
for character in character_names:
|
||||
for character in character_names:
|
||||
character_id = character.get('id')
|
||||
if character_id in affiliations:
|
||||
affiliations[character_id]['name'] = character.get('name')
|
||||
|
||||
# fetch current characters
|
||||
characters = EveCharacter.objects.filter(character_id__in=character_ids_chunk)\
|
||||
.values('character_id', 'corporation_id', 'alliance_id', 'character_name')
|
||||
|
||||
.values('character_id', 'corporation_id', 'alliance_id', 'character_name')
|
||||
|
||||
for character in characters:
|
||||
character_id = character.get('character_id')
|
||||
if character_id in affiliations:
|
||||
affiliation = affiliations[character_id]
|
||||
|
||||
|
||||
corp_changed = (
|
||||
character.get('corporation_id') != affiliation.get('corporation_id')
|
||||
)
|
||||
|
||||
|
||||
alliance_id = character.get('alliance_id')
|
||||
if not alliance_id:
|
||||
alliance_id = None
|
||||
alliance_changed = alliance_id != affiliation.get('alliance_id')
|
||||
|
||||
|
||||
name_changed = False
|
||||
fetched_name = affiliation.get('name', False)
|
||||
if fetched_name:
|
||||
name_changed = character.get('character_name') != fetched_name
|
||||
|
||||
if corp_changed or alliance_changed or name_changed:
|
||||
|
||||
if corp_changed or alliance_changed or name_changed:
|
||||
update_character.apply_async(
|
||||
args=[character.get('character_id')], priority=TASK_PRIORITY
|
||||
)
|
||||
|
@ -1,14 +1,14 @@
|
||||
# This module defines template tags for evelinks URLs and eve image URLs
|
||||
#
|
||||
#
|
||||
# Many tags will work both with their respective eveonline object
|
||||
# and their respective eve entity ID
|
||||
#
|
||||
#
|
||||
# Example:
|
||||
# character URL on evewho: {{ my_character|evewho_character_url}}
|
||||
# character URL on evewho: {{ 1456384556|evewho_character_url}}
|
||||
#
|
||||
#
|
||||
# For more examples see examples.html
|
||||
#
|
||||
#
|
||||
# To add templatetags for additional providers just add the respective
|
||||
# template functions and let them call the generic functions
|
||||
|
||||
@ -25,59 +25,59 @@ _DEFAULT_IMAGE_SIZE = 32
|
||||
# generic functions
|
||||
|
||||
def _generic_character_url(
|
||||
provider: object,
|
||||
obj_prop: str,
|
||||
provider: object,
|
||||
obj_prop: str,
|
||||
eve_obj: EveCharacter
|
||||
) -> str:
|
||||
"""returns character URL for given provider and object"""
|
||||
my_func = getattr(provider, 'character_url')
|
||||
if isinstance(eve_obj, EveCharacter):
|
||||
if isinstance(eve_obj, EveCharacter):
|
||||
return my_func(getattr(eve_obj, obj_prop))
|
||||
|
||||
|
||||
elif eve_obj is None:
|
||||
return ''
|
||||
|
||||
else:
|
||||
return my_func(eve_obj)
|
||||
|
||||
|
||||
|
||||
def _generic_corporation_url(
|
||||
provider: object,
|
||||
obj_prop: str,
|
||||
provider: object,
|
||||
obj_prop: str,
|
||||
eve_obj: object
|
||||
) -> str:
|
||||
"""returns corporation URL for given provider and object"""
|
||||
my_func = getattr(provider, 'corporation_url')
|
||||
if isinstance(eve_obj, (EveCharacter, EveCorporationInfo)):
|
||||
if isinstance(eve_obj, (EveCharacter, EveCorporationInfo)):
|
||||
return my_func(getattr(eve_obj, obj_prop))
|
||||
|
||||
elif eve_obj is None:
|
||||
return ''
|
||||
|
||||
|
||||
else:
|
||||
return my_func(eve_obj)
|
||||
|
||||
|
||||
|
||||
def _generic_alliance_url(
|
||||
provider: object,
|
||||
provider: object,
|
||||
obj_prop: str,
|
||||
eve_obj: object
|
||||
) -> str:
|
||||
"""returns alliance URL for given provider and object"""
|
||||
my_func = getattr(provider, 'alliance_url')
|
||||
|
||||
|
||||
if isinstance(eve_obj, EveCharacter):
|
||||
if eve_obj.alliance_id:
|
||||
if eve_obj.alliance_id:
|
||||
return my_func(getattr(eve_obj, obj_prop))
|
||||
else:
|
||||
return ''
|
||||
|
||||
elif isinstance(eve_obj, EveAllianceInfo):
|
||||
|
||||
elif isinstance(eve_obj, EveAllianceInfo):
|
||||
return my_func(getattr(eve_obj, obj_prop))
|
||||
|
||||
elif eve_obj is None:
|
||||
return ''
|
||||
|
||||
|
||||
else:
|
||||
return my_func(eve_obj)
|
||||
|
||||
@ -91,7 +91,7 @@ def _generic_evelinks_url(
|
||||
my_func = getattr(provider, provider_func)
|
||||
if eve_obj is None:
|
||||
return ''
|
||||
|
||||
|
||||
else:
|
||||
return my_func(eve_obj)
|
||||
|
||||
@ -99,29 +99,29 @@ def _generic_evelinks_url(
|
||||
# evewho
|
||||
|
||||
@register.filter
|
||||
def evewho_character_url(eve_obj: EveCharacter) -> str:
|
||||
def evewho_character_url(eve_obj: EveCharacter) -> str:
|
||||
"""generates an evewho URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_character_url(evewho, 'character_id', eve_obj)
|
||||
|
||||
|
||||
|
||||
@register.filter
|
||||
def evewho_corporation_url(eve_obj: object) -> str:
|
||||
"""generates an evewho URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_corporation_url(evewho, 'corporation_id', eve_obj)
|
||||
|
||||
|
||||
|
||||
@register.filter
|
||||
def evewho_alliance_url(eve_obj: object) -> str:
|
||||
def evewho_alliance_url(eve_obj: object) -> str:
|
||||
"""generates an evewho URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_alliance_url(evewho, 'alliance_id', eve_obj)
|
||||
|
||||
|
||||
@ -131,69 +131,69 @@ def evewho_alliance_url(eve_obj: object) -> str:
|
||||
def dotlan_corporation_url(eve_obj: object) -> str:
|
||||
"""generates a dotlan URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity names
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_corporation_url(dotlan, 'corporation_name', eve_obj)
|
||||
|
||||
|
||||
|
||||
@register.filter
|
||||
def dotlan_alliance_url(eve_obj: object) -> str:
|
||||
def dotlan_alliance_url(eve_obj: object) -> str:
|
||||
"""generates a dotlan URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity names
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_alliance_url(dotlan, 'alliance_name', eve_obj)
|
||||
|
||||
|
||||
@register.filter
|
||||
def dotlan_region_url(eve_obj: object) -> str:
|
||||
def dotlan_region_url(eve_obj: object) -> str:
|
||||
"""generates a dotlan URL for the given object
|
||||
Works with eve entity names
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_evelinks_url(dotlan, 'region_url', eve_obj)
|
||||
|
||||
|
||||
@register.filter
|
||||
def dotlan_solar_system_url(eve_obj: object) -> str:
|
||||
def dotlan_solar_system_url(eve_obj: object) -> str:
|
||||
"""generates a dotlan URL for the given object
|
||||
Works with eve entity names
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_evelinks_url(dotlan, 'solar_system_url', eve_obj)
|
||||
|
||||
|
||||
# zkillboard
|
||||
|
||||
@register.filter
|
||||
def zkillboard_character_url(eve_obj: EveCharacter) -> str:
|
||||
def zkillboard_character_url(eve_obj: EveCharacter) -> str:
|
||||
"""generates a zkillboard URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_character_url(zkillboard, 'character_id', eve_obj)
|
||||
|
||||
|
||||
|
||||
@register.filter
|
||||
def zkillboard_corporation_url(eve_obj: object) -> str:
|
||||
"""generates a zkillboard URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity IDs
|
||||
Returns URL or empty string
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_corporation_url(zkillboard, 'corporation_id', eve_obj)
|
||||
|
||||
|
||||
|
||||
@register.filter
|
||||
def zkillboard_alliance_url(eve_obj: object) -> str:
|
||||
def zkillboard_alliance_url(eve_obj: object) -> str:
|
||||
"""generates a zkillboard URL for the given object
|
||||
Works with allianceauth.eveonline objects and eve entity IDs
|
||||
Returns URL or empty string
|
||||
Returns URL or empty string
|
||||
"""
|
||||
return _generic_alliance_url(zkillboard, 'alliance_id', eve_obj)
|
||||
|
||||
|
||||
@register.filter
|
||||
def zkillboard_region_url(eve_obj: object) -> str:
|
||||
def zkillboard_region_url(eve_obj: object) -> str:
|
||||
"""generates a zkillboard URL for the given object
|
||||
Works with eve entity IDs
|
||||
Returns URL or empty string
|
||||
@ -202,7 +202,7 @@ def zkillboard_region_url(eve_obj: object) -> str:
|
||||
|
||||
|
||||
@register.filter
|
||||
def zkillboard_solar_system_url(eve_obj: object) -> str:
|
||||
def zkillboard_solar_system_url(eve_obj: object) -> str:
|
||||
"""generates zkillboard URL for the given object
|
||||
Works with eve entity IDs
|
||||
Returns URL or empty string
|
||||
@ -214,20 +214,20 @@ def zkillboard_solar_system_url(eve_obj: object) -> str:
|
||||
|
||||
@register.filter
|
||||
def character_portrait_url(
|
||||
eve_obj: object,
|
||||
eve_obj: object,
|
||||
size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""generates an image URL for the given object
|
||||
Works with EveCharacter objects or character IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
if isinstance(eve_obj, EveCharacter):
|
||||
if isinstance(eve_obj, EveCharacter):
|
||||
return eve_obj.portrait_url(size)
|
||||
|
||||
elif eve_obj is None:
|
||||
return ''
|
||||
|
||||
else:
|
||||
|
||||
else:
|
||||
try:
|
||||
return EveCharacter.generic_portrait_url(eve_obj, size)
|
||||
except ValueError:
|
||||
@ -236,23 +236,23 @@ def character_portrait_url(
|
||||
|
||||
@register.filter
|
||||
def corporation_logo_url(
|
||||
eve_obj: object,
|
||||
eve_obj: object,
|
||||
size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""generates image URL for the given object
|
||||
Works with EveCharacter, EveCorporationInfo objects or corporation IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
if isinstance(eve_obj, EveCorporationInfo):
|
||||
if isinstance(eve_obj, EveCorporationInfo):
|
||||
return eve_obj.logo_url(size)
|
||||
|
||||
elif isinstance(eve_obj, EveCharacter):
|
||||
elif isinstance(eve_obj, EveCharacter):
|
||||
return eve_obj.corporation_logo_url(size)
|
||||
|
||||
elif eve_obj is None:
|
||||
return ''
|
||||
|
||||
else:
|
||||
|
||||
else:
|
||||
try:
|
||||
return EveCorporationInfo.generic_logo_url(eve_obj, size)
|
||||
except ValueError:
|
||||
@ -261,23 +261,23 @@ def corporation_logo_url(
|
||||
|
||||
@register.filter
|
||||
def alliance_logo_url(
|
||||
eve_obj: object,
|
||||
eve_obj: object,
|
||||
size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""generates image URL for the given object
|
||||
Works with EveCharacter, EveAllianceInfo objects or alliance IDs
|
||||
Returns URL or empty string
|
||||
"""
|
||||
if isinstance(eve_obj, EveAllianceInfo):
|
||||
if isinstance(eve_obj, EveAllianceInfo):
|
||||
return eve_obj.logo_url(size)
|
||||
|
||||
elif isinstance(eve_obj, EveCharacter):
|
||||
elif isinstance(eve_obj, EveCharacter):
|
||||
return eve_obj.alliance_logo_url(size)
|
||||
|
||||
elif eve_obj is None:
|
||||
return ''
|
||||
|
||||
else:
|
||||
|
||||
else:
|
||||
try:
|
||||
return EveAllianceInfo.generic_logo_url(eve_obj, size)
|
||||
except ValueError:
|
||||
@ -286,10 +286,10 @@ def alliance_logo_url(
|
||||
|
||||
@register.filter
|
||||
def type_icon_url(
|
||||
type_id: int,
|
||||
type_id: int,
|
||||
size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""generates a icon image URL for the given type ID
|
||||
"""generates a icon image URL for the given type ID
|
||||
Returns URL or empty string
|
||||
"""
|
||||
try:
|
||||
@ -300,10 +300,10 @@ def type_icon_url(
|
||||
|
||||
@register.filter
|
||||
def type_render_url(
|
||||
type_id: int,
|
||||
type_id: int,
|
||||
size: int = _DEFAULT_IMAGE_SIZE
|
||||
) -> str:
|
||||
"""generates a render image URL for the given type ID
|
||||
"""generates a render image URL for the given type ID
|
||||
Returns URL or empty string
|
||||
"""
|
||||
try:
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- This is an example template for the evelinks template tags
|
||||
<!-- This is an example template for the evelinks template tags
|
||||
|
||||
Needs to be called with a context containing three objects:
|
||||
|
||||
@ -18,15 +18,15 @@ Needs to be called with a context containing three objects:
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">Evelinks templatetags examples</h1>
|
||||
<div class="col-lg-12 container">
|
||||
|
||||
|
||||
<h2>profile URLs</h2>
|
||||
|
||||
<div class="rows">
|
||||
|
||||
<div class="col-md-4">
|
||||
<h3>evewho</h3>
|
||||
<p><a href="{{ my_character|evewho_character_url}}">character from character object</a></p>
|
||||
<p><a href="{{ my_corporation|evewho_corporation_url}}">corporation form corporation object</a></p>
|
||||
<p><a href="{{ my_character|evewho_character_url}}">character from character object</a></p>
|
||||
<p><a href="{{ my_corporation|evewho_corporation_url}}">corporation form corporation object</a></p>
|
||||
<p><a href="{{ my_character|evewho_corporation_url}}">corporation from charachter object</a></p>
|
||||
<p><a href="{{ my_alliance|evewho_alliance_url}}">alliance from alliance object</a></p>
|
||||
<p><a href="{{ my_character|evewho_alliance_url}}">alliance from character object</a></p>
|
||||
@ -42,23 +42,23 @@ Needs to be called with a context containing three objects:
|
||||
<p><a href="{{ 'Tama'|dotlan_solar_system_url}}">solar system from name string</a></p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-4">
|
||||
<h3>zkillboard</h3>
|
||||
<p><a href="{{ my_character|zkillboard_character_url}}">character from character object</a></p>
|
||||
<p><a href="{{ my_character|zkillboard_character_url}}">character from character object</a></p>
|
||||
<p><a href="{{ my_character|zkillboard_corporation_url}}">corporation from character object</a></p>
|
||||
<p><a href="{{ my_corporation|zkillboard_corporation_url}}">corporation form corporation object</a></p>
|
||||
<p><a href="{{ my_corporation|zkillboard_corporation_url}}">corporation form corporation object</a></p>
|
||||
<p><a href="{{ my_character|zkillboard_alliance_url}}">alliance from character object</a></p>
|
||||
<p><a href="{{ my_alliance|zkillboard_alliance_url}}">alliance from alliance object</a></p>
|
||||
<p><a href="{{ 10000069|zkillboard_region_url}}">region from ID</a></p>
|
||||
<p><a href="{{ 30002813|zkillboard_solar_system_url}}">solar sytem from ID</a></p>
|
||||
<p><a href="{{ 30002813|zkillboard_solar_system_url}}">solar sytem from ID</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>image URLs</h2>
|
||||
|
||||
<div class="rows">
|
||||
|
||||
|
||||
<div class="col-md-4">
|
||||
<p>character from ID: <img src="{{ my_character.character_id|character_portrait_url:128}}"></p>
|
||||
<p>character from character object: <img src="{{ my_character|character_portrait_url:128}}"></p>
|
||||
@ -67,18 +67,18 @@ Needs to be called with a context containing three objects:
|
||||
<div class="col-md-4">
|
||||
<p>corporation from ID: <img src="{{ my_character.corporation_id|corporation_logo_url:128}}"></p>
|
||||
<p>corporation from character object: <img src="{{ my_character|corporation_logo_url:128}}"></p>
|
||||
<p>corporation from corporation object: <img src="{{ my_corporation|corporation_logo_url:128}}"></p>
|
||||
<p>corporation from corporation object: <img src="{{ my_corporation|corporation_logo_url:128}}"></p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<p>alliance from ID: <img src="{{ my_character.alliance_id|alliance_logo_url:128}}"></p>
|
||||
<p>alliance from character object: <img src="{{ my_character|alliance_logo_url:128}}"></p>
|
||||
<p>alliance from alliance object: <img src="{{ my_alliance|alliance_logo_url:128}}"></p>
|
||||
<p>alliance from alliance object: <img src="{{ my_alliance|alliance_logo_url:128}}"></p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
|
||||
def set_logger(logger_name: str, name: str) -> object:
|
||||
"""set logger for current test module
|
||||
|
||||
|
||||
Args:
|
||||
- logger: current logger object
|
||||
- name: name of current module, e.g. __file__
|
||||
|
||||
|
||||
Returns:
|
||||
- amended logger
|
||||
"""
|
||||
|
||||
|
||||
# reconfigure logger so we get logging from tested module
|
||||
f_format = logging.Formatter(
|
||||
'%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s'
|
||||
|
@ -27,9 +27,9 @@ class EveCharacterManagerTestCase(TestCase):
|
||||
@property
|
||||
def corp(self):
|
||||
return Corporation(
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
alliance_id=3456,
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
alliance_id=3456,
|
||||
ticker='0BUGS' #lies, blatant lies!
|
||||
)
|
||||
|
||||
@ -37,9 +37,9 @@ class EveCharacterManagerTestCase(TestCase):
|
||||
def test_create_character(self, provider):
|
||||
# Also covers create_character_obj
|
||||
expected = self.TestCharacter(
|
||||
id=1234,
|
||||
name='Test Character',
|
||||
corp_id=2345,
|
||||
id=1234,
|
||||
name='Test Character',
|
||||
corp_id=2345,
|
||||
alliance_id=3456
|
||||
)
|
||||
|
||||
@ -69,9 +69,9 @@ class EveCharacterManagerTestCase(TestCase):
|
||||
)
|
||||
|
||||
expected = self.TestCharacter(
|
||||
id=1234,
|
||||
name='Test Character',
|
||||
corp_id=2345,
|
||||
id=1234,
|
||||
name='Test Character',
|
||||
corp_id=2345,
|
||||
alliance_id=3456
|
||||
)
|
||||
provider.get_character.return_value = expected
|
||||
@ -104,7 +104,7 @@ class EveCharacterManagerTestCase(TestCase):
|
||||
self.assertEqual(result.character_id, 1234)
|
||||
self.assertEqual(result.character_name, 'character.name')
|
||||
|
||||
# try to get non existing character
|
||||
# try to get non existing character
|
||||
self.assertIsNone(EveCharacter.objects.get_character_by_id(9999))
|
||||
|
||||
|
||||
@ -130,10 +130,10 @@ class EveAllianceManagerTestCase(TestCase):
|
||||
def test_create_alliance(self, provider, populate_alliance):
|
||||
# Also covers create_alliance_obj
|
||||
expected = self.TestAlliance(
|
||||
id=3456,
|
||||
name='Test Alliance',
|
||||
id=3456,
|
||||
name='Test Alliance',
|
||||
ticker='TEST',
|
||||
corp_ids=[2345],
|
||||
corp_ids=[2345],
|
||||
executor_corp_id=2345
|
||||
)
|
||||
|
||||
@ -157,10 +157,10 @@ class EveAllianceManagerTestCase(TestCase):
|
||||
executor_corp_id=2345,
|
||||
)
|
||||
expected = self.TestAlliance(
|
||||
id=3456,
|
||||
name='Test Alliance',
|
||||
id=3456,
|
||||
name='Test Alliance',
|
||||
ticker='TEST',
|
||||
corp_ids=[2345],
|
||||
corp_ids=[2345],
|
||||
executor_corp_id=2345
|
||||
)
|
||||
|
||||
@ -189,19 +189,19 @@ class EveCorporationManagerTestCase(TestCase):
|
||||
@property
|
||||
def alliance(self):
|
||||
return EveAllianceManagerTestCase.TestAlliance(
|
||||
id=3456,
|
||||
name='Test Alliance',
|
||||
id=3456,
|
||||
name='Test Alliance',
|
||||
ticker='TEST',
|
||||
corp_ids=[2345],
|
||||
corp_ids=[2345],
|
||||
executor_corp_id=2345
|
||||
)
|
||||
|
||||
@property
|
||||
def ceo(self):
|
||||
return EveCharacterManagerTestCase.TestCharacter(
|
||||
id=1234,
|
||||
id=1234,
|
||||
name='Test Character',
|
||||
corp_id=2345,
|
||||
corp_id=2345,
|
||||
alliance_id=3456
|
||||
)
|
||||
|
||||
@ -216,11 +216,11 @@ class EveCorporationManagerTestCase(TestCase):
|
||||
)
|
||||
|
||||
expected = self.TestCorporation(
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
ticker='0BUGS',
|
||||
ceo_id=1234,
|
||||
members=1,
|
||||
ceo_id=1234,
|
||||
members=1,
|
||||
alliance_id=3456
|
||||
)
|
||||
|
||||
@ -237,13 +237,13 @@ class EveCorporationManagerTestCase(TestCase):
|
||||
@mock.patch('allianceauth.eveonline.managers.providers.provider')
|
||||
def test_create_corporation_no_alliance(self, provider):
|
||||
# variant to test no alliance case
|
||||
# Also covers create_corp_obj
|
||||
# Also covers create_corp_obj
|
||||
expected = self.TestCorporation(
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
ticker='0BUGS',
|
||||
ceo_id=1234,
|
||||
members=1,
|
||||
ceo_id=1234,
|
||||
members=1,
|
||||
alliance_id=3456
|
||||
)
|
||||
|
||||
@ -276,11 +276,11 @@ class EveCorporationManagerTestCase(TestCase):
|
||||
)
|
||||
|
||||
expected = self.TestCorporation(
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
id=2345,
|
||||
name='Test Corp',
|
||||
ticker='0BUGS',
|
||||
ceo_id=1234,
|
||||
members=1,
|
||||
ceo_id=1234,
|
||||
members=1,
|
||||
alliance_id=3456
|
||||
)
|
||||
|
||||
|
@ -133,9 +133,9 @@ class EveCharacterTestCase(TestCase):
|
||||
name='Dummy Corp 2',
|
||||
ticker='DC2',
|
||||
ceo_id=1001,
|
||||
members=34,
|
||||
members=34,
|
||||
)
|
||||
|
||||
|
||||
my_character = EveCharacter.objects.create(
|
||||
character_id=1001,
|
||||
character_name='Bruce Wayne',
|
||||
@ -144,7 +144,7 @@ class EveCharacterTestCase(TestCase):
|
||||
corporation_ticker='DC1',
|
||||
alliance_id=3001,
|
||||
alliance_name='Dummy Alliance 1',
|
||||
)
|
||||
)
|
||||
my_updated_character = Character(
|
||||
name='Bruce X. Wayne',
|
||||
corp_id=2002
|
||||
@ -169,8 +169,8 @@ class EveCharacterTestCase(TestCase):
|
||||
character_id=42,
|
||||
character_name='character.name',
|
||||
corporation_id=123,
|
||||
corporation_name='corporation.name',
|
||||
corporation_ticker='ABC',
|
||||
corporation_name='corporation.name',
|
||||
corporation_ticker='ABC',
|
||||
)
|
||||
self.assertEqual(
|
||||
x.portrait_url(),
|
||||
@ -179,7 +179,7 @@ class EveCharacterTestCase(TestCase):
|
||||
self.assertEqual(
|
||||
x.portrait_url(64),
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=64)
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
x.portrait_url_32,
|
||||
eveimageserver._eve_entity_image_url('character', 42, size=32)
|
||||
@ -202,8 +202,8 @@ class EveCharacterTestCase(TestCase):
|
||||
character_id=42,
|
||||
character_name='character.name',
|
||||
corporation_id=123,
|
||||
corporation_name='corporation.name',
|
||||
corporation_ticker='ABC',
|
||||
corporation_name='corporation.name',
|
||||
corporation_ticker='ABC',
|
||||
)
|
||||
self.assertEqual(
|
||||
x.corporation_logo_url(),
|
||||
@ -235,9 +235,9 @@ class EveCharacterTestCase(TestCase):
|
||||
character_id=42,
|
||||
character_name='character.name',
|
||||
corporation_id=123,
|
||||
corporation_name='corporation.name',
|
||||
corporation_ticker='ABC',
|
||||
)
|
||||
corporation_name='corporation.name',
|
||||
corporation_ticker='ABC',
|
||||
)
|
||||
self.assertEqual(
|
||||
x.alliance_logo_url(),
|
||||
''
|
||||
@ -286,7 +286,7 @@ class EveCharacterTestCase(TestCase):
|
||||
|
||||
|
||||
class EveAllianceTestCase(TestCase):
|
||||
|
||||
|
||||
def test_str(self):
|
||||
my_alliance = EveAllianceInfo(
|
||||
alliance_id=3001,
|
||||
@ -295,12 +295,12 @@ class EveAllianceTestCase(TestCase):
|
||||
executor_corp_id=2001
|
||||
)
|
||||
self.assertEqual(str(my_alliance), 'Dummy Alliance 1')
|
||||
|
||||
|
||||
@patch(
|
||||
'allianceauth.eveonline.models.EveCorporationInfo.objects.create_corporation'
|
||||
)
|
||||
def test_populate_alliance(self, mock_create_corporation):
|
||||
|
||||
|
||||
def create_corp(corp_id):
|
||||
if corp_id == 2002:
|
||||
EveCorporationInfo.objects.create(
|
||||
@ -311,23 +311,23 @@ class EveAllianceTestCase(TestCase):
|
||||
)
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
|
||||
mock_EveAllianceProviderManager = Mock()
|
||||
mock_EveAllianceProviderManager.get_alliance.return_value = \
|
||||
Alliance(
|
||||
id=3001,
|
||||
id=3001,
|
||||
name='Dummy Alliance 1',
|
||||
corp_ids=[2001, 2002]
|
||||
)
|
||||
mock_create_corporation.side_effect = create_corp
|
||||
|
||||
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2001,
|
||||
corporation_name='Dummy Corporation 1',
|
||||
corporation_ticker='DC1',
|
||||
member_count=42,
|
||||
)
|
||||
|
||||
|
||||
my_alliance = EveAllianceInfo(
|
||||
alliance_id=3001,
|
||||
alliance_name='Dummy Alliance 1',
|
||||
@ -336,13 +336,13 @@ class EveAllianceTestCase(TestCase):
|
||||
)
|
||||
my_alliance.provider = mock_EveAllianceProviderManager
|
||||
my_alliance.save()
|
||||
my_alliance.populate_alliance()
|
||||
my_alliance.populate_alliance()
|
||||
|
||||
for corporation in EveCorporationInfo.objects\
|
||||
.filter(corporation_id__in=[2001, 2002]
|
||||
):
|
||||
self.assertEqual(corporation.alliance, my_alliance)
|
||||
|
||||
|
||||
def test_update_alliance_with_object(self):
|
||||
my_alliance = EveAllianceInfo.objects.create(
|
||||
alliance_id=3001,
|
||||
@ -351,9 +351,9 @@ class EveAllianceTestCase(TestCase):
|
||||
executor_corp_id=2001
|
||||
)
|
||||
updated_alliance = Alliance(
|
||||
id=3002,
|
||||
id=3002,
|
||||
name='Dummy Alliance 2',
|
||||
corp_ids=[2004],
|
||||
corp_ids=[2004],
|
||||
executor_corp_id=2004
|
||||
)
|
||||
my_alliance.update_alliance(updated_alliance)
|
||||
@ -367,7 +367,7 @@ class EveAllianceTestCase(TestCase):
|
||||
mock_EveAllianceProviderManager = Mock()
|
||||
mock_EveAllianceProviderManager.get_alliance.return_value = \
|
||||
Alliance(
|
||||
id=3002,
|
||||
id=3002,
|
||||
name='Dummy Alliance 2',
|
||||
corp_ids=[2004],
|
||||
executor_corp_id=2004
|
||||
@ -381,15 +381,15 @@ class EveAllianceTestCase(TestCase):
|
||||
)
|
||||
my_alliance.provider = mock_EveAllianceProviderManager
|
||||
my_alliance.save()
|
||||
Alliance(
|
||||
Alliance(
|
||||
name='Dummy Alliance 2',
|
||||
corp_ids=[2004],
|
||||
corp_ids=[2004],
|
||||
executor_corp_id=2004
|
||||
)
|
||||
my_alliance.update_alliance()
|
||||
my_alliance.refresh_from_db()
|
||||
self.assertEqual(int(my_alliance.executor_corp_id), 2004)
|
||||
|
||||
|
||||
# potential bug
|
||||
# update_alliance() is only updateting executor_corp_id nothing else ???
|
||||
|
||||
@ -417,7 +417,7 @@ class EveAllianceTestCase(TestCase):
|
||||
self.assertEqual(
|
||||
x.logo_url(64),
|
||||
'https://images.evetech.net/alliances/42/logo?size=64'
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
x.logo_url_32,
|
||||
'https://images.evetech.net/alliances/42/logo?size=32'
|
||||
@ -434,10 +434,10 @@ class EveAllianceTestCase(TestCase):
|
||||
x.logo_url_256,
|
||||
'https://images.evetech.net/alliances/42/logo?size=256'
|
||||
)
|
||||
|
||||
|
||||
|
||||
class EveCorporationTestCase(TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
my_alliance = EveAllianceInfo.objects.create(
|
||||
alliance_id=3001,
|
||||
@ -451,9 +451,9 @@ class EveCorporationTestCase(TestCase):
|
||||
corporation_ticker='DC1',
|
||||
member_count=42,
|
||||
alliance=my_alliance
|
||||
)
|
||||
)
|
||||
|
||||
def test_str(self):
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.my_corp), 'Dummy Corporation 1')
|
||||
|
||||
def test_update_corporation_from_object_w_alliance(self):
|
||||
@ -465,15 +465,15 @@ class EveCorporationTestCase(TestCase):
|
||||
|
||||
# potential bug
|
||||
# update_corporation updates member_count only
|
||||
|
||||
|
||||
def test_update_corporation_no_object_w_alliance(self):
|
||||
mock_provider = Mock()
|
||||
mock_provider.get_corporation.return_value = Corporation(members=87)
|
||||
self.my_corp.provider = mock_provider
|
||||
|
||||
|
||||
self.my_corp.update_corporation()
|
||||
self.assertEqual(self.my_corp.member_count, 87)
|
||||
|
||||
|
||||
def test_update_corporation_from_object_wo_alliance(self):
|
||||
my_corp2 = EveCorporationInfo(
|
||||
corporation_id=2011,
|
||||
@ -498,7 +498,7 @@ class EveCorporationTestCase(TestCase):
|
||||
eveimageserver._eve_entity_image_url('corporation', 42, 256)
|
||||
)
|
||||
|
||||
def test_logo_url(self):
|
||||
def test_logo_url(self):
|
||||
self.assertEqual(
|
||||
self.my_corp.logo_url(),
|
||||
'https://images.evetech.net/corporations/2001/logo?size=32'
|
||||
|
@ -8,13 +8,13 @@ from django.test import TestCase
|
||||
|
||||
from . import set_logger
|
||||
from ..providers import (
|
||||
ObjectNotFound,
|
||||
Entity,
|
||||
Character,
|
||||
Corporation,
|
||||
Alliance,
|
||||
ItemType,
|
||||
EveProvider,
|
||||
ObjectNotFound,
|
||||
Entity,
|
||||
Character,
|
||||
Corporation,
|
||||
Alliance,
|
||||
ItemType,
|
||||
EveProvider,
|
||||
EveSwaggerProvider
|
||||
)
|
||||
|
||||
@ -92,12 +92,12 @@ class TestCorporation(TestCase):
|
||||
executor_corp_id=2001
|
||||
)
|
||||
mock_provider_get_alliance.return_value = my_alliance
|
||||
|
||||
|
||||
x = Corporation(alliance_id=3001)
|
||||
self.assertEqual(
|
||||
x.alliance,
|
||||
my_alliance
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
x.alliance,
|
||||
my_alliance
|
||||
@ -106,25 +106,25 @@ class TestCorporation(TestCase):
|
||||
self.assertEqual(mock_provider_get_alliance.call_count, 1)
|
||||
|
||||
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
|
||||
def test_alliance_not_defined(self, mock_provider_get_alliance):
|
||||
def test_alliance_not_defined(self, mock_provider_get_alliance):
|
||||
mock_provider_get_alliance.return_value = None
|
||||
|
||||
|
||||
x = Corporation()
|
||||
self.assertEqual(
|
||||
x.alliance,
|
||||
Entity(None, None)
|
||||
)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.EveSwaggerProvider.get_character')
|
||||
def test_ceo(self, mock_provider_get_character):
|
||||
def test_ceo(self, mock_provider_get_character):
|
||||
my_ceo = Character(
|
||||
id=1001,
|
||||
name='Bruce Wayne',
|
||||
corp_id=2001,
|
||||
alliance_id=3001
|
||||
)
|
||||
)
|
||||
mock_provider_get_character.return_value = my_ceo
|
||||
|
||||
|
||||
# fetch from provider if not defined
|
||||
x = Corporation()
|
||||
self.assertEqual(
|
||||
@ -142,7 +142,7 @@ class TestCorporation(TestCase):
|
||||
|
||||
# bug in ceo(): will try to fetch character even if ceo_id is None
|
||||
|
||||
|
||||
|
||||
class TestAlliance(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -155,48 +155,48 @@ class TestAlliance(TestCase):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_corp(corp_id):
|
||||
def _get_corp(corp_id):
|
||||
corps = {
|
||||
2001: Corporation(
|
||||
id=2001,
|
||||
name='Dummy Corp 1',
|
||||
alliance_id=3001
|
||||
alliance_id=3001
|
||||
),
|
||||
2002: Corporation(
|
||||
id=2002,
|
||||
name='Dummy Corp 2',
|
||||
alliance_id=3001
|
||||
alliance_id=3001
|
||||
),
|
||||
2003: Corporation(
|
||||
id=2003,
|
||||
name='Dummy Corp 3',
|
||||
alliance_id=3001
|
||||
alliance_id=3001
|
||||
),
|
||||
}
|
||||
|
||||
if corp_id:
|
||||
|
||||
if corp_id:
|
||||
return corps[int(corp_id)]
|
||||
|
||||
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||
def test_corp(self, mock_provider_get_corp):
|
||||
def test_corp(self, mock_provider_get_corp):
|
||||
mock_provider_get_corp.side_effect = TestAlliance._get_corp
|
||||
|
||||
# should fetch corp if not in the object
|
||||
|
||||
# should fetch corp if not in the object
|
||||
self.assertEqual(
|
||||
self.my_alliance.corp(2001),
|
||||
TestAlliance._get_corp(2001)
|
||||
)
|
||||
# should fetch corp if not in the object
|
||||
# should fetch corp if not in the object
|
||||
self.assertEqual(
|
||||
self.my_alliance.corp(2002),
|
||||
TestAlliance._get_corp(2002)
|
||||
)
|
||||
# should return from the object if its there
|
||||
# should return from the object if its there
|
||||
self.assertEqual(
|
||||
self.my_alliance.corp(2001),
|
||||
TestAlliance._get_corp(2001)
|
||||
)
|
||||
# should return from the object if its there
|
||||
# should return from the object if its there
|
||||
self.assertEqual(
|
||||
self.my_alliance.corp(2002),
|
||||
TestAlliance._get_corp(2002)
|
||||
@ -220,7 +220,7 @@ class TestAlliance(TestCase):
|
||||
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||
def test_executor_corp(self, mock_provider_get_corp):
|
||||
mock_provider_get_corp.side_effect = TestAlliance._get_corp
|
||||
|
||||
|
||||
self.assertEqual(
|
||||
self.my_alliance.executor_corp,
|
||||
TestAlliance._get_corp(2001),
|
||||
@ -248,7 +248,7 @@ class TestCharacter(TestCase):
|
||||
my_corp = Corporation(
|
||||
id=2001,
|
||||
name='Dummy Corp 1'
|
||||
)
|
||||
)
|
||||
mock_provider_get_corp.return_value = my_corp
|
||||
|
||||
self.assertEqual(self.my_character.corp, my_corp)
|
||||
@ -256,11 +256,11 @@ class TestCharacter(TestCase):
|
||||
|
||||
# should call the provider one time only
|
||||
self.assertEqual(mock_provider_get_corp.call_count, 1)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
|
||||
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
|
||||
def test_alliance_has_one(
|
||||
self,
|
||||
self,
|
||||
mock_provider_get_corp,
|
||||
mock_provider_get_alliance,
|
||||
):
|
||||
@ -268,14 +268,14 @@ class TestCharacter(TestCase):
|
||||
id=2001,
|
||||
name='Dummy Corp 1',
|
||||
alliance_id=3001
|
||||
)
|
||||
)
|
||||
mock_provider_get_corp.return_value = my_corp
|
||||
my_alliance = Alliance(
|
||||
id=3001,
|
||||
name='Dummy Alliance 1',
|
||||
executor_corp_id=2001,
|
||||
corp_ids=[2001, 2002]
|
||||
)
|
||||
)
|
||||
mock_provider_get_alliance.return_value = my_alliance
|
||||
|
||||
self.assertEqual(self.my_character.alliance, my_alliance)
|
||||
@ -301,7 +301,7 @@ class TestEveProvider(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.my_provider = EveProvider()
|
||||
|
||||
|
||||
def test_get_alliance(self):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
self.my_provider.get_alliance(3001)
|
||||
@ -315,7 +315,7 @@ class TestEveProvider(TestCase):
|
||||
self.my_provider.get_character(1001)
|
||||
|
||||
# bug: should be calling NotImplementedError() not NotImplemented
|
||||
"""
|
||||
"""
|
||||
def test_get_itemtype(self):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
self.my_provider.get_itemtype(4001)
|
||||
@ -323,7 +323,7 @@ class TestEveProvider(TestCase):
|
||||
|
||||
|
||||
class TestEveSwaggerProvider(TestCase):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def esi_get_alliances_alliance_id(alliance_id):
|
||||
alliances = {
|
||||
@ -404,11 +404,11 @@ class TestEveSwaggerProvider(TestCase):
|
||||
@staticmethod
|
||||
def esi_post_characters_affiliation(characters):
|
||||
character_data = {
|
||||
1001: {
|
||||
1001: {
|
||||
'corporation_id': 2001,
|
||||
'alliance_id': 3001
|
||||
},
|
||||
1002: {
|
||||
1002: {
|
||||
'corporation_id': 2101
|
||||
}
|
||||
}
|
||||
@ -424,7 +424,7 @@ class TestEveSwaggerProvider(TestCase):
|
||||
return mock_result
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def esi_get_universe_types_type_id(type_id):
|
||||
types = {
|
||||
@ -443,7 +443,7 @@ class TestEveSwaggerProvider(TestCase):
|
||||
raise HTTPNotFound(Mock())
|
||||
|
||||
@patch(MODULE_PATH + '.esi_client_factory')
|
||||
def test_str(self, mock_esi_client_factory):
|
||||
def test_str(self, mock_esi_client_factory):
|
||||
my_provider = EveSwaggerProvider()
|
||||
self.assertEqual(str(my_provider), 'esi')
|
||||
|
||||
@ -457,7 +457,7 @@ class TestEveSwaggerProvider(TestCase):
|
||||
= TestEveSwaggerProvider.esi_get_alliances_alliance_id_corporations
|
||||
|
||||
my_provider = EveSwaggerProvider()
|
||||
|
||||
|
||||
# fully defined alliance
|
||||
my_alliance = my_provider.get_alliance(3001)
|
||||
self.assertEqual(my_alliance.id, 3001)
|
||||
@ -494,13 +494,13 @@ class TestEveSwaggerProvider(TestCase):
|
||||
|
||||
# corporation wo/ alliance
|
||||
my_corp = my_provider.get_corp(2002)
|
||||
self.assertEqual(my_corp.id, 2002)
|
||||
self.assertEqual(my_corp.id, 2002)
|
||||
self.assertEqual(my_corp.alliance_id, None)
|
||||
|
||||
# corporation not found
|
||||
with self.assertRaises(ObjectNotFound):
|
||||
my_provider.get_corp(2999)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.esi_client_factory')
|
||||
def test_get_character(self, mock_esi_client_factory):
|
||||
mock_esi_client_factory.return_value\
|
||||
@ -540,7 +540,7 @@ class TestEveSwaggerProvider(TestCase):
|
||||
my_type = my_provider.get_itemtype(4001)
|
||||
self.assertEqual(my_type.id, 4001)
|
||||
self.assertEqual(my_type.name, 'Dummy Type 1')
|
||||
|
||||
|
||||
# type not found
|
||||
with self.assertRaises(ObjectNotFound):
|
||||
my_provider.get_itemtype(4999)
|
||||
@ -551,7 +551,7 @@ class TestEveSwaggerProvider(TestCase):
|
||||
my_provider = EveSwaggerProvider()
|
||||
self.assertTrue(mock_esi_client_factory.called)
|
||||
self.assertIsNotNone(my_provider._client)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.SWAGGER_SPEC_PATH', SWAGGER_OLD_SPEC_PATH)
|
||||
@patch(MODULE_PATH + '.settings.DEBUG', False)
|
||||
@patch('socket.socket')
|
||||
@ -559,8 +559,8 @@ class TestEveSwaggerProvider(TestCase):
|
||||
self, mock_socket
|
||||
):
|
||||
mock_socket.side_effect = Exception('Network blocked for testing')
|
||||
my_provider = EveSwaggerProvider()
|
||||
self.assertIsNone(my_provider._client)
|
||||
my_provider = EveSwaggerProvider()
|
||||
self.assertIsNone(my_provider._client)
|
||||
|
||||
@patch(MODULE_PATH + '.settings.DEBUG', True)
|
||||
@patch(MODULE_PATH + '.esi_client_factory')
|
||||
|
@ -4,9 +4,9 @@ from django.test import TestCase
|
||||
|
||||
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
|
||||
from ..tasks import (
|
||||
update_alliance,
|
||||
update_corp,
|
||||
update_character,
|
||||
update_alliance,
|
||||
update_corp,
|
||||
update_character,
|
||||
run_model_update
|
||||
)
|
||||
|
||||
@ -22,7 +22,7 @@ class TestTasks(TestCase):
|
||||
self.assertEqual(
|
||||
mock_EveCorporationInfo.objects.update_corporation.call_args[0][0], 42
|
||||
)
|
||||
|
||||
|
||||
@patch('allianceauth.eveonline.tasks.EveAllianceInfo')
|
||||
def test_update_alliance(self, mock_EveAllianceInfo):
|
||||
update_alliance(42)
|
||||
@ -58,7 +58,7 @@ class TestRunModelUpdate(TestCase):
|
||||
EveCorporationInfo.objects.all().delete()
|
||||
EveAllianceInfo.objects.all().delete()
|
||||
EveCharacter.objects.all().delete()
|
||||
|
||||
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2345,
|
||||
corporation_name='corp.name',
|
||||
@ -71,13 +71,13 @@ class TestRunModelUpdate(TestCase):
|
||||
alliance_name='alliance.name',
|
||||
alliance_ticker='a.t',
|
||||
executor_corp_id=5,
|
||||
)
|
||||
)
|
||||
EveCharacter.objects.create(
|
||||
character_id=1,
|
||||
character_name='character.name1',
|
||||
corporation_id=2345,
|
||||
corporation_name='character.corp.name',
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
alliance_id=None
|
||||
)
|
||||
EveCharacter.objects.create(
|
||||
@ -85,7 +85,7 @@ class TestRunModelUpdate(TestCase):
|
||||
character_name='character.name2',
|
||||
corporation_id=9876,
|
||||
corporation_name='character.corp.name',
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
alliance_id=3456,
|
||||
alliance_name='character.alliance.name',
|
||||
)
|
||||
@ -94,7 +94,7 @@ class TestRunModelUpdate(TestCase):
|
||||
character_name='character.name3',
|
||||
corporation_id=9876,
|
||||
corporation_name='character.corp.name',
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
alliance_id=3456,
|
||||
alliance_name='character.alliance.name',
|
||||
)
|
||||
@ -103,7 +103,7 @@ class TestRunModelUpdate(TestCase):
|
||||
character_name='character.name4',
|
||||
corporation_id=9876,
|
||||
corporation_name='character.corp.name',
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
alliance_id=3456,
|
||||
alliance_name='character.alliance.name',
|
||||
)
|
||||
@ -113,12 +113,12 @@ class TestRunModelUpdate(TestCase):
|
||||
character_name='character.name5',
|
||||
corporation_id=9876,
|
||||
corporation_name='character.corp.name',
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
corporation_ticker='c.c.t', # max 5 chars
|
||||
alliance_id=3456,
|
||||
alliance_name='character.alliance.name',
|
||||
)
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.affiliations = [
|
||||
{'character_id': 1, 'corporation_id': 5},
|
||||
@ -152,14 +152,14 @@ class TestRunModelUpdate(TestCase):
|
||||
|
||||
mock_provider.client.Character.post_characters_affiliation.side_effect \
|
||||
= get_affiliations
|
||||
|
||||
|
||||
mock_provider.client.Universe.post_universe_names.side_effect = get_names
|
||||
|
||||
|
||||
run_model_update()
|
||||
|
||||
|
||||
self.assertEqual(
|
||||
mock_provider.client.Character.post_characters_affiliation.call_count, 2
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
mock_provider.client.Universe.post_universe_names.call_count, 2
|
||||
)
|
||||
@ -175,7 +175,7 @@ class TestRunModelUpdate(TestCase):
|
||||
self.assertEqual(mock_update_alliance.apply_async.call_count, 1)
|
||||
self.assertEqual(
|
||||
int(mock_update_alliance.apply_async.call_args[1]['args'][0]), 3456
|
||||
)
|
||||
)
|
||||
characters_updated = {
|
||||
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
|
||||
}
|
||||
@ -203,9 +203,9 @@ class TestRunModelUpdate(TestCase):
|
||||
|
||||
mock_provider.client.Character.post_characters_affiliation.side_effect \
|
||||
= get_affiliations
|
||||
|
||||
|
||||
mock_provider.client.Universe.post_universe_names.side_effect = get_names
|
||||
|
||||
|
||||
run_model_update()
|
||||
characters_updated = {
|
||||
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
|
||||
@ -234,9 +234,9 @@ class TestRunModelUpdate(TestCase):
|
||||
|
||||
mock_provider.client.Character.post_characters_affiliation.side_effect \
|
||||
= get_affiliations
|
||||
|
||||
|
||||
mock_provider.client.Universe.post_universe_names.side_effect = get_names
|
||||
|
||||
|
||||
run_model_update()
|
||||
characters_updated = {
|
||||
x[1]['args'][0] for x in mock_update_character.apply_async.call_args_list
|
||||
|
@ -10,8 +10,8 @@ from django.dispatch import receiver
|
||||
from .models import AuthGroup
|
||||
from .models import GroupRequest
|
||||
|
||||
if 'eve_autogroups' in apps.app_configs:
|
||||
_has_auto_groups = True
|
||||
if 'eve_autogroups' in apps.app_configs:
|
||||
_has_auto_groups = True
|
||||
else:
|
||||
_has_auto_groups = False
|
||||
|
||||
@ -20,12 +20,12 @@ class AuthGroupInlineAdmin(admin.StackedInline):
|
||||
model = AuthGroup
|
||||
filter_horizontal = ('group_leaders', 'group_leader_groups', 'states',)
|
||||
fields = (
|
||||
'description',
|
||||
'group_leaders',
|
||||
'group_leader_groups',
|
||||
'states', 'internal',
|
||||
'hidden',
|
||||
'open',
|
||||
'description',
|
||||
'group_leaders',
|
||||
'group_leader_groups',
|
||||
'states', 'internal',
|
||||
'hidden',
|
||||
'open',
|
||||
'public'
|
||||
)
|
||||
verbose_name_plural = 'Auth Settings'
|
||||
@ -64,12 +64,12 @@ if _has_auto_groups:
|
||||
value = self.value()
|
||||
if value == 'yes':
|
||||
return queryset.exclude(
|
||||
managedalliancegroup__isnull=True,
|
||||
managedalliancegroup__isnull=True,
|
||||
managedcorpgroup__isnull=True
|
||||
)
|
||||
elif value == 'no':
|
||||
return queryset.filter(
|
||||
managedalliancegroup__isnull=True,
|
||||
managedalliancegroup__isnull=True,
|
||||
managedcorpgroup__isnull=True
|
||||
)
|
||||
else:
|
||||
@ -96,36 +96,36 @@ class HasLeaderFilter(admin.SimpleListFilter):
|
||||
return queryset
|
||||
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
list_select_related = ('authgroup',)
|
||||
ordering = ('name',)
|
||||
list_display = (
|
||||
'name',
|
||||
'_description',
|
||||
'_properties',
|
||||
'_member_count',
|
||||
'name',
|
||||
'_description',
|
||||
'_properties',
|
||||
'_member_count',
|
||||
'has_leader'
|
||||
)
|
||||
list_filter = [
|
||||
'authgroup__internal',
|
||||
'authgroup__hidden',
|
||||
'authgroup__open',
|
||||
'authgroup__public',
|
||||
'authgroup__internal',
|
||||
'authgroup__hidden',
|
||||
'authgroup__open',
|
||||
'authgroup__public',
|
||||
]
|
||||
if _has_auto_groups:
|
||||
list_filter.append(IsAutoGroupFilter)
|
||||
list_filter.append(HasLeaderFilter)
|
||||
|
||||
search_fields = ('name', 'authgroup__description')
|
||||
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
qs = super().get_queryset(request)
|
||||
if _has_auto_groups:
|
||||
qs = qs.prefetch_related('managedalliancegroup_set', 'managedcorpgroup_set')
|
||||
qs = qs.prefetch_related('authgroup__group_leaders')
|
||||
qs = qs.annotate(
|
||||
member_count=Count('user', distinct=True),
|
||||
)
|
||||
)
|
||||
return qs
|
||||
|
||||
def _description(self, obj):
|
||||
@ -136,16 +136,16 @@ class GroupAdmin(admin.ModelAdmin):
|
||||
|
||||
_member_count.short_description = 'Members'
|
||||
_member_count.admin_order_field = 'member_count'
|
||||
|
||||
|
||||
def has_leader(self, obj):
|
||||
return obj.authgroup.group_leaders.exists()
|
||||
|
||||
has_leader.boolean = True
|
||||
|
||||
has_leader.boolean = True
|
||||
|
||||
def _properties(self, obj):
|
||||
properties = list()
|
||||
properties = list()
|
||||
if _has_auto_groups and (
|
||||
obj.managedalliancegroup_set.exists()
|
||||
obj.managedalliancegroup_set.exists()
|
||||
or obj.managedcorpgroup_set.exists()
|
||||
):
|
||||
properties.append('Auto Group')
|
||||
@ -160,7 +160,7 @@ class GroupAdmin(admin.ModelAdmin):
|
||||
properties.append('Public')
|
||||
if not properties:
|
||||
properties.append('Default')
|
||||
|
||||
|
||||
return properties
|
||||
|
||||
_properties.short_description = "properties"
|
||||
@ -183,19 +183,19 @@ finally:
|
||||
|
||||
|
||||
@admin.register(GroupRequest)
|
||||
class GroupRequestAdmin(admin.ModelAdmin):
|
||||
class GroupRequestAdmin(admin.ModelAdmin):
|
||||
search_fields = ('user__username', )
|
||||
list_display = ('id', 'group', 'user', '_leave_request', 'status')
|
||||
list_filter = (
|
||||
('group', admin.RelatedOnlyFieldListFilter),
|
||||
('user', admin.RelatedOnlyFieldListFilter),
|
||||
'leave_request',
|
||||
'leave_request',
|
||||
'status'
|
||||
)
|
||||
|
||||
def _leave_request(self, obj) -> True:
|
||||
return obj.leave_request
|
||||
|
||||
|
||||
_leave_request.short_description = 'is leave request'
|
||||
_leave_request.boolean = True
|
||||
|
||||
|
@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GroupManager:
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_joinable_groups_for_user(
|
||||
cls, user: User, include_hidden = True
|
||||
@ -21,7 +21,7 @@ class GroupManager:
|
||||
|
||||
if not user.has_perm('groupmanagement.request_groups'):
|
||||
groups_qs = groups_qs.filter(authgroup__public=True)
|
||||
|
||||
|
||||
if not include_hidden:
|
||||
groups_qs = groups_qs.filter(authgroup__hidden=False)
|
||||
|
||||
@ -57,7 +57,7 @@ class GroupManager:
|
||||
:param state: allianceauth.authentication.State object
|
||||
:return: bool True if its joinable, False otherwise
|
||||
"""
|
||||
if (len(group.authgroup.states.all()) != 0
|
||||
if (len(group.authgroup.states.all()) != 0
|
||||
and state not in group.authgroup.states.all()
|
||||
):
|
||||
return False
|
||||
@ -106,7 +106,7 @@ class GroupManager:
|
||||
@classmethod
|
||||
def pending_requests_count_for_user(cls, user: User) -> int:
|
||||
"""Returns the number of pending group requests for the given user"""
|
||||
|
||||
|
||||
if cls.has_management_permission(user):
|
||||
return GroupRequest.objects.filter(status="pending").count()
|
||||
else:
|
||||
|
@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
|
||||
def check_groups_on_state_change(sender, user, state, **kwargs):
|
||||
logger.debug(
|
||||
"Checking group memberships for %s based on new state %s" % (user, state)
|
||||
)
|
||||
)
|
||||
state_groups = (
|
||||
user.groups.select_related("authgroup").exclude(authgroup__states=None)
|
||||
)
|
||||
@ -18,5 +18,5 @@ def check_groups_on_state_change(sender, user, state, **kwargs):
|
||||
if not group.authgroup.states.filter(id=state.id).exists():
|
||||
logger.info(
|
||||
"Removing user %s from group %s due to missing state" % (user, group)
|
||||
)
|
||||
)
|
||||
user.groups.remove(group)
|
||||
|
@ -42,9 +42,9 @@
|
||||
|
||||
<div class="panel panel-default panel-tabs-aa">
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
|
||||
<div id="add" class="tab-pane active">
|
||||
<div class="tab-content">
|
||||
|
||||
<div id="add" class="tab-pane active">
|
||||
{% if acceptrequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
@ -98,9 +98,9 @@
|
||||
{% else %}
|
||||
<div class="alert alert-warning text-center">{% trans "No group add requests." %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="leave" class="tab-pane">
|
||||
</div>
|
||||
|
||||
<div id="leave" class="tab-pane">
|
||||
{% if leaverequests %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-aa">
|
||||
|
@ -26,7 +26,7 @@ MODULE_PATH = 'allianceauth.groupmanagement.admin'
|
||||
|
||||
|
||||
class MockRequest(object):
|
||||
|
||||
|
||||
def __init__(self, user=None):
|
||||
self.user = user
|
||||
|
||||
@ -38,7 +38,7 @@ class TestGroupAdmin(TestCase):
|
||||
super().setUpClass()
|
||||
|
||||
# group 1 - has leader
|
||||
cls.group_1 = Group.objects.create(name='Group 1')
|
||||
cls.group_1 = Group.objects.create(name='Group 1')
|
||||
cls.group_1.authgroup.description = 'Default Group'
|
||||
cls.group_1.authgroup.internal = False
|
||||
cls.group_1.authgroup.hidden = False
|
||||
@ -47,7 +47,7 @@ class TestGroupAdmin(TestCase):
|
||||
# group 2 - no leader
|
||||
cls.group_2 = Group.objects.create(name='Group 2')
|
||||
cls.group_2.authgroup.description = 'Internal Group'
|
||||
cls.group_2.authgroup.internal = True
|
||||
cls.group_2.authgroup.internal = True
|
||||
cls.group_2.authgroup.save()
|
||||
|
||||
# group 3 - has leader
|
||||
@ -81,7 +81,7 @@ class TestGroupAdmin(TestCase):
|
||||
cls.group_6.authgroup.open = True
|
||||
cls.group_6.authgroup.public = True
|
||||
cls.group_6.authgroup.save()
|
||||
|
||||
|
||||
# user 1 - corp and alliance, normal user
|
||||
cls.character_1 = EveCharacter.objects.create(
|
||||
character_id=1001,
|
||||
@ -106,16 +106,16 @@ class TestGroupAdmin(TestCase):
|
||||
alliance = EveAllianceInfo.objects.create(
|
||||
alliance_id=3001,
|
||||
alliance_name='Wayne Enterprises',
|
||||
alliance_ticker='WE',
|
||||
alliance_ticker='WE',
|
||||
executor_corp_id=2001
|
||||
)
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2001,
|
||||
corporation_name='Wayne Technologies',
|
||||
corporation_ticker='WT',
|
||||
corporation_ticker='WT',
|
||||
member_count=42,
|
||||
alliance=alliance
|
||||
)
|
||||
)
|
||||
cls.user_1 = User.objects.create_user(
|
||||
cls.character_1.character_name.replace(' ', '_'),
|
||||
'abc@example.com',
|
||||
@ -148,7 +148,7 @@ class TestGroupAdmin(TestCase):
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2002,
|
||||
corporation_name='Daily Plane',
|
||||
corporation_ticker='DP',
|
||||
corporation_ticker='DP',
|
||||
member_count=99,
|
||||
alliance=None
|
||||
)
|
||||
@ -167,7 +167,7 @@ class TestGroupAdmin(TestCase):
|
||||
cls.user_2.groups.add(cls.group_2)
|
||||
cls.user_2.is_staff = True
|
||||
cls.user_2.save()
|
||||
|
||||
|
||||
# user 3 - no main, no group, superuser
|
||||
cls.character_3 = EveCharacter.objects.create(
|
||||
character_id=1101,
|
||||
@ -180,7 +180,7 @@ class TestGroupAdmin(TestCase):
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2101,
|
||||
corporation_name='Lex Corp',
|
||||
corporation_ticker='LC',
|
||||
corporation_ticker='LC',
|
||||
member_count=666,
|
||||
alliance=None
|
||||
)
|
||||
@ -222,7 +222,7 @@ class TestGroupAdmin(TestCase):
|
||||
for state in State.objects.all():
|
||||
autogroups_config.states.add(state)
|
||||
autogroups_config.update_corp_group_membership(self.user_1)
|
||||
|
||||
|
||||
# column rendering
|
||||
|
||||
def test_description(self):
|
||||
@ -230,14 +230,14 @@ class TestGroupAdmin(TestCase):
|
||||
result = self.modeladmin._description(self.group_1)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_member_count(self):
|
||||
def test_member_count(self):
|
||||
expected = 1
|
||||
obj = self.modeladmin.get_queryset(MockRequest(user=self.user_1))\
|
||||
.get(pk=self.group_1.pk)
|
||||
result = self.modeladmin._member_count(obj)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_has_leader(self):
|
||||
def test_has_leader(self):
|
||||
result = self.modeladmin.has_leader(self.group_1)
|
||||
self.assertTrue(result)
|
||||
|
||||
@ -283,17 +283,17 @@ class TestGroupAdmin(TestCase):
|
||||
self.assertListEqual(result, expected)
|
||||
|
||||
# actions
|
||||
|
||||
|
||||
# filters
|
||||
|
||||
|
||||
if _has_auto_groups:
|
||||
@patch(MODULE_PATH + '._has_auto_groups', True)
|
||||
def test_filter_is_auto_group(self):
|
||||
|
||||
class GroupAdminTest(admin.ModelAdmin):
|
||||
|
||||
class GroupAdminTest(admin.ModelAdmin):
|
||||
list_filter = (IsAutoGroupFilter,)
|
||||
|
||||
self._create_autogroups()
|
||||
|
||||
self._create_autogroups()
|
||||
my_modeladmin = GroupAdminTest(Group, AdminSite())
|
||||
|
||||
# Make sure the lookups are correct
|
||||
@ -333,17 +333,17 @@ class TestGroupAdmin(TestCase):
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
queryset = changelist.get_queryset(request)
|
||||
expected = Group.objects.exclude(
|
||||
managedalliancegroup__isnull=True,
|
||||
managedalliancegroup__isnull=True,
|
||||
managedcorpgroup__isnull=True
|
||||
)
|
||||
self.assertSetEqual(set(queryset), set(expected))
|
||||
|
||||
|
||||
def test_filter_has_leader(self):
|
||||
|
||||
class GroupAdminTest(admin.ModelAdmin):
|
||||
|
||||
class GroupAdminTest(admin.ModelAdmin):
|
||||
list_filter = (HasLeaderFilter,)
|
||||
|
||||
self._create_autogroups()
|
||||
|
||||
self._create_autogroups()
|
||||
my_modeladmin = GroupAdminTest(Group, AdminSite())
|
||||
|
||||
# Make sure the lookups are correct
|
||||
@ -378,16 +378,16 @@ class TestGroupAdmin(TestCase):
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
queryset = changelist.get_queryset(request)
|
||||
expected = [
|
||||
self.group_1,
|
||||
self.group_1,
|
||||
self.group_3
|
||||
]
|
||||
self.assertSetEqual(set(queryset), set(expected))
|
||||
|
||||
|
||||
def test_change_view_loads_normally(self):
|
||||
User.objects.create_superuser(
|
||||
username='superuser', password='secret', email='admin@example.com'
|
||||
)
|
||||
c = Client()
|
||||
c.login(username='superuser', password='secret')
|
||||
c.login(username='superuser', password='secret')
|
||||
response = c.get(get_admin_change_view_url(self.group_1))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -46,7 +46,7 @@ class GroupManagementVisibilityTestCase(TestCase):
|
||||
|
||||
self.assertIn(self.group1, groups) #avail due to user
|
||||
self.assertNotIn(self.group2, groups) #not avail due to group
|
||||
self.assertNotIn(self.group3, groups) #not avail at all
|
||||
self.assertNotIn(self.group3, groups) #not avail at all
|
||||
|
||||
self.user.groups.add(self.group1)
|
||||
self._refresh_user()
|
||||
@ -60,7 +60,7 @@ class GroupManagementVisibilityTestCase(TestCase):
|
||||
self.assertTrue(GroupManager.can_manage_group(self.user, self.group1))
|
||||
self.assertFalse(GroupManager.can_manage_group(self.user, self.group2))
|
||||
self.assertFalse(GroupManager.can_manage_group(self.user, self.group3))
|
||||
|
||||
|
||||
self.group2.authgroup.group_leader_groups.add(self.group1)
|
||||
self.group1.authgroup.group_leaders.remove(self.user)
|
||||
self._refresh_user()
|
||||
@ -71,13 +71,13 @@ class GroupManagementVisibilityTestCase(TestCase):
|
||||
|
||||
|
||||
class TestGroupManager(TestCase):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
# group 1
|
||||
cls.group_default = Group.objects.create(name='default')
|
||||
cls.group_default = Group.objects.create(name='default')
|
||||
cls.group_default.authgroup.description = 'Default Group'
|
||||
cls.group_default.authgroup.internal = False
|
||||
cls.group_default.authgroup.hidden = False
|
||||
@ -86,7 +86,7 @@ class TestGroupManager(TestCase):
|
||||
# group 2
|
||||
cls.group_internal = Group.objects.create(name='internal')
|
||||
cls.group_internal.authgroup.description = 'Internal Group'
|
||||
cls.group_internal.authgroup.internal = True
|
||||
cls.group_internal.authgroup.internal = True
|
||||
cls.group_internal.authgroup.save()
|
||||
|
||||
# group 3
|
||||
@ -133,19 +133,19 @@ class TestGroupManager(TestCase):
|
||||
AuthUtils.get_member_state()
|
||||
)
|
||||
cls.group_default_member.authgroup.save()
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.user = AuthUtils.create_user('Bruce Wayne')
|
||||
|
||||
def test_get_joinable_group_member(self):
|
||||
def test_get_joinable_group_member(self):
|
||||
result = GroupManager.get_joinable_groups(
|
||||
AuthUtils.get_member_state()
|
||||
)
|
||||
expected = {
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2,
|
||||
self.group_default_member
|
||||
}
|
||||
@ -156,10 +156,10 @@ class TestGroupManager(TestCase):
|
||||
AuthUtils.get_guest_state()
|
||||
)
|
||||
expected = {
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2
|
||||
}
|
||||
self.assertSetEqual(set(result), expected)
|
||||
@ -167,27 +167,27 @@ class TestGroupManager(TestCase):
|
||||
def test_joinable_group_member(self):
|
||||
member_state = AuthUtils.get_member_state()
|
||||
for x in [
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2,
|
||||
self.group_default_member
|
||||
]:
|
||||
self.assertTrue(GroupManager.joinable_group(x, member_state))
|
||||
|
||||
for x in [
|
||||
self.group_internal,
|
||||
self.group_internal,
|
||||
]:
|
||||
self.assertFalse(GroupManager.joinable_group(x, member_state))
|
||||
|
||||
def test_joinable_group_guest(self):
|
||||
guest_state = AuthUtils.get_guest_state()
|
||||
for x in [
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2
|
||||
]:
|
||||
self.assertTrue(GroupManager.joinable_group(x, guest_state))
|
||||
@ -201,11 +201,11 @@ class TestGroupManager(TestCase):
|
||||
def test_get_all_non_internal_groups(self):
|
||||
result = GroupManager.get_all_non_internal_groups()
|
||||
expected = {
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2,
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2,
|
||||
self.group_default_member
|
||||
}
|
||||
self.assertSetEqual(set(result), expected)
|
||||
@ -231,10 +231,10 @@ class TestGroupManager(TestCase):
|
||||
)
|
||||
result = GroupManager.get_joinable_groups_for_user(self.user)
|
||||
expected = {
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_default,
|
||||
self.group_hidden,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_public_2
|
||||
}
|
||||
self.assertSetEqual(set(result), expected)
|
||||
@ -265,69 +265,69 @@ class TestGroupManager(TestCase):
|
||||
)
|
||||
expected = {
|
||||
self.group_default,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_open,
|
||||
self.group_public_1,
|
||||
self.group_default_member
|
||||
}
|
||||
self.assertSetEqual(set(result), expected)
|
||||
|
||||
def test_has_management_permission(self):
|
||||
def test_has_management_permission(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
AuthUtils.add_permission_to_user_by_name(
|
||||
'auth.group_management', user
|
||||
)
|
||||
)
|
||||
self.assertTrue(GroupManager.has_management_permission(user))
|
||||
|
||||
def test_can_manage_groups_no_perm_no_group(self):
|
||||
def test_can_manage_groups_no_perm_no_group(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
self.assertFalse(GroupManager.can_manage_groups(user))
|
||||
|
||||
def test_can_manage_groups_user_not_authenticated(self):
|
||||
def test_can_manage_groups_user_not_authenticated(self):
|
||||
user = MockUserNotAuthenticated()
|
||||
self.assertFalse(GroupManager.can_manage_groups(user))
|
||||
|
||||
def test_can_manage_groups_has_perm(self):
|
||||
def test_can_manage_groups_has_perm(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
AuthUtils.add_permission_to_user_by_name(
|
||||
'auth.group_management', user
|
||||
)
|
||||
)
|
||||
self.assertTrue(GroupManager.can_manage_groups(user))
|
||||
|
||||
def test_can_manage_groups_no_perm_leads_group(self):
|
||||
def test_can_manage_groups_no_perm_leads_group(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
self.group_default.authgroup.group_leaders.add(user)
|
||||
self.assertTrue(GroupManager.can_manage_groups(user))
|
||||
|
||||
def test_can_manage_group_no_perm_no_group(self):
|
||||
def test_can_manage_group_no_perm_no_group(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
self.assertFalse(
|
||||
GroupManager.can_manage_group(user, self.group_default)
|
||||
)
|
||||
|
||||
def test_can_manage_group_has_perm(self):
|
||||
def test_can_manage_group_has_perm(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
AuthUtils.add_permission_to_user_by_name(
|
||||
'auth.group_management', user
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
GroupManager.can_manage_group(user, self.group_default)
|
||||
)
|
||||
|
||||
def test_can_manage_group_no_perm_leads_correct_group(self):
|
||||
def test_can_manage_group_no_perm_leads_correct_group(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
self.group_default.authgroup.group_leaders.add(user)
|
||||
self.assertTrue(
|
||||
GroupManager.can_manage_group(user, self.group_default)
|
||||
)
|
||||
|
||||
def test_can_manage_group_no_perm_leads_other_group(self):
|
||||
def test_can_manage_group_no_perm_leads_other_group(self):
|
||||
user = AuthUtils.create_user('Clark Kent')
|
||||
self.group_hidden.authgroup.group_leaders.add(user)
|
||||
self.assertFalse(
|
||||
GroupManager.can_manage_group(user, self.group_default)
|
||||
)
|
||||
|
||||
def test_can_manage_group_user_not_authenticated(self):
|
||||
def test_can_manage_group_user_not_authenticated(self):
|
||||
user = MockUserNotAuthenticated()
|
||||
self.assertFalse(
|
||||
GroupManager.can_manage_group(user, self.group_default)
|
||||
@ -335,7 +335,7 @@ class TestGroupManager(TestCase):
|
||||
|
||||
|
||||
class TestPendingRequestsCountForUser(TestCase):
|
||||
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.group_1 = Group.objects.create(name="Group 1")
|
||||
self.group_2 = Group.objects.create(name="Group 2")
|
||||
@ -345,8 +345,8 @@ class TestPendingRequestsCountForUser(TestCase):
|
||||
self.group_2.authgroup.group_leaders.add(self.user_leader_2)
|
||||
self.user_requestor = AuthUtils.create_member('Bruce Wayne')
|
||||
|
||||
def test_single_request_for_leader(self):
|
||||
# given user_leader_1 is leader of group_1
|
||||
def test_single_request_for_leader(self):
|
||||
# given user_leader_1 is leader of group_1
|
||||
# and user_leader_2 is leader of group_2
|
||||
# when user_requestor is requesting access to group 1
|
||||
# then return 1 for user_leader 1 and 0 for user_leader_2
|
||||
@ -359,7 +359,7 @@ class TestPendingRequestsCountForUser(TestCase):
|
||||
self.assertEqual(
|
||||
GroupManager.pending_requests_count_for_user(self.user_leader_2), 0
|
||||
)
|
||||
|
||||
|
||||
def test_return_none_for_none_leader(self):
|
||||
# given user_requestor is leader of no group
|
||||
# when user_requestor is requesting access to group 1
|
||||
@ -370,7 +370,7 @@ class TestPendingRequestsCountForUser(TestCase):
|
||||
self.assertEqual(
|
||||
GroupManager.pending_requests_count_for_user(self.user_requestor), 0
|
||||
)
|
||||
|
||||
|
||||
def test_single_leave_request(self):
|
||||
# given user_leader_2 is leader of group_2
|
||||
# and user_requestor is member of group 2
|
||||
@ -379,9 +379,9 @@ class TestPendingRequestsCountForUser(TestCase):
|
||||
self.user_requestor.groups.add(self.group_2)
|
||||
|
||||
GroupRequest.objects.create(
|
||||
status="pending",
|
||||
user=self.user_requestor,
|
||||
group=self.group_2,
|
||||
status="pending",
|
||||
user=self.user_requestor,
|
||||
group=self.group_2,
|
||||
leave_request=True
|
||||
)
|
||||
self.assertEqual(
|
||||
@ -391,31 +391,31 @@ class TestPendingRequestsCountForUser(TestCase):
|
||||
def test_join_and_leave_request(self):
|
||||
# given user_leader_2 is leader of group_2
|
||||
# and user_requestor is member of group 2
|
||||
# when user_requestor is requesting to leave group 2
|
||||
# when user_requestor is requesting to leave group 2
|
||||
# and user_requestor_2 is requesting to join group 2
|
||||
# then return 2 for user_leader_2
|
||||
self.user_requestor.groups.add(self.group_2)
|
||||
user_requestor_2 = AuthUtils.create_member("Lex Luther")
|
||||
GroupRequest.objects.create(
|
||||
status="pending",
|
||||
user=user_requestor_2,
|
||||
group=self.group_2
|
||||
status="pending",
|
||||
user=user_requestor_2,
|
||||
group=self.group_2
|
||||
)
|
||||
GroupRequest.objects.create(
|
||||
status="pending",
|
||||
user=self.user_requestor,
|
||||
group=self.group_2,
|
||||
status="pending",
|
||||
user=self.user_requestor,
|
||||
group=self.group_2,
|
||||
leave_request=True
|
||||
)
|
||||
self.assertEqual(
|
||||
GroupManager.pending_requests_count_for_user(self.user_leader_2), 2
|
||||
)
|
||||
|
||||
def test_single_request_for_user_with_management_perm(self):
|
||||
def test_single_request_for_user_with_management_perm(self):
|
||||
# given user_leader_4 which is leafer of no group
|
||||
# but has the management permissions
|
||||
# when user_requestor is requesting access to group 1
|
||||
# then return 1 for user_leader_4
|
||||
# then return 1 for user_leader_4
|
||||
user_leader_4 = AuthUtils.create_member("Lex Luther")
|
||||
AuthUtils.add_permission_to_user_by_name("auth.group_management", user_leader_4)
|
||||
user_leader_4 = User.objects.get(pk=user_leader_4.pk)
|
||||
|
@ -11,7 +11,7 @@ from allianceauth.eveonline.models import (
|
||||
from ..models import GroupRequest, RequestLog
|
||||
|
||||
|
||||
def create_testdata():
|
||||
def create_testdata():
|
||||
# clear DB
|
||||
User.objects.all().delete()
|
||||
Group.objects.all().delete()
|
||||
@ -20,13 +20,13 @@ def create_testdata():
|
||||
EveAllianceInfo.objects.all().delete()
|
||||
|
||||
# group 1
|
||||
group = Group.objects.create(name='Superheros')
|
||||
group = Group.objects.create(name='Superheros')
|
||||
group.authgroup.description = 'Default Group'
|
||||
group.authgroup.internal = False
|
||||
group.authgroup.hidden = False
|
||||
group.authgroup.save()
|
||||
|
||||
# user 1
|
||||
|
||||
# user 1
|
||||
user_1 = AuthUtils.create_user('Bruce Wayne')
|
||||
AuthUtils.add_main_character_2(
|
||||
user_1,
|
||||
@ -34,7 +34,7 @@ def create_testdata():
|
||||
character_id=1001,
|
||||
corp_id=2001,
|
||||
corp_name='Wayne Technologies'
|
||||
)
|
||||
)
|
||||
user_1.groups.add(group)
|
||||
group.authgroup.group_leaders.add(user_1)
|
||||
|
||||
@ -46,13 +46,13 @@ def create_testdata():
|
||||
character_id=1002,
|
||||
corp_id=2002,
|
||||
corp_name='Wayne Technologies'
|
||||
)
|
||||
)
|
||||
return group, user_1, user_2
|
||||
|
||||
|
||||
|
||||
class TestGroupRequest(TestCase):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
@ -78,7 +78,7 @@ class TestGroupRequest(TestCase):
|
||||
|
||||
|
||||
class TestRequestLog(TestCase):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
@ -124,7 +124,7 @@ class TestRequestLog(TestCase):
|
||||
self.assertEqual(request_log.type_to_str(), expected)
|
||||
|
||||
def test_action_to_str_accept(self):
|
||||
request_log = RequestLog.objects.create(
|
||||
request_log = RequestLog.objects.create(
|
||||
group=self.group,
|
||||
request_info='Clark Kent:Superheros',
|
||||
request_actor=self.user_1,
|
||||
@ -134,7 +134,7 @@ class TestRequestLog(TestCase):
|
||||
self.assertEqual(request_log.action_to_str(), expected)
|
||||
|
||||
def test_action_to_str_reject(self):
|
||||
request_log = RequestLog.objects.create(
|
||||
request_log = RequestLog.objects.create(
|
||||
group=self.group,
|
||||
request_info='Clark Kent:Superheros',
|
||||
request_actor=self.user_1,
|
||||
@ -144,7 +144,7 @@ class TestRequestLog(TestCase):
|
||||
self.assertEqual(request_log.action_to_str(), expected)
|
||||
|
||||
def test_req_char(self):
|
||||
request_log = RequestLog.objects.create(
|
||||
request_log = RequestLog.objects.create(
|
||||
group=self.group,
|
||||
request_info='Clark Kent:Superheros',
|
||||
request_actor=self.user_1,
|
||||
@ -157,7 +157,7 @@ class TestRequestLog(TestCase):
|
||||
class TestAuthGroup(TestCase):
|
||||
|
||||
def test_str(self):
|
||||
group = Group.objects.create(name='Superheros')
|
||||
group = Group.objects.create(name='Superheros')
|
||||
group.authgroup.description = 'Default Group'
|
||||
group.authgroup.internal = False
|
||||
group.authgroup.hidden = False
|
||||
|
@ -14,7 +14,7 @@ class TestCheckGroupsOnStateChange(TestCase):
|
||||
cls.character = AuthUtils.add_main_character_2(
|
||||
cls.user, 'test character', 1001, corp_id=2001, corp_name='test corp 1', corp_ticker='TEST'
|
||||
)
|
||||
cls.user.profile.refresh_from_db()
|
||||
cls.user.profile.refresh_from_db()
|
||||
cls.corp_1 = EveCorporationInfo.objects.create(
|
||||
corporation_id=2001, corporation_name='test corp 1', corporation_ticker='C1', member_count=1
|
||||
)
|
||||
@ -24,13 +24,13 @@ class TestCheckGroupsOnStateChange(TestCase):
|
||||
cls.guest_state = AuthUtils.get_guest_state()
|
||||
cls.test_state_1 = AuthUtils.create_state('test_state_1', 500)
|
||||
cls.test_state_2 = AuthUtils.create_state('test_state_2', 600)
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.user.refresh_from_db()
|
||||
self.user.refresh_from_db()
|
||||
|
||||
def _refresh_user(self):
|
||||
self.user = User.objects.get(pk=self.user.pk)
|
||||
|
||||
|
||||
def test_drop_state_group(self):
|
||||
"""
|
||||
given user is member of: state group, normal group and auto group
|
||||
@ -39,29 +39,29 @@ class TestCheckGroupsOnStateChange(TestCase):
|
||||
and remains member of normal group and auto group
|
||||
"""
|
||||
# setup
|
||||
state_group = Group.objects.create(name='state_group')
|
||||
state_group = Group.objects.create(name='state_group')
|
||||
state_group.authgroup.states.add(self.test_state_1)
|
||||
state_group.authgroup.internal = False
|
||||
state_group.save()
|
||||
normal_group = Group.objects.create(name='normal_group')
|
||||
normal_group.authgroup.internal = False
|
||||
normal_group.save()
|
||||
internal_group = Group.objects.create(name='internal_group')
|
||||
internal_group = Group.objects.create(name='internal_group')
|
||||
autogroup_config = AutogroupsConfig.objects.create(corp_groups=True)
|
||||
autogroup_config.states.add(self.test_state_1)
|
||||
autogroup_config.states.add(self.guest_state)
|
||||
auto_group = autogroup_config.corp_managed_groups.first()
|
||||
internal_state_group = Group.objects.create(name='internal_state_group')
|
||||
internal_state_group = Group.objects.create(name='internal_state_group')
|
||||
internal_state_group.authgroup.states.add(self.test_state_1)
|
||||
self.test_state_1.member_corporations.add(self.corp_1)
|
||||
self.user.groups.add(normal_group)
|
||||
self.user.groups.add(internal_group)
|
||||
self.user.groups.add(state_group)
|
||||
self.user.groups.add(internal_state_group)
|
||||
|
||||
self.user.groups.add(state_group)
|
||||
self.user.groups.add(internal_state_group)
|
||||
|
||||
# user changes state back to guest
|
||||
self.test_state_1.member_corporations.clear()
|
||||
|
||||
|
||||
# assert
|
||||
self._refresh_user()
|
||||
self.assertEqual(self.user.profile.state, self.guest_state)
|
||||
@ -79,22 +79,21 @@ class TestCheckGroupsOnStateChange(TestCase):
|
||||
then user remains member of that group
|
||||
"""
|
||||
# setup
|
||||
state_group = Group.objects.create(name='state_group')
|
||||
state_group = Group.objects.create(name='state_group')
|
||||
state_group.authgroup.states.add(self.test_state_1)
|
||||
state_group.authgroup.states.add(self.test_state_2)
|
||||
|
||||
|
||||
self.test_state_1.member_corporations.add(self.corp_1)
|
||||
self.test_state_2.member_corporations.add(self.corp_2)
|
||||
self.user.groups.add(state_group)
|
||||
|
||||
|
||||
# user changes state back to guest
|
||||
self.character.corporation_id = 2002
|
||||
self.character.corporation_name = "test corp 2"
|
||||
self.character.save()
|
||||
|
||||
|
||||
# assert
|
||||
self._refresh_user()
|
||||
self.assertEqual(self.user.profile.state, self.test_state_2)
|
||||
groups = self.user.groups.all()
|
||||
groups = self.user.groups.all()
|
||||
self.assertIn(state_group, groups)
|
||||
|
@ -10,13 +10,13 @@ from .. import views
|
||||
|
||||
|
||||
class TestViews(TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
self.user = AuthUtils.create_user('Bruce Wayne')
|
||||
self.user = AuthUtils.create_user('Bruce Wayne')
|
||||
|
||||
def test_groups_view_can_load(self):
|
||||
request = self.factory.get(reverse('groupmanagement:groups'))
|
||||
request.user = self.user
|
||||
response = views.groups_view(request)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.status_code, 200)
|
@ -15,11 +15,11 @@ class ApplicationsMenu(MenuItemHook):
|
||||
'hrapplications:index',
|
||||
navactive=['hrapplications:'])
|
||||
|
||||
def render(self, request):
|
||||
def render(self, request):
|
||||
app_count = Application.objects.pending_requests_count_for_user(request.user)
|
||||
self.count = app_count if app_count and app_count > 0 else None
|
||||
return MenuItemHook.render(self, request)
|
||||
|
||||
|
||||
|
||||
@hooks.register('menu_item_hook')
|
||||
def register_menu():
|
||||
|
@ -23,7 +23,7 @@ class NotificationManager(models.Manager):
|
||||
|
||||
USER_NOTIFICATION_COUNT_PREFIX = 'USER_NOTIFICATION_COUNT'
|
||||
USER_NOTIFICATION_COUNT_CACHE_DURATION = 86_400
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
return NotificationQuerySet(self.model, using=self._db)
|
||||
|
||||
@ -39,37 +39,37 @@ class NotificationManager(models.Manager):
|
||||
)[max_notifications - 1:]
|
||||
for notification in to_be_deleted_qs:
|
||||
notification.delete()
|
||||
|
||||
|
||||
if not message:
|
||||
message = title
|
||||
|
||||
|
||||
obj = self.create(user=user, title=title, message=message, level=level)
|
||||
logger.info("Created notification %s", obj)
|
||||
return obj
|
||||
|
||||
|
||||
def _max_notifications_per_user(self):
|
||||
"""return the maximum number of notifications allowed per user"""
|
||||
max_notifications = getattr(settings, 'NOTIFICATIONS_MAX_PER_USER', None)
|
||||
if (
|
||||
max_notifications is None
|
||||
or not isinstance(max_notifications, int)
|
||||
max_notifications is None
|
||||
or not isinstance(max_notifications, int)
|
||||
or max_notifications < 0
|
||||
):
|
||||
logger.warning(
|
||||
'NOTIFICATIONS_MAX_PER_USER setting is invalid. Using default.'
|
||||
)
|
||||
max_notifications = self.model.NOTIFICATIONS_MAX_PER_USER_DEFAULT
|
||||
|
||||
|
||||
return max_notifications
|
||||
|
||||
def user_unread_count(self, user_pk: int) -> int:
|
||||
"""returns the cached unread count for a user given by user PK
|
||||
|
||||
|
||||
Will return -1 if user can not be found
|
||||
"""
|
||||
cache_key = self._user_notification_cache_key(user_pk)
|
||||
unread_count = cache.get(key=cache_key)
|
||||
if not unread_count:
|
||||
if not unread_count:
|
||||
try:
|
||||
user = User.objects.get(pk=user_pk)
|
||||
except User.DoesNotExist:
|
||||
@ -80,8 +80,8 @@ class NotificationManager(models.Manager):
|
||||
)
|
||||
unread_count = user.notification_set.filter(viewed=False).count()
|
||||
cache.set(
|
||||
key=cache_key,
|
||||
value=unread_count,
|
||||
key=cache_key,
|
||||
value=unread_count,
|
||||
timeout=self.USER_NOTIFICATION_COUNT_CACHE_DURATION
|
||||
)
|
||||
else:
|
||||
@ -92,7 +92,7 @@ class NotificationManager(models.Manager):
|
||||
return unread_count
|
||||
|
||||
@classmethod
|
||||
def invalidate_user_notification_cache(cls, user_pk: int) -> None:
|
||||
def invalidate_user_notification_cache(cls, user_pk: int) -> None:
|
||||
cache.delete(key=cls._user_notification_cache_key(user_pk))
|
||||
logger.debug('Invalided notification cache for user with pk %s', user_pk)
|
||||
|
||||
|
@ -10,10 +10,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class Notification(models.Model):
|
||||
"""Notification to a user within Auth"""
|
||||
|
||||
|
||||
NOTIFICATIONS_MAX_PER_USER_DEFAULT = 50
|
||||
NOTIFICATIONS_REFRESH_TIME_DEFAULT = 30
|
||||
|
||||
|
||||
LEVEL_CHOICES = (
|
||||
('danger', 'CRITICAL'),
|
||||
('danger', 'ERROR'),
|
||||
@ -30,18 +30,18 @@ class Notification(models.Model):
|
||||
viewed = models.BooleanField(default=False, db_index=True)
|
||||
|
||||
objects = NotificationManager()
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "%s: %s" % (self.user, self.title)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# overriden save to ensure cache is invaidated on very call
|
||||
super().save(*args, **kwargs)
|
||||
super().save(*args, **kwargs)
|
||||
Notification.objects.invalidate_user_notification_cache(self.user.pk)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
# overriden delete to ensure cache is invaidated on very call
|
||||
super().delete(*args, **kwargs)
|
||||
super().delete(*args, **kwargs)
|
||||
Notification.objects.invalidate_user_notification_cache(self.user.pk)
|
||||
|
||||
def mark_viewed(self) -> None:
|
||||
@ -52,7 +52,7 @@ class Notification(models.Model):
|
||||
|
||||
def set_level(self, level_name: str) -> None:
|
||||
"""set notification level according to level name, e.g. 'CRITICAL'
|
||||
|
||||
|
||||
raised exception on invalid level names
|
||||
"""
|
||||
try:
|
||||
|
@ -20,7 +20,7 @@ register = template.Library()
|
||||
@register.filter
|
||||
def user_unread_notification_count(user: object) -> int:
|
||||
"""returns the number of unread notifications for user
|
||||
|
||||
|
||||
Will return -1 on error
|
||||
"""
|
||||
if not isinstance(user, User):
|
||||
@ -29,10 +29,10 @@ def user_unread_notification_count(user: object) -> int:
|
||||
unread_count = Notification.objects.user_unread_count(user.pk)
|
||||
|
||||
return unread_count
|
||||
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def notifications_refresh_time() -> int:
|
||||
def notifications_refresh_time() -> int:
|
||||
refresh_time = getattr(settings, 'NOTIFICATIONS_REFRESH_TIME', Notification.NOTIFICATIONS_REFRESH_TIME_DEFAULT)
|
||||
if (not isinstance(refresh_time, int) or refresh_time < 0):
|
||||
logger.warning('NOTIFICATIONS_REFRESH_TIME setting is invalid. Using default.')
|
||||
|
@ -13,16 +13,16 @@ class TestUserNotificationCount(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('magic_mike')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
|
||||
|
||||
def test_can_notify(self):
|
||||
notify(self.user, 'dummy')
|
||||
self.assertEqual(Notification.objects.filter(user=self.user).count(), 1)
|
||||
|
@ -18,14 +18,14 @@ class TestQuerySet(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user_1 = AuthUtils.create_user('Peter Parker')
|
||||
cls.user_2 = AuthUtils.create_user('Clark Kent')
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.Notification.objects.invalidate_user_notification_cache')
|
||||
def test_update_will_invalidate_cache(
|
||||
self, mock_invalidate_user_notification_cache
|
||||
):
|
||||
Notification.objects.notify_user(self.user_1, 'dummy_1')
|
||||
Notification.objects.notify_user(self.user_2, 'dummy_2')
|
||||
Notification.objects.update(viewed=True)
|
||||
Notification.objects.notify_user(self.user_2, 'dummy_2')
|
||||
Notification.objects.update(viewed=True)
|
||||
self.assertEquals(mock_invalidate_user_notification_cache.call_count, 2)
|
||||
|
||||
|
||||
@ -34,16 +34,16 @@ class TestUserNotify(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('magic_mike')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
|
||||
|
||||
def test_can_notify(self):
|
||||
title = 'dummy_title'
|
||||
message = 'dummy message'
|
||||
@ -57,14 +57,14 @@ class TestUserNotify(TestCase):
|
||||
self.assertEqual(obj.level, level)
|
||||
|
||||
def test_use_message_as_title_if_missing(self):
|
||||
title = 'dummy_title'
|
||||
title = 'dummy_title'
|
||||
Notification.objects.notify_user(self.user, title)
|
||||
self.assertEqual(Notification.objects.filter(user=self.user).count(), 1)
|
||||
obj = Notification.objects.first()
|
||||
self.assertEqual(obj.user, self.user)
|
||||
self.assertEqual(obj.title, title)
|
||||
self.assertEqual(obj.message, title)
|
||||
|
||||
self.assertEqual(obj.message, title)
|
||||
|
||||
@override_settings(NOTIFICATIONS_MAX_PER_USER=3)
|
||||
def test_remove_when_too_many_notifications(self):
|
||||
Notification.objects.notify_user(self.user, 'dummy')
|
||||
@ -116,16 +116,16 @@ class TestUnreadCount(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user_1 = AuthUtils.create_user('magic_mike')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user_1,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user_1,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
|
||||
|
||||
# test notifications for mike
|
||||
Notification.objects.all().delete()
|
||||
Notification.objects.create(
|
||||
@ -146,19 +146,19 @@ class TestUnreadCount(TestCase):
|
||||
level="INFO",
|
||||
title="Job 3 Failed",
|
||||
message="Because it was broken"
|
||||
)
|
||||
)
|
||||
|
||||
cls.user_2 = AuthUtils.create_user('teh_kid')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user_2,
|
||||
'The Kid', '2',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user_2,
|
||||
'The Kid', '2',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
|
||||
|
||||
# Notifications for kid
|
||||
Notification.objects.create(
|
||||
user=cls.user_2,
|
||||
@ -166,7 +166,7 @@ class TestUnreadCount(TestCase):
|
||||
title="Job 6 Failed",
|
||||
message="Because it was broken"
|
||||
)
|
||||
|
||||
|
||||
def test_update_cache_when_not_in_cache(self, mock_cache):
|
||||
mock_cache.get.return_value = None
|
||||
|
||||
@ -174,9 +174,9 @@ class TestUnreadCount(TestCase):
|
||||
expected = 2
|
||||
self.assertEqual(result, expected)
|
||||
self.assertTrue(mock_cache.set.called)
|
||||
args, kwargs = mock_cache.set.call_args
|
||||
args, kwargs = mock_cache.set.call_args
|
||||
self.assertEqual(
|
||||
kwargs['key'],
|
||||
kwargs['key'],
|
||||
Notification.objects._user_notification_cache_key(self.user_1.pk)
|
||||
)
|
||||
self.assertEqual(kwargs['value'], expected)
|
||||
@ -195,12 +195,12 @@ class TestUnreadCount(TestCase):
|
||||
expected = -1
|
||||
self.assertEqual(result, expected)
|
||||
self.assertFalse(mock_cache.set.called)
|
||||
|
||||
|
||||
def test_can_invalidate_cache(self, mock_cache):
|
||||
Notification.objects.invalidate_user_notification_cache(self.user_1.pk)
|
||||
self.assertTrue(mock_cache.delete)
|
||||
args, kwargs = mock_cache.delete.call_args
|
||||
args, kwargs = mock_cache.delete.call_args
|
||||
self.assertEqual(
|
||||
kwargs['key'],
|
||||
kwargs['key'],
|
||||
Notification.objects._user_notification_cache_key(self.user_1.pk)
|
||||
)
|
||||
|
@ -15,16 +15,16 @@ class TestUserNotify(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('magic_mike')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.Notification.objects.invalidate_user_notification_cache')
|
||||
def test_save_will_invalidate_cache(self, mock_invalidate_user_notification_cache):
|
||||
obj = Notification.objects.notify_user(self.user, 'dummy')
|
||||
|
@ -20,20 +20,20 @@ class TestUserNotificationCount(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('magic_mike')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
|
||||
|
||||
def test_return_normal(self, mock_user_unread_count):
|
||||
unread_count = 42
|
||||
mock_user_unread_count.return_value = unread_count
|
||||
|
||||
|
||||
result = user_unread_notification_count(self.user)
|
||||
expected = unread_count
|
||||
self.assertEqual(result, expected)
|
||||
@ -43,7 +43,7 @@ class TestUserNotificationCount(TestCase):
|
||||
def test_return_error_if_non_user(self, mock_user_unread_count):
|
||||
unread_count = -1
|
||||
mock_user_unread_count.return_value = unread_count
|
||||
|
||||
|
||||
result = user_unread_notification_count('invalid')
|
||||
expected = unread_count
|
||||
self.assertEqual(result, expected)
|
||||
@ -60,9 +60,9 @@ class TestNotificationsRefreshTime(TestCase):
|
||||
result = notifications_refresh_time()
|
||||
expected = MY_NOTIFICATIONS_REFRESH_TIME
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
||||
@override_settings(NOTIFICATIONS_REFRESH_TIME=0)
|
||||
def test_refresh_time_can_be_zero(self):
|
||||
def test_refresh_time_can_be_zero(self):
|
||||
result = notifications_refresh_time()
|
||||
expected = 0
|
||||
self.assertEqual(result, expected)
|
||||
|
@ -19,23 +19,23 @@ class TestViews(TestCase):
|
||||
def setUpTestData(cls):
|
||||
cls.user = AuthUtils.create_user('magic_mike')
|
||||
AuthUtils.add_main_character(
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
cls.user,
|
||||
'Magic Mike',
|
||||
'1',
|
||||
corp_id='2',
|
||||
corp_name='Pole Riders',
|
||||
corp_ticker='PRIDE',
|
||||
alliance_id='3',
|
||||
alliance_name='RIDERS'
|
||||
)
|
||||
cls.factory = RequestFactory()
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.Notification.objects.user_unread_count')
|
||||
def test_user_notifications_count(self, mock_user_unread_count):
|
||||
unread_count = 42
|
||||
user_pk = 3
|
||||
mock_user_unread_count.return_value = unread_count
|
||||
|
||||
|
||||
request = self.factory.get(
|
||||
reverse('notifications:user_notifications_count', args=[user_pk])
|
||||
)
|
||||
@ -43,7 +43,7 @@ class TestViews(TestCase):
|
||||
|
||||
response = user_notifications_count(request, user_pk)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(mock_user_unread_count.called)
|
||||
self.assertTrue(mock_user_unread_count.called)
|
||||
expected = {'unread_count': unread_count}
|
||||
result = json.loads(response.content.decode(response.charset))
|
||||
self.assertDictEqual(result, expected)
|
||||
|
@ -10,8 +10,8 @@ urlpatterns = [
|
||||
url(r'^notifications/$', views.notification_list, name='list'),
|
||||
url(r'^notifications/(\w+)/$', views.notification_view, name='view'),
|
||||
url(
|
||||
r'^user_notifications_count/(?P<user_pk>\d+)/$',
|
||||
views.user_notifications_count,
|
||||
r'^user_notifications_count/(?P<user_pk>\d+)/$',
|
||||
views.user_notifications_count,
|
||||
name='user_notifications_count'
|
||||
),
|
||||
]
|
||||
|
@ -18,9 +18,9 @@ def notification_list(request):
|
||||
new_notifs = notifications_qs.filter(viewed=False)
|
||||
old_notifs = notifications_qs.filter(viewed=True)
|
||||
logger.debug(
|
||||
"User %s has %s unread and %s read notifications",
|
||||
request.user,
|
||||
len(new_notifs),
|
||||
"User %s has %s unread and %s read notifications",
|
||||
request.user,
|
||||
len(new_notifs),
|
||||
len(old_notifs)
|
||||
)
|
||||
context = {
|
||||
@ -33,8 +33,8 @@ def notification_list(request):
|
||||
@login_required
|
||||
def notification_view(request, notif_id):
|
||||
logger.debug(
|
||||
"notification_view called by user %s for notif_id %s",
|
||||
request.user,
|
||||
"notification_view called by user %s for notif_id %s",
|
||||
request.user,
|
||||
notif_id
|
||||
)
|
||||
notif = get_object_or_404(Notification, pk=notif_id)
|
||||
@ -46,7 +46,7 @@ def notification_view(request, notif_id):
|
||||
else:
|
||||
logger.warn(
|
||||
"User %s not authorized to view notif_id %s belonging to user %s",
|
||||
request.user,
|
||||
request.user,
|
||||
notif_id, notif.user
|
||||
)
|
||||
messages.error(request, _('You are not authorized to view that notification.'))
|
||||
@ -57,7 +57,7 @@ def notification_view(request, notif_id):
|
||||
def remove_notification(request, notif_id):
|
||||
logger.debug(
|
||||
"remove notification called by user %s for notif_id %s",
|
||||
request.user,
|
||||
request.user,
|
||||
notif_id
|
||||
)
|
||||
notif = get_object_or_404(Notification, pk=notif_id)
|
||||
@ -68,8 +68,8 @@ def remove_notification(request, notif_id):
|
||||
messages.success(request, _('Deleted notification.'))
|
||||
else:
|
||||
logger.error(
|
||||
"Unable to delete notif id %s for user %s - notif matching id not found.",
|
||||
notif_id,
|
||||
"Unable to delete notif id %s for user %s - notif matching id not found.",
|
||||
notif_id,
|
||||
request.user
|
||||
)
|
||||
messages.error(request, _('Failed to locate notification.'))
|
||||
@ -94,9 +94,9 @@ def delete_all_read(request):
|
||||
|
||||
def user_notifications_count(request, user_pk: int):
|
||||
"""returns to notifications count for the give user as JSON
|
||||
|
||||
|
||||
This view is public and does not require login
|
||||
"""
|
||||
unread_count = Notification.objects.user_unread_count(user_pk)
|
||||
unread_count = Notification.objects.user_unread_count(user_pk)
|
||||
data = {'unread_count': unread_count}
|
||||
return JsonResponse(data, safe=False)
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% load evelinks %}
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<td>
|
||||
{{ type }}: {{ name }}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@ -20,6 +20,6 @@
|
||||
{% else %}
|
||||
(unknown)
|
||||
{% endif %}
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -104,7 +104,7 @@ TEMPLATES = [
|
||||
'django.template.context_processors.i18n',
|
||||
'django.template.context_processors.media',
|
||||
'django.template.context_processors.static',
|
||||
'django.template.context_processors.tz',
|
||||
'django.template.context_processors.tz',
|
||||
'allianceauth.context_processors.auth_settings',
|
||||
],
|
||||
},
|
||||
|
@ -19,7 +19,7 @@ DEBUG = False
|
||||
|
||||
# Add any additional apps to this list.
|
||||
INSTALLED_APPS += [
|
||||
|
||||
|
||||
]
|
||||
|
||||
# To change the logging level for extensions, uncomment the following line.
|
||||
|
@ -3,9 +3,9 @@ from django.contrib import admin
|
||||
|
||||
from allianceauth import hooks
|
||||
from allianceauth.authentication.admin import (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
user_main_organization,
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
user_main_organization,
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter
|
||||
)
|
||||
@ -22,17 +22,17 @@ class ServicesUserAdmin(admin.ModelAdmin):
|
||||
|
||||
search_fields = ('user__username',)
|
||||
ordering = ('user__username',)
|
||||
list_select_related = True
|
||||
list_select_related = True
|
||||
list_display = (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
'_state',
|
||||
user_main_organization,
|
||||
user_main_organization,
|
||||
'_date_joined'
|
||||
)
|
||||
list_filter = (
|
||||
list_filter = (
|
||||
'user__profile__state',
|
||||
MainCorporationsFilter,
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter,
|
||||
'user__date_joined',
|
||||
)
|
||||
@ -45,7 +45,7 @@ class ServicesUserAdmin(admin.ModelAdmin):
|
||||
|
||||
def _date_joined(self, obj):
|
||||
return obj.user.date_joined
|
||||
|
||||
|
||||
_date_joined.short_description = 'date joined'
|
||||
_date_joined.admin_order_field = 'user__date_joined'
|
||||
|
||||
|
@ -139,11 +139,11 @@ class MenuItemHook:
|
||||
self.url_name = url_name
|
||||
self.template = 'public/menuitem.html'
|
||||
self.order = order if order is not None else 9999
|
||||
|
||||
|
||||
# count is an integer shown next to the menu item as badge when count != None
|
||||
# apps need to set the count in their child class, e.g. in render() method
|
||||
self.count = None
|
||||
|
||||
|
||||
navactive = navactive or []
|
||||
navactive.append(url_name)
|
||||
self.navactive = navactive
|
||||
|
@ -6,7 +6,7 @@ from allianceauth.authentication.models import State
|
||||
class NameFormatConfig(models.Model):
|
||||
service_name = models.CharField(max_length=100, blank=False)
|
||||
default_to_username = models.BooleanField(
|
||||
default=True,
|
||||
default=True,
|
||||
help_text=
|
||||
'If a user has no main_character, '
|
||||
'default to using their Auth username instead.'
|
||||
@ -20,10 +20,10 @@ class NameFormatConfig(models.Model):
|
||||
'topic "Services Name Formats".'
|
||||
)
|
||||
states = models.ManyToManyField(
|
||||
State,
|
||||
State,
|
||||
help_text=
|
||||
"States to apply this format to. You should only have one "
|
||||
"formatter for each state for each service."
|
||||
"formatter for each state for each service."
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -12,15 +12,15 @@ logger = LoggerAddTag(logging.getLogger(__name__), __title__)
|
||||
|
||||
|
||||
@admin.register(DiscordUser)
|
||||
class DiscordUserAdmin(ServicesUserAdmin):
|
||||
class DiscordUserAdmin(ServicesUserAdmin):
|
||||
search_fields = ServicesUserAdmin.search_fields + ('uid', 'username')
|
||||
list_display = ServicesUserAdmin.list_display + ('activated', '_username', '_uid')
|
||||
list_filter = ServicesUserAdmin.list_filter + ('activated',)
|
||||
ordering = ('-activated',)
|
||||
|
||||
|
||||
def _uid(self, obj):
|
||||
return obj.uid
|
||||
|
||||
|
||||
_uid.short_description = 'Discord ID (UID)'
|
||||
_uid.admin_order_field = 'uid'
|
||||
|
||||
@ -29,6 +29,6 @@ class DiscordUserAdmin(ServicesUserAdmin):
|
||||
return f'{obj.username}#{obj.discriminator}'
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
_username.short_description = 'Discord Username'
|
||||
_username.admin_order_field = 'username'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from allianceauth import hooks
|
||||
@ -33,11 +33,11 @@ class DiscordService(ServicesHook):
|
||||
if self.user_has_account(user):
|
||||
logger.debug('Deleting user %s %s account', user, self.name)
|
||||
tasks.delete_user.apply_async(
|
||||
kwargs={'user_pk': user.pk, 'notify_user': notify_user},
|
||||
kwargs={'user_pk': user.pk, 'notify_user': notify_user},
|
||||
priority=SINGLE_TASK_PRIORITY
|
||||
)
|
||||
|
||||
def render_services_ctrl(self, request):
|
||||
)
|
||||
|
||||
def render_services_ctrl(self, request):
|
||||
if self.user_has_account(request.user):
|
||||
user_has_account = True
|
||||
username = request.user.discord.username
|
||||
@ -51,12 +51,12 @@ class DiscordService(ServicesHook):
|
||||
user_has_account = False
|
||||
|
||||
return render_to_string(
|
||||
self.service_ctrl_template,
|
||||
self.service_ctrl_template,
|
||||
{
|
||||
'server_name': DiscordUser.objects.server_name(),
|
||||
'user_has_account': user_has_account,
|
||||
'discord_username': discord_username
|
||||
},
|
||||
},
|
||||
request=request
|
||||
)
|
||||
|
||||
@ -71,15 +71,15 @@ class DiscordService(ServicesHook):
|
||||
tasks.update_nickname.apply_async(
|
||||
kwargs={
|
||||
'user_pk': user.pk,
|
||||
# since the new nickname is not yet in the DB we need to
|
||||
# since the new nickname is not yet in the DB we need to
|
||||
# provide it manually to the task
|
||||
'nickname': DiscordUser.objects.user_formatted_nick(user)
|
||||
},
|
||||
},
|
||||
priority=SINGLE_TASK_PRIORITY
|
||||
)
|
||||
|
||||
def sync_nicknames_bulk(self, users: list):
|
||||
"""Sync nickname for a list of users in bulk.
|
||||
"""Sync nickname for a list of users in bulk.
|
||||
Preferred over sync_nickname(), because it will not break the rate limit
|
||||
"""
|
||||
logger.debug(
|
||||
@ -92,21 +92,21 @@ class DiscordService(ServicesHook):
|
||||
logger.debug('Update all %s groups called', self.name)
|
||||
tasks.update_all_groups.delay()
|
||||
|
||||
def update_groups(self, user):
|
||||
logger.debug('Processing %s groups for %s', self.name, user)
|
||||
def update_groups(self, user):
|
||||
logger.debug('Processing %s groups for %s', self.name, user)
|
||||
if self.user_has_account(user):
|
||||
tasks.update_groups.apply_async(
|
||||
kwargs={
|
||||
'user_pk': user.pk,
|
||||
# since state changes may not yet be in the DB we need to
|
||||
# since state changes may not yet be in the DB we need to
|
||||
# provide the new state name manually to the task
|
||||
'state_name': user.profile.state.name
|
||||
},
|
||||
},
|
||||
priority=SINGLE_TASK_PRIORITY
|
||||
)
|
||||
|
||||
def update_groups_bulk(self, users: list):
|
||||
"""Updates groups for a list of users in bulk.
|
||||
"""Updates groups for a list of users in bulk.
|
||||
Preferred over update_groups(), because it will not break the rate limit
|
||||
"""
|
||||
logger.debug(
|
||||
@ -114,7 +114,7 @@ class DiscordService(ServicesHook):
|
||||
)
|
||||
user_pks = [user.pk for user in users]
|
||||
tasks.update_groups_bulk.delay(user_pks)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def user_has_account(user: User) -> bool:
|
||||
result = DiscordUser.objects.user_has_account(user)
|
||||
|
@ -26,7 +26,7 @@ DISCORD_OAUTH_TOKEN_URL = clean_setting(
|
||||
'DISCORD_OAUTH_TOKEN_URL', 'https://discord.com/api/oauth2/token'
|
||||
)
|
||||
|
||||
# How long the Discord guild names retrieved from the server are
|
||||
# How long the Discord guild names retrieved from the server are
|
||||
# caches locally in seconds.
|
||||
DISCORD_GUILD_NAME_CACHE_MAX_AGE = clean_setting(
|
||||
'DISCORD_GUILD_NAME_CACHE_MAX_AGE', 3600 * 24
|
||||
@ -38,7 +38,7 @@ DISCORD_ROLES_CACHE_MAX_AGE = clean_setting(
|
||||
)
|
||||
|
||||
# Turns off creation of new roles. In case the rate limit for creating roles is
|
||||
# exhausted, this setting allows the Discord service to continue to function
|
||||
# exhausted, this setting allows the Discord service to continue to function
|
||||
# and wait out the reset. Rate limit is about 250 per 48 hrs.
|
||||
DISCORD_DISABLE_ROLE_CREATION = clean_setting(
|
||||
'DISCORD_DISABLE_ROLE_CREATION', False
|
||||
|
@ -19,9 +19,9 @@ from .app_settings import (
|
||||
DISCORD_API_TIMEOUT_READ,
|
||||
DISCORD_DISABLE_ROLE_CREATION,
|
||||
DISCORD_GUILD_NAME_CACHE_MAX_AGE,
|
||||
DISCORD_OAUTH_BASE_URL,
|
||||
DISCORD_OAUTH_TOKEN_URL,
|
||||
DISCORD_ROLES_CACHE_MAX_AGE,
|
||||
DISCORD_OAUTH_BASE_URL,
|
||||
DISCORD_OAUTH_TOKEN_URL,
|
||||
DISCORD_ROLES_CACHE_MAX_AGE,
|
||||
)
|
||||
from .exceptions import DiscordRateLimitExhausted, DiscordTooManyRequestsError
|
||||
from .helpers import DiscordRoles
|
||||
@ -31,7 +31,7 @@ from ..utils import LoggerAddTag
|
||||
logger = LoggerAddTag(logging.getLogger(__name__), __title__)
|
||||
|
||||
# max requests that can be executed until reset
|
||||
RATE_LIMIT_MAX_REQUESTS = 5
|
||||
RATE_LIMIT_MAX_REQUESTS = 5
|
||||
|
||||
# Time until remaining requests are reset
|
||||
RATE_LIMIT_RESETS_AFTER = 5000
|
||||
@ -39,7 +39,7 @@ RATE_LIMIT_RESETS_AFTER = 5000
|
||||
# Delay used for API backoff in case no info returned from API on 429s
|
||||
DEFAULT_BACKOFF_DELAY = 5000
|
||||
|
||||
# additional duration to compensate for potential clock discrepancies
|
||||
# additional duration to compensate for potential clock discrepancies
|
||||
# with the Discord server
|
||||
DURATION_CONTINGENCY = 500
|
||||
|
||||
@ -51,23 +51,23 @@ WAIT_THRESHOLD = 250
|
||||
MINIMUM_BLOCKING_WAIT = 50
|
||||
|
||||
# If the rate limit resets soon we will wait it out and then retry to
|
||||
# either get a remaining request from our cached counter
|
||||
# either get a remaining request from our cached counter
|
||||
# or again wait out a short reset time and retry again.
|
||||
# This could happen several times within a high concurrency situation,
|
||||
# This could happen several times within a high concurrency situation,
|
||||
# but must fail after x tries to avoid an infinite loop
|
||||
RATE_LIMIT_RETRIES = 1000
|
||||
|
||||
|
||||
class DiscordClient:
|
||||
"""This class provides a web client for interacting with the Discord API
|
||||
|
||||
|
||||
The client has rate limiting that supports concurrency.
|
||||
This means it is able to ensure the API rate limit is not violated,
|
||||
This means it is able to ensure the API rate limit is not violated,
|
||||
even when used concurrently, e.g. with multiple parallel celery tasks.
|
||||
|
||||
In addition the client support proper API backoff.
|
||||
|
||||
Synchronization of rate limit infos accross multiple processes
|
||||
Synchronization of rate limit infos accross multiple processes
|
||||
is implemented with Redis and thus requires Redis as Django cache backend.
|
||||
|
||||
All durations are in milliseconds.
|
||||
@ -79,25 +79,25 @@ class DiscordClient:
|
||||
_KEY_GLOBAL_RATE_LIMIT_REMAINING = 'DISCORD_GLOBAL_RATE_LIMIT_REMAINING'
|
||||
_KEYPREFIX_GUILD_NAME = 'DISCORD_GUILD_NAME'
|
||||
_KEYPREFIX_GUILD_ROLES = 'DISCORD_GUILD_ROLES'
|
||||
_KEYPREFIX_ROLE_NAME = 'DISCORD_ROLE_NAME'
|
||||
_KEYPREFIX_ROLE_NAME = 'DISCORD_ROLE_NAME'
|
||||
_NICK_MAX_CHARS = 32
|
||||
|
||||
|
||||
_HTTP_STATUS_CODE_NOT_FOUND = 404
|
||||
_HTTP_STATUS_CODE_RATE_LIMITED = 429
|
||||
_DISCORD_STATUS_CODE_UNKNOWN_MEMBER = 10007
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
access_token: str,
|
||||
redis: Redis = None,
|
||||
self,
|
||||
access_token: str,
|
||||
redis: Redis = None,
|
||||
is_rate_limited: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
"""
|
||||
Params:
|
||||
- access_token: Discord access token used to authenticate all calls to the API
|
||||
- redis: Redis instance to be used.
|
||||
- redis: Redis instance to be used.
|
||||
- is_rate_limited: Set to False to run of rate limiting (use with care)
|
||||
If not specified will try to use the Redis instance
|
||||
If not specified will try to use the Redis instance
|
||||
from the default Django cache backend.
|
||||
"""
|
||||
self._access_token = str(access_token)
|
||||
@ -116,7 +116,7 @@ class DiscordClient:
|
||||
lua_1 = """
|
||||
if redis.call("exists", KEYS[1]) == 0 then
|
||||
redis.call("set", KEYS[1], ARGV[1], 'px', ARGV[2])
|
||||
end
|
||||
end
|
||||
return redis.call("decr", KEYS[1])
|
||||
"""
|
||||
self.__redis_script_decr_or_set = self._redis.register_script(lua_1)
|
||||
@ -138,24 +138,24 @@ class DiscordClient:
|
||||
@property
|
||||
def is_rate_limited(self):
|
||||
return self._is_rate_limited
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f'{type(self).__name__}(access_token=...{self.access_token[-5:]})'
|
||||
|
||||
def _redis_decr_or_set(self, name: str, value: str, px: int) -> bool:
|
||||
"""decreases the key value if it exists and returns the result
|
||||
else sets the key
|
||||
|
||||
|
||||
Implemented as Lua script to ensure atomicity.
|
||||
"""
|
||||
return self.__redis_script_decr_or_set(
|
||||
keys=[str(name)], args=[str(value), int(px)]
|
||||
)
|
||||
|
||||
|
||||
def _redis_set_if_longer(self, name: str, value: str, px: int) -> bool:
|
||||
"""like set, but only goes through if either key doesn't exist
|
||||
or px would be extended.
|
||||
|
||||
"""like set, but only goes through if either key doesn't exist
|
||||
or px would be extended.
|
||||
|
||||
Implemented as Lua script to ensure atomicity.
|
||||
"""
|
||||
return self.__redis_script_set_longer(
|
||||
@ -163,7 +163,7 @@ class DiscordClient:
|
||||
)
|
||||
|
||||
# users
|
||||
|
||||
|
||||
def current_user(self) -> dict:
|
||||
"""returns the user belonging to the current access_token"""
|
||||
authorization = f'Bearer {self.access_token}'
|
||||
@ -171,7 +171,7 @@ class DiscordClient:
|
||||
method='get', route='users/@me', authorization=authorization
|
||||
)
|
||||
return r.json()
|
||||
|
||||
|
||||
# guild
|
||||
|
||||
def guild_infos(self, guild_id: int) -> dict:
|
||||
@ -181,7 +181,7 @@ class DiscordClient:
|
||||
return r.json()
|
||||
|
||||
def guild_name(self, guild_id: int, use_cache: bool = True) -> str:
|
||||
"""returns the name of this guild (cached)
|
||||
"""returns the name of this guild (cached)
|
||||
or an empty string if something went wrong
|
||||
|
||||
Params:
|
||||
@ -198,8 +198,8 @@ class DiscordClient:
|
||||
if 'name' in guild_infos:
|
||||
guild_name = guild_infos['name']
|
||||
self._redis.set(
|
||||
name=key_name,
|
||||
value=guild_name,
|
||||
name=key_name,
|
||||
value=guild_name,
|
||||
ex=DISCORD_GUILD_NAME_CACHE_MAX_AGE
|
||||
)
|
||||
else:
|
||||
@ -208,7 +208,7 @@ class DiscordClient:
|
||||
return guild_name
|
||||
|
||||
@classmethod
|
||||
def _guild_name_cache_key(cls, guild_id: int) -> str:
|
||||
def _guild_name_cache_key(cls, guild_id: int) -> str:
|
||||
"""Returns key for accessing role given by name in the role cache"""
|
||||
gen_key = DiscordClient._generate_hash(f'{guild_id}')
|
||||
return f'{cls._KEYPREFIX_GUILD_NAME}__{gen_key}'
|
||||
@ -217,38 +217,38 @@ class DiscordClient:
|
||||
|
||||
def guild_roles(self, guild_id: int, use_cache: bool = True) -> list:
|
||||
"""Returns the list of all roles for this guild
|
||||
|
||||
|
||||
If use_cache is set to False it will always hit the API to retrieve
|
||||
fresh data and update the cache
|
||||
"""
|
||||
cache_key = self._guild_roles_cache_key(guild_id)
|
||||
if use_cache:
|
||||
if use_cache:
|
||||
roles_raw = self._redis.get(name=cache_key)
|
||||
if roles_raw:
|
||||
logger.debug('Returning roles for guild %s from cache', guild_id)
|
||||
return json.loads(self._redis_decode(roles_raw))
|
||||
else:
|
||||
logger.debug('No roles for guild %s in cache', guild_id)
|
||||
|
||||
|
||||
route = f"guilds/{guild_id}/roles"
|
||||
r = self._api_request(method='get', route=route)
|
||||
r = self._api_request(method='get', route=route)
|
||||
roles = r.json()
|
||||
if roles and isinstance(roles, list):
|
||||
self._redis.set(
|
||||
name=cache_key,
|
||||
value=json.dumps(roles),
|
||||
name=cache_key,
|
||||
value=json.dumps(roles),
|
||||
ex=DISCORD_ROLES_CACHE_MAX_AGE
|
||||
)
|
||||
return roles
|
||||
|
||||
def create_guild_role(self, guild_id: int, role_name: str, **kwargs) -> dict:
|
||||
"""Create a new guild role with the given name.
|
||||
"""Create a new guild role with the given name.
|
||||
See official documentation for additional optional parameters.
|
||||
|
||||
Note that Discord allows the creation of multiple roles with the same name,
|
||||
so to avoid duplicates it's important to check existing roles
|
||||
so to avoid duplicates it's important to check existing roles
|
||||
before creating new one
|
||||
|
||||
|
||||
returns a new role dict on success
|
||||
"""
|
||||
route = f"guilds/{guild_id}/roles"
|
||||
@ -269,9 +269,9 @@ class DiscordClient:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _invalidate_guild_roles_cache(self, guild_id: int) -> None:
|
||||
cache_key = self._guild_roles_cache_key(guild_id)
|
||||
|
||||
def _invalidate_guild_roles_cache(self, guild_id: int) -> None:
|
||||
cache_key = self._guild_roles_cache_key(guild_id)
|
||||
self._redis.delete(cache_key)
|
||||
logger.debug('Guild roles cache invalidated')
|
||||
|
||||
@ -280,7 +280,7 @@ class DiscordClient:
|
||||
"""Returns key for accessing cached roles for a guild"""
|
||||
gen_key = cls._generate_hash(f'{guild_id}')
|
||||
return f'{cls._KEYPREFIX_GUILD_ROLES}__{gen_key}'
|
||||
|
||||
|
||||
def match_role_from_name(self, guild_id: int, role_name: str) -> dict:
|
||||
"""returns Discord role matching the given name or an empty dict"""
|
||||
guild_roles = DiscordRoles(self.guild_roles(guild_id))
|
||||
@ -288,12 +288,12 @@ class DiscordClient:
|
||||
|
||||
def match_or_create_roles_from_names(self, guild_id: int, role_names: list) -> list:
|
||||
"""returns Discord roles matching the given names
|
||||
|
||||
|
||||
Returns as list of tuple of role and created flag
|
||||
|
||||
Will try to match with existing roles names
|
||||
Non-existing roles will be created, then created flag will be True
|
||||
|
||||
|
||||
Params:
|
||||
- guild_id: ID of guild
|
||||
- role_names: list of name strings each defining a role
|
||||
@ -305,7 +305,7 @@ class DiscordClient:
|
||||
}
|
||||
for role_name in role_names_cleaned:
|
||||
role, created = self.match_or_create_role_from_name(
|
||||
guild_id=guild_id,
|
||||
guild_id=guild_id,
|
||||
role_name=DiscordRoles.sanitize_role_name(role_name),
|
||||
guild_roles=guild_roles
|
||||
)
|
||||
@ -321,46 +321,46 @@ class DiscordClient:
|
||||
"""returns Discord role matching the given name
|
||||
|
||||
Returns as tuple of role and created flag
|
||||
|
||||
|
||||
Will try to match with existing roles names
|
||||
Non-existing roles will be created, then created flag will be True
|
||||
|
||||
|
||||
Params:
|
||||
- guild_id: ID of guild
|
||||
- role_name: strings defining name of a role
|
||||
- guild_roles: All known guild roles as DiscordRoles object.
|
||||
Helps to void redundant lookups of guild roles
|
||||
- guild_roles: All known guild roles as DiscordRoles object.
|
||||
Helps to void redundant lookups of guild roles
|
||||
when this method is used multiple times.
|
||||
"""
|
||||
if not isinstance(role_name, str):
|
||||
raise TypeError('role_name must be of type string')
|
||||
|
||||
created = False
|
||||
created = False
|
||||
if guild_roles is None:
|
||||
guild_roles = DiscordRoles(self.guild_roles(guild_id))
|
||||
role = guild_roles.role_by_name(role_name)
|
||||
if not role:
|
||||
if not DISCORD_DISABLE_ROLE_CREATION:
|
||||
logger.debug('Need to create missing role: %s', role_name)
|
||||
role = self.create_guild_role(guild_id, role_name)
|
||||
role = self.create_guild_role(guild_id, role_name)
|
||||
created = True
|
||||
else:
|
||||
role = None
|
||||
|
||||
|
||||
return role, created
|
||||
|
||||
# guild members
|
||||
|
||||
def add_guild_member(
|
||||
self,
|
||||
guild_id: int,
|
||||
user_id: int,
|
||||
access_token: str,
|
||||
role_ids: list = None,
|
||||
self,
|
||||
guild_id: int,
|
||||
user_id: int,
|
||||
access_token: str,
|
||||
role_ids: list = None,
|
||||
nick: str = None
|
||||
) -> bool:
|
||||
) -> bool:
|
||||
"""Adds a user to the guilds.
|
||||
|
||||
|
||||
Returns:
|
||||
- True when a new user was added
|
||||
- None if the user already existed
|
||||
@ -370,13 +370,13 @@ class DiscordClient:
|
||||
data = {
|
||||
'access_token': str(access_token)
|
||||
}
|
||||
if role_ids:
|
||||
if role_ids:
|
||||
data['roles'] = self._sanitize_role_ids(role_ids)
|
||||
|
||||
if nick:
|
||||
data['nick'] = str(nick)[:self._NICK_MAX_CHARS]
|
||||
|
||||
r = self._api_request(method='put', route=route, data=data)
|
||||
r = self._api_request(method='put', route=route, data=data)
|
||||
r.raise_for_status()
|
||||
if r.status_code == 201:
|
||||
return True
|
||||
@ -384,10 +384,10 @@ class DiscordClient:
|
||||
return None
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def guild_member(self, guild_id: int, user_id: int) -> dict:
|
||||
"""returns the user info for a guild member
|
||||
|
||||
|
||||
or None if the user is not a member of the guild
|
||||
"""
|
||||
route = f'guilds/{guild_id}/members/{user_id}'
|
||||
@ -411,14 +411,14 @@ class DiscordClient:
|
||||
"""
|
||||
if not role_ids and not nick:
|
||||
raise ValueError('Must specify role_ids or nick')
|
||||
|
||||
|
||||
if role_ids and not isinstance(role_ids, list):
|
||||
raise TypeError('role_ids must be a list type')
|
||||
|
||||
data = dict()
|
||||
if role_ids:
|
||||
if role_ids:
|
||||
data['roles'] = self._sanitize_role_ids(role_ids)
|
||||
|
||||
|
||||
if nick:
|
||||
data['nick'] = self._sanitize_nick(nick)
|
||||
|
||||
@ -431,7 +431,7 @@ class DiscordClient:
|
||||
return None
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
|
||||
if r.status_code == 204:
|
||||
return True
|
||||
else:
|
||||
@ -439,7 +439,7 @@ class DiscordClient:
|
||||
|
||||
def remove_guild_member(self, guild_id: int, user_id: int) -> bool:
|
||||
"""Remove a member from a guild
|
||||
|
||||
|
||||
Returns:
|
||||
- True when successful
|
||||
- None if member does not exist
|
||||
@ -448,7 +448,7 @@ class DiscordClient:
|
||||
route = f"guilds/{guild_id}/members/{user_id}"
|
||||
r = self._api_request(
|
||||
method='delete', route=route, raise_for_status=False
|
||||
)
|
||||
)
|
||||
if self._is_member_unknown_error(r):
|
||||
logger.warning('User ID %s is not a member of this guild', user_id)
|
||||
return None
|
||||
@ -461,12 +461,12 @@ class DiscordClient:
|
||||
return False
|
||||
|
||||
# Guild member roles
|
||||
|
||||
|
||||
def add_guild_member_role(
|
||||
self, guild_id: int, user_id: int, role_id: int
|
||||
) -> bool:
|
||||
) -> bool:
|
||||
"""Adds a role to a guild member
|
||||
|
||||
|
||||
Returns:
|
||||
- True when successful
|
||||
- None if member does not exist
|
||||
@ -479,7 +479,7 @@ class DiscordClient:
|
||||
return None
|
||||
else:
|
||||
r.raise_for_status()
|
||||
|
||||
|
||||
if r.status_code == 204:
|
||||
return True
|
||||
else:
|
||||
@ -489,7 +489,7 @@ class DiscordClient:
|
||||
self, guild_id: int, user_id: int, role_id: int
|
||||
) -> bool:
|
||||
"""Removes a role to a guild member
|
||||
|
||||
|
||||
Returns:
|
||||
- True when successful
|
||||
- None if member does not exist
|
||||
@ -517,31 +517,31 @@ class DiscordClient:
|
||||
)
|
||||
except (ValueError, KeyError):
|
||||
result = False
|
||||
|
||||
|
||||
return result
|
||||
|
||||
# Internal methods
|
||||
|
||||
def _api_request(
|
||||
self,
|
||||
method: str,
|
||||
route: str,
|
||||
data: dict = None,
|
||||
self,
|
||||
method: str,
|
||||
route: str,
|
||||
data: dict = None,
|
||||
authorization: str = None,
|
||||
raise_for_status: bool = True
|
||||
) -> requests.Response:
|
||||
"""Core method for performing all API calls"""
|
||||
uid = uuid1().hex
|
||||
|
||||
|
||||
if not hasattr(requests, method):
|
||||
raise ValueError('Invalid method: %s' % method)
|
||||
|
||||
if not authorization:
|
||||
authorization = f'Bot {self.access_token}'
|
||||
|
||||
self._handle_ongoing_api_backoff(uid)
|
||||
|
||||
self._handle_ongoing_api_backoff(uid)
|
||||
if self.is_rate_limited:
|
||||
self._ensure_rate_limed_not_exhausted(uid)
|
||||
self._ensure_rate_limed_not_exhausted(uid)
|
||||
headers = {
|
||||
'User-Agent': f'{AUTH_TITLE} ({__url__}, {__version__})',
|
||||
'accept': 'application/json',
|
||||
@ -559,21 +559,21 @@ class DiscordClient:
|
||||
}
|
||||
if data:
|
||||
args['json'] = data
|
||||
|
||||
|
||||
logger.info('%s: sending %s request to url \'%s\'', uid, method.upper(), url)
|
||||
logger.debug('%s: request headers: %s', uid, headers)
|
||||
r = getattr(requests, method)(**args)
|
||||
logger.debug(
|
||||
'%s: returned status code %d with headers: %s',
|
||||
uid,
|
||||
r.status_code,
|
||||
'%s: returned status code %d with headers: %s',
|
||||
uid,
|
||||
r.status_code,
|
||||
r.headers
|
||||
)
|
||||
logger.debug('%s: response:\n%s', uid, r.text)
|
||||
if not r.ok:
|
||||
logger.warning(
|
||||
'%s: Discord API returned error code %d and this response: %s',
|
||||
uid,
|
||||
uid,
|
||||
r.status_code,
|
||||
r.text
|
||||
)
|
||||
@ -582,15 +582,15 @@ class DiscordClient:
|
||||
self._handle_new_api_backoff(r, uid)
|
||||
|
||||
self._report_rate_limit_from_api(r, uid)
|
||||
|
||||
|
||||
if raise_for_status:
|
||||
r.raise_for_status()
|
||||
|
||||
|
||||
return r
|
||||
|
||||
def _handle_ongoing_api_backoff(self, uid: str) -> None:
|
||||
def _handle_ongoing_api_backoff(self, uid: str) -> None:
|
||||
"""checks if api is currently on backoff
|
||||
if on backoff: will do a blocking wait if it expires soon,
|
||||
if on backoff: will do a blocking wait if it expires soon,
|
||||
else raises exception
|
||||
"""
|
||||
global_backoff_duration = self._redis.pttl(self._KEY_GLOBAL_BACKOFF_UNTIL)
|
||||
@ -611,52 +611,52 @@ class DiscordClient:
|
||||
raise DiscordTooManyRequestsError(retry_after=global_backoff_duration)
|
||||
|
||||
def _ensure_rate_limed_not_exhausted(self, uid: str) -> int:
|
||||
"""ensures that the rate limit is not exhausted
|
||||
if exhausted: will do a blocking wait if rate limit resets soon,
|
||||
"""ensures that the rate limit is not exhausted
|
||||
if exhausted: will do a blocking wait if rate limit resets soon,
|
||||
else raises exception
|
||||
|
||||
returns requests remaining on success
|
||||
"""
|
||||
for _ in range(RATE_LIMIT_RETRIES):
|
||||
requests_remaining = self._redis_decr_or_set(
|
||||
name=self._KEY_GLOBAL_RATE_LIMIT_REMAINING,
|
||||
value=RATE_LIMIT_MAX_REQUESTS,
|
||||
name=self._KEY_GLOBAL_RATE_LIMIT_REMAINING,
|
||||
value=RATE_LIMIT_MAX_REQUESTS,
|
||||
px=RATE_LIMIT_RESETS_AFTER + DURATION_CONTINGENCY
|
||||
)
|
||||
)
|
||||
resets_in = max(
|
||||
MINIMUM_BLOCKING_WAIT,
|
||||
MINIMUM_BLOCKING_WAIT,
|
||||
self._redis.pttl(self._KEY_GLOBAL_RATE_LIMIT_REMAINING)
|
||||
)
|
||||
if requests_remaining >= 0:
|
||||
logger.debug(
|
||||
'%s: Got one of %d remaining requests until reset in %s ms',
|
||||
uid,
|
||||
uid,
|
||||
requests_remaining + 1,
|
||||
resets_in
|
||||
)
|
||||
return requests_remaining
|
||||
|
||||
elif resets_in < WAIT_THRESHOLD:
|
||||
elif resets_in < WAIT_THRESHOLD:
|
||||
sleep(resets_in / 1000)
|
||||
logger.debug(
|
||||
'%s: No requests remaining until reset in %d ms. '
|
||||
'Waiting for reset.',
|
||||
uid,
|
||||
uid,
|
||||
resets_in
|
||||
)
|
||||
continue
|
||||
|
||||
else:
|
||||
else:
|
||||
logger.debug(
|
||||
'%s: No requests remaining until reset in %d ms. '
|
||||
'Raising exception.',
|
||||
uid,
|
||||
uid,
|
||||
resets_in
|
||||
)
|
||||
raise DiscordRateLimitExhausted(resets_in)
|
||||
|
||||
raise RuntimeError('Failed to handle rate limit after after too tries.')
|
||||
|
||||
|
||||
def _handle_new_api_backoff(self, r: requests.Response, uid: str) -> None:
|
||||
"""raises exception for new API backoff error"""
|
||||
response = r.json()
|
||||
@ -669,7 +669,7 @@ class DiscordClient:
|
||||
else:
|
||||
retry_after = DEFAULT_BACKOFF_DELAY
|
||||
self._redis_set_if_longer(
|
||||
name=self._KEY_GLOBAL_BACKOFF_UNTIL,
|
||||
name=self._KEY_GLOBAL_BACKOFF_UNTIL,
|
||||
value='GLOBAL_API_BACKOFF',
|
||||
px=retry_after
|
||||
)
|
||||
@ -684,8 +684,8 @@ class DiscordClient:
|
||||
"""Tries to log the current rate limit reported from API"""
|
||||
if (
|
||||
logger.getEffectiveLevel() <= logging.DEBUG
|
||||
and 'x-ratelimit-limit' in r.headers
|
||||
and 'x-ratelimit-remaining' in r.headers
|
||||
and 'x-ratelimit-limit' in r.headers
|
||||
and 'x-ratelimit-remaining' in r.headers
|
||||
and 'x-ratelimit-reset-after' in r.headers
|
||||
):
|
||||
try:
|
||||
@ -701,7 +701,7 @@ class DiscordClient:
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _redis_decode(value: str) -> str:
|
||||
"""Decodes a string from Redis and passes through None and Booleans"""
|
||||
|
@ -3,18 +3,18 @@ import math
|
||||
|
||||
class DiscordClientException(Exception):
|
||||
"""Base Exception for the Discord client"""
|
||||
|
||||
|
||||
|
||||
class DiscordApiBackoff(DiscordClientException):
|
||||
"""Exception signaling we need to backoff from sending requests to the API for now
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, retry_after: int):
|
||||
"""
|
||||
:param retry_after: int time to retry after in milliseconds
|
||||
"""
|
||||
super().__init__()
|
||||
self.retry_after = int(retry_after)
|
||||
self.retry_after = int(retry_after)
|
||||
|
||||
@property
|
||||
def retry_after_seconds(self):
|
||||
@ -22,12 +22,12 @@ class DiscordApiBackoff(DiscordClientException):
|
||||
|
||||
|
||||
class DiscordRateLimitExhausted(DiscordApiBackoff):
|
||||
"""Exception signaling that the total number of requests allowed under the
|
||||
"""Exception signaling that the total number of requests allowed under the
|
||||
current rate limit have been exhausted and weed to wait until next reset.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class DiscordTooManyRequestsError(DiscordApiBackoff):
|
||||
"""API has responded with a 429 Too Many Requests Error.
|
||||
"""API has responded with a 429 Too Many Requests Error.
|
||||
Need to backoff for now.
|
||||
"""
|
||||
"""
|
||||
|
@ -3,10 +3,10 @@ from copy import copy
|
||||
|
||||
class DiscordRoles:
|
||||
"""Container class that helps dealing with Discord roles.
|
||||
|
||||
|
||||
Objects of this class are immutable and work in many ways like sets.
|
||||
|
||||
Ideally objects are initialized from raw API responses,
|
||||
|
||||
Ideally objects are initialized from raw API responses,
|
||||
e.g. from DiscordClient.guild.roles()
|
||||
"""
|
||||
_ROLE_NAME_MAX_CHARS = 100
|
||||
@ -21,7 +21,7 @@ class DiscordRoles:
|
||||
self._assert_valid_role(role)
|
||||
self._roles[int(role['id'])] = role
|
||||
self._roles_by_name[self.sanitize_role_name(role['name'])] = role
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, type(self)):
|
||||
return self.ids() == other.ids()
|
||||
@ -29,31 +29,31 @@ class DiscordRoles:
|
||||
|
||||
def __hash__(self):
|
||||
return hash(tuple(sorted(self._roles.keys())))
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
for role in self._roles.values():
|
||||
yield role
|
||||
|
||||
def __contains__(self, item) -> bool:
|
||||
def __contains__(self, item) -> bool:
|
||||
return int(item) in self._roles
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self):
|
||||
return len(self._roles.keys())
|
||||
|
||||
|
||||
def has_roles(self, role_ids: set) -> bool:
|
||||
"""returns true if this objects contains all roles defined by given role_ids
|
||||
incl. managed roles
|
||||
"""
|
||||
role_ids = {int(id) for id in role_ids}
|
||||
all_role_ids = self._roles.keys()
|
||||
all_role_ids = self._roles.keys()
|
||||
return role_ids.issubset(all_role_ids)
|
||||
|
||||
|
||||
def ids(self) -> set:
|
||||
"""return a set of all role IDs"""
|
||||
return set(self._roles.keys())
|
||||
|
||||
def subset(self, role_ids: set = None, managed_only: bool = False) -> object:
|
||||
"""returns a new object containing the subset of roles as defined
|
||||
"""returns a new object containing the subset of roles as defined
|
||||
by given role IDs and/or including managed roles only
|
||||
"""
|
||||
if role_ids is not None:
|
||||
@ -68,23 +68,23 @@ class DiscordRoles:
|
||||
return type(self)([
|
||||
role for _, role in self._roles.items() if role['managed']
|
||||
])
|
||||
|
||||
|
||||
elif role_ids is not None and managed_only:
|
||||
return type(self)([
|
||||
role for role_id, role in self._roles.items()
|
||||
role for role_id, role in self._roles.items()
|
||||
if role_id in role_ids and role['managed']
|
||||
])
|
||||
|
||||
|
||||
else:
|
||||
return copy(self)
|
||||
|
||||
def union(self, other: object) -> object:
|
||||
"""returns a new roles object that is the union of this roles object
|
||||
"""returns a new roles object that is the union of this roles object
|
||||
with other"""
|
||||
return type(self)(list(self) + list(other))
|
||||
|
||||
def difference(self, other: object) -> object:
|
||||
"""returns a new roles object that only contains the roles
|
||||
"""returns a new roles object that only contains the roles
|
||||
that exist in the current objects, but not in other
|
||||
"""
|
||||
new_ids = self.ids().difference(other.ids())
|
||||
@ -97,11 +97,11 @@ class DiscordRoles:
|
||||
return self._roles_by_name[role_name]
|
||||
else:
|
||||
return dict()
|
||||
|
||||
|
||||
@classmethod
|
||||
def create_from_matched_roles(cls, matched_roles: list) -> None:
|
||||
"""returns a new object created from the given list of matches roles
|
||||
|
||||
|
||||
matches_roles must be a list of tuples in the form: (role, created)
|
||||
"""
|
||||
raw_roles = [x[0] for x in matched_roles]
|
||||
@ -111,10 +111,10 @@ class DiscordRoles:
|
||||
def _assert_valid_role(role: dict):
|
||||
if not isinstance(role, dict):
|
||||
raise TypeError('Roles must be of type dict: %s' % role)
|
||||
|
||||
|
||||
if 'id' not in role or 'name' not in role or 'managed' not in role:
|
||||
raise ValueError('This role is not valid: %s' % role)
|
||||
|
||||
|
||||
@classmethod
|
||||
def sanitize_role_name(cls, role_name: str) -> str:
|
||||
"""shortens too long strings if necessary"""
|
||||
|
@ -27,8 +27,8 @@ ALL_ROLES = [ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE]
|
||||
|
||||
|
||||
def create_user_info(
|
||||
id: int = TEST_USER_ID,
|
||||
username: str = TEST_USER_NAME,
|
||||
id: int = TEST_USER_ID,
|
||||
username: str = TEST_USER_NAME,
|
||||
discriminator: str = TEST_USER_DISCRIMINATOR
|
||||
):
|
||||
return {
|
||||
|
@ -5,11 +5,11 @@ The results can be analysed in a special log file.
|
||||
|
||||
This script is design to be run manually as unit test, e.g. by running the following:
|
||||
|
||||
python manage.py test
|
||||
python manage.py test
|
||||
allianceauth.services.modules.discord.discord_client.tests.piloting_concurrency
|
||||
|
||||
To make it work please set the below mentioned environment variables for your server.
|
||||
Since this may cause lots of 429s we'd recommend NOT to use your
|
||||
Since this may cause lots of 429s we'd recommend NOT to use your
|
||||
alliance Discord server for this.
|
||||
"""
|
||||
|
||||
@ -49,7 +49,7 @@ def worker(num: int):
|
||||
client = DiscordClient(DISCORD_BOT_TOKEN)
|
||||
try:
|
||||
runs = 0
|
||||
while runs < NUMBER_OF_RUNS:
|
||||
while runs < NUMBER_OF_RUNS:
|
||||
run_info = '%s: run %d' % (worker_info, runs + 1)
|
||||
my_jitter_secs = random() * MAX_JITTER_PER_RUN_SECS
|
||||
logger.info('%s - waiting %s secs', run_info, f'{my_jitter_secs:.3f}')
|
||||
@ -67,18 +67,18 @@ def worker(num: int):
|
||||
logger.info(message)
|
||||
print()
|
||||
print(message)
|
||||
sleep(bo.retry_after / 1000)
|
||||
|
||||
sleep(bo.retry_after / 1000)
|
||||
|
||||
except Exception as ex:
|
||||
logger.exception('%s: Processing aborted: %s', worker_info, ex)
|
||||
|
||||
|
||||
logger.info('%s: finished', worker_info)
|
||||
return
|
||||
|
||||
|
||||
class TestMulti(TestCase):
|
||||
|
||||
def test_multi(self):
|
||||
|
||||
def test_multi(self):
|
||||
logger.info('Starting multi test')
|
||||
for num in range(NUMBER_OF_WORKERS):
|
||||
x = threading.Thread(target=worker, args=(num + 1,))
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""This script is for functional testing of the Discord client with a Discord server
|
||||
|
||||
It will run single requests of the various functions to validate
|
||||
that they actually work - excluding those that require Oauth, or does not work
|
||||
It will run single requests of the various functions to validate
|
||||
that they actually work - excluding those that require Oauth, or does not work
|
||||
with a bot token. The results can be also seen in a special log file.
|
||||
|
||||
This script is design to be run manually as unit test, e.g. by running the following:
|
||||
@ -10,7 +10,7 @@ python manage.py test
|
||||
allianceauth.services.modules.discord.discord_self.client.tests.piloting_functionality
|
||||
|
||||
To make it work please set the below mentioned environment variables for your server.
|
||||
Since this may cause lots of 429s we'd recommend NOT to use your
|
||||
Since this may cause lots of 429s we'd recommend NOT to use your
|
||||
alliance Discord server for this.
|
||||
"""
|
||||
|
||||
@ -46,7 +46,7 @@ class TestDiscordApiLive(TestCase):
|
||||
"""runs features that have not been run in any of the other tests"""
|
||||
self.client.guild_infos(DISCORD_GUILD_ID)
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
|
||||
|
||||
self.client.guild_name(DISCORD_GUILD_ID)
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
|
||||
@ -57,9 +57,9 @@ class TestDiscordApiLive(TestCase):
|
||||
DISCORD_GUILD_ID, ['Testrole A', 'Testrole B']
|
||||
)
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
|
||||
def test_create_and_remove_roles(self):
|
||||
# get base
|
||||
|
||||
def test_create_and_remove_roles(self):
|
||||
# get base
|
||||
logger.info('guild_roles')
|
||||
expected = {role['id'] for role in self.client.guild_roles(DISCORD_GUILD_ID)}
|
||||
|
||||
@ -70,8 +70,8 @@ class TestDiscordApiLive(TestCase):
|
||||
guild_id=DISCORD_GUILD_ID, role_name=role_name
|
||||
)
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
self.assertEqual(new_role['name'], role_name)
|
||||
|
||||
self.assertEqual(new_role['name'], role_name)
|
||||
|
||||
# remove role again
|
||||
logger.info('delete_guild_role')
|
||||
self.client.delete_guild_role(
|
||||
@ -102,7 +102,7 @@ class TestDiscordApiLive(TestCase):
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
self.assertEqual(user['nick'], new_nick)
|
||||
|
||||
def test_member_add_remove_roles(self):
|
||||
def test_member_add_remove_roles(self):
|
||||
# create new guild role
|
||||
logger.info('create_guild_role')
|
||||
new_role = self.client.create_guild_role(
|
||||
@ -110,7 +110,7 @@ class TestDiscordApiLive(TestCase):
|
||||
)
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
new_role_id = new_role['id']
|
||||
|
||||
|
||||
# add to member
|
||||
logger.info('add_guild_member_role')
|
||||
self.assertTrue(
|
||||
@ -119,7 +119,7 @@ class TestDiscordApiLive(TestCase):
|
||||
)
|
||||
)
|
||||
sleep(RATE_LIMIT_DELAY_SECS)
|
||||
|
||||
|
||||
# remove again
|
||||
logger.info('remove_guild_member_role')
|
||||
self.assertTrue(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from ..exceptions import (
|
||||
DiscordApiBackoff,
|
||||
DiscordClientException,
|
||||
DiscordApiBackoff,
|
||||
DiscordClientException,
|
||||
DiscordRateLimitExhausted,
|
||||
DiscordTooManyRequestsError
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ class TestDiscordRoles(TestCase):
|
||||
def test_raises_exception_if_roles_raw_of_wrong_type(self):
|
||||
with self.assertRaises(TypeError):
|
||||
DiscordRoles({'id': 1})
|
||||
|
||||
|
||||
def test_raises_exception_if_list_contains_non_dict(self):
|
||||
roles_raw = [ROLE_ALPHA, 'not_valid']
|
||||
with self.assertRaises(TypeError):
|
||||
@ -45,7 +45,7 @@ class TestDiscordRoles(TestCase):
|
||||
roles_raw = [{'id': 1, 'name': 'alpha'}]
|
||||
with self.assertRaises(ValueError):
|
||||
DiscordRoles(roles_raw)
|
||||
|
||||
|
||||
def test_roles_are_equal(self):
|
||||
roles_a = DiscordRoles([ROLE_ALPHA, ROLE_BRAVO])
|
||||
roles_b = DiscordRoles([ROLE_ALPHA, ROLE_BRAVO])
|
||||
@ -57,19 +57,19 @@ class TestDiscordRoles(TestCase):
|
||||
self.assertNotEqual(roles_a, roles_b)
|
||||
|
||||
def test_different_objects_are_not_equal(self):
|
||||
roles_a = DiscordRoles([ROLE_ALPHA, ROLE_BRAVO])
|
||||
roles_a = DiscordRoles([ROLE_ALPHA, ROLE_BRAVO])
|
||||
self.assertFalse(roles_a == "invalid")
|
||||
|
||||
def test_len(self):
|
||||
def test_len(self):
|
||||
self.assertEqual(len(self.all_roles), 4)
|
||||
|
||||
def test_contains(self):
|
||||
self.assertTrue(1 in self.all_roles)
|
||||
self.assertFalse(99 in self.all_roles)
|
||||
|
||||
def test_sanitize_role_name(self):
|
||||
def test_sanitize_role_name(self):
|
||||
role_name_input = 'x' * 110
|
||||
role_name_expected = 'x' * 100
|
||||
role_name_expected = 'x' * 100
|
||||
result = DiscordRoles.sanitize_role_name(role_name_input)
|
||||
self.assertEqual(result, role_name_expected)
|
||||
|
||||
@ -94,8 +94,8 @@ class TestIds(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.all_roles = DiscordRoles(ALL_ROLES)
|
||||
|
||||
def test_return_role_ids_default(self):
|
||||
|
||||
def test_return_role_ids_default(self):
|
||||
result = self.all_roles.ids()
|
||||
expected = {1, 2, 3, 13}
|
||||
self.assertSetEqual(result, expected)
|
||||
@ -154,7 +154,7 @@ class TestHasRoles(TestCase):
|
||||
|
||||
def test_true_if_all_roles_exit_str(self):
|
||||
self.assertTrue(self.all_roles.has_roles(['1', '2']))
|
||||
|
||||
|
||||
def test_false_if_role_does_not_exit(self):
|
||||
self.assertFalse(self.all_roles.has_roles([99]))
|
||||
|
||||
@ -163,7 +163,7 @@ class TestHasRoles(TestCase):
|
||||
|
||||
def test_true_for_empty_roles(self):
|
||||
self.assertTrue(self.all_roles.has_roles([]))
|
||||
|
||||
|
||||
|
||||
class TestGetMatchingRolesByName(TestCase):
|
||||
|
||||
|
@ -16,7 +16,7 @@ from .app_settings import (
|
||||
DISCORD_APP_SECRET,
|
||||
DISCORD_BOT_TOKEN,
|
||||
DISCORD_CALLBACK_URL,
|
||||
DISCORD_GUILD_ID,
|
||||
DISCORD_GUILD_ID,
|
||||
DISCORD_SYNC_NAMES
|
||||
)
|
||||
from .discord_client import DiscordClient
|
||||
@ -41,9 +41,9 @@ class DiscordUserManager(models.Manager):
|
||||
]
|
||||
|
||||
def add_user(
|
||||
self,
|
||||
user: User,
|
||||
authorization_code: str,
|
||||
self,
|
||||
user: User,
|
||||
authorization_code: str,
|
||||
is_rate_limited: bool = True
|
||||
) -> bool:
|
||||
"""adds a new Discord user
|
||||
@ -52,10 +52,10 @@ class DiscordUserManager(models.Manager):
|
||||
- user: Auth user to join
|
||||
- authorization_code: authorization code returns from oauth
|
||||
- is_rate_limited: When False will disable default rate limiting (use with care)
|
||||
|
||||
|
||||
Returns: True on success, else False or raises exception
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
nickname = self.user_formatted_nick(user) if DISCORD_SYNC_NAMES else None
|
||||
group_names = self.user_group_names(user)
|
||||
access_token = self._exchange_auth_code_for_token(authorization_code)
|
||||
@ -63,18 +63,18 @@ class DiscordUserManager(models.Manager):
|
||||
discord_user = user_client.current_user()
|
||||
user_id = discord_user['id']
|
||||
bot_client = self._bot_client(is_rate_limited=is_rate_limited)
|
||||
|
||||
if group_names:
|
||||
|
||||
if group_names:
|
||||
role_ids = match_or_create_roles_from_names(
|
||||
client=bot_client,
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
client=bot_client,
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
role_names=group_names
|
||||
).ids()
|
||||
else:
|
||||
role_ids = None
|
||||
|
||||
|
||||
created = bot_client.add_guild_member(
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
user_id=user_id,
|
||||
access_token=access_token,
|
||||
role_ids=role_ids,
|
||||
@ -88,10 +88,10 @@ class DiscordUserManager(models.Manager):
|
||||
user_id,
|
||||
)
|
||||
self.update_or_create(
|
||||
user=user,
|
||||
user=user,
|
||||
defaults={
|
||||
'uid': user_id,
|
||||
'username': discord_user['username'][:32],
|
||||
'uid': user_id,
|
||||
'username': discord_user['username'][:32],
|
||||
'discriminator': discord_user['discriminator'][:4],
|
||||
'activated': now()
|
||||
}
|
||||
@ -121,7 +121,7 @@ class DiscordUserManager(models.Manager):
|
||||
or None if user has no main
|
||||
"""
|
||||
from .auth_hooks import DiscordService
|
||||
|
||||
|
||||
if user.profile.main_character:
|
||||
return NameFormatter(DiscordService(), user).format_name()
|
||||
else:
|
||||
@ -139,10 +139,10 @@ class DiscordUserManager(models.Manager):
|
||||
"Group names for roles updates of user %s are: %s", user, group_names
|
||||
)
|
||||
return group_names
|
||||
|
||||
|
||||
def user_has_account(self, user: User) -> bool:
|
||||
"""Returns True if the user has an Discord account, else False
|
||||
|
||||
|
||||
only checks locally, does not hit the API
|
||||
"""
|
||||
if not isinstance(user, User):
|
||||
@ -157,7 +157,7 @@ class DiscordUserManager(models.Manager):
|
||||
'permissions': str(cls.BOT_PERMISSIONS)
|
||||
|
||||
})
|
||||
return f'{DiscordClient.OAUTH_BASE_URL}?{params}'
|
||||
return f'{DiscordClient.OAUTH_BASE_URL}?{params}'
|
||||
|
||||
@classmethod
|
||||
def generate_oauth_redirect_url(cls) -> str:
|
||||
@ -171,16 +171,16 @@ class DiscordUserManager(models.Manager):
|
||||
def _exchange_auth_code_for_token(authorization_code: str) -> str:
|
||||
oauth = OAuth2Session(DISCORD_APP_ID, redirect_uri=DISCORD_CALLBACK_URL)
|
||||
token = oauth.fetch_token(
|
||||
DiscordClient.OAUTH_TOKEN_URL,
|
||||
client_secret=DISCORD_APP_SECRET,
|
||||
DiscordClient.OAUTH_TOKEN_URL,
|
||||
client_secret=DISCORD_APP_SECRET,
|
||||
code=authorization_code
|
||||
)
|
||||
logger.debug("Received token from OAuth")
|
||||
return token['access_token']
|
||||
|
||||
|
||||
@classmethod
|
||||
def server_name(cls, use_cache: bool = True) -> str:
|
||||
"""returns the name of the current Discord server
|
||||
"""returns the name of the current Discord server
|
||||
or an empty string if the name could not be retrieved
|
||||
|
||||
Params:
|
||||
@ -194,7 +194,7 @@ class DiscordUserManager(models.Manager):
|
||||
server_name = ""
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Unexpected error when trying to retrieve the server name from Discord",
|
||||
"Unexpected error when trying to retrieve the server name from Discord",
|
||||
exc_info=True
|
||||
)
|
||||
server_name = ""
|
||||
|
@ -22,11 +22,11 @@ logger = LoggerAddTag(logging.getLogger(__name__), __title__)
|
||||
class DiscordUser(models.Model):
|
||||
|
||||
USER_RELATED_NAME = 'discord'
|
||||
|
||||
|
||||
user = models.OneToOneField(
|
||||
User,
|
||||
primary_key=True,
|
||||
on_delete=models.CASCADE,
|
||||
User,
|
||||
primary_key=True,
|
||||
on_delete=models.CASCADE,
|
||||
related_name=USER_RELATED_NAME,
|
||||
help_text='Auth user owning this Discord account'
|
||||
)
|
||||
@ -35,21 +35,21 @@ class DiscordUser(models.Model):
|
||||
help_text='user\'s ID on Discord'
|
||||
)
|
||||
username = models.CharField(
|
||||
max_length=32,
|
||||
default='',
|
||||
max_length=32,
|
||||
default='',
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text='user\'s username on Discord'
|
||||
)
|
||||
discriminator = models.CharField(
|
||||
max_length=4,
|
||||
default='',
|
||||
blank=True,
|
||||
max_length=4,
|
||||
default='',
|
||||
blank=True,
|
||||
help_text='user\'s discriminator on Discord'
|
||||
)
|
||||
activated = models.DateTimeField(
|
||||
default=None,
|
||||
null=True,
|
||||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text='Date & time this service account was activated'
|
||||
)
|
||||
@ -77,11 +77,11 @@ class DiscordUser(models.Model):
|
||||
- True on success
|
||||
- None if user is no longer a member of the Discord server
|
||||
- False on error or raises exception
|
||||
"""
|
||||
"""
|
||||
if not nickname:
|
||||
nickname = DiscordUser.objects.user_formatted_nick(self.user)
|
||||
if nickname:
|
||||
client = DiscordUser.objects._bot_client()
|
||||
if nickname:
|
||||
client = DiscordUser.objects._bot_client()
|
||||
success = client.modify_guild_member(
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
user_id=self.uid,
|
||||
@ -92,14 +92,14 @@ class DiscordUser(models.Model):
|
||||
else:
|
||||
logger.warning('Failed to update nickname for %s', self.user)
|
||||
return success
|
||||
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def update_groups(self, state_name: str = None) -> bool:
|
||||
"""update groups for a user based on his current group memberships.
|
||||
"""update groups for a user based on his current group memberships.
|
||||
Will add or remove roles of a user as needed.
|
||||
|
||||
|
||||
Params:
|
||||
- state_name: optional state name to be used
|
||||
|
||||
@ -107,13 +107,13 @@ class DiscordUser(models.Model):
|
||||
- True on success
|
||||
- None if user is no longer a member of the Discord server
|
||||
- False on error or raises exception
|
||||
"""
|
||||
client = DiscordUser.objects._bot_client()
|
||||
"""
|
||||
client = DiscordUser.objects._bot_client()
|
||||
member_info = client.guild_member(guild_id=DISCORD_GUILD_ID, user_id=self.uid)
|
||||
if member_info is None:
|
||||
if member_info is None:
|
||||
# User is no longer a member
|
||||
return None
|
||||
|
||||
|
||||
guild_roles = DiscordRoles(client.guild_roles(guild_id=DISCORD_GUILD_ID))
|
||||
logger.debug('Current guild roles: %s', guild_roles.ids())
|
||||
if 'roles' in member_info:
|
||||
@ -124,17 +124,17 @@ class DiscordUser(models.Model):
|
||||
if not guild_roles.has_roles(member_info['roles']):
|
||||
raise RuntimeError(
|
||||
'Member %s has unknown roles: %s' % (
|
||||
self.user,
|
||||
self.user,
|
||||
set(member_info['roles']).difference(guild_roles.ids())
|
||||
)
|
||||
)
|
||||
member_roles = guild_roles.subset(member_info['roles'])
|
||||
else:
|
||||
raise RuntimeError('member_info from %s is not valid' % self.user)
|
||||
|
||||
|
||||
requested_roles = match_or_create_roles_from_names(
|
||||
client=client,
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
client=client,
|
||||
guild_id=DISCORD_GUILD_ID,
|
||||
role_names=DiscordUser.objects.user_group_names(
|
||||
user=self.user, state_name=state_name
|
||||
)
|
||||
@ -142,7 +142,7 @@ class DiscordUser(models.Model):
|
||||
logger.debug(
|
||||
'Requested roles for user %s: %s', self.user, requested_roles.ids()
|
||||
)
|
||||
logger.debug('Current roles user %s: %s', self.user, member_roles.ids())
|
||||
logger.debug('Current roles user %s: %s', self.user, member_roles.ids())
|
||||
member_roles_managed = member_roles.subset(managed_only=True)
|
||||
if requested_roles != member_roles.difference(member_roles_managed):
|
||||
logger.debug('Need to update roles for user %s', self.user)
|
||||
@ -163,25 +163,25 @@ class DiscordUser(models.Model):
|
||||
return True
|
||||
|
||||
def update_username(self) -> bool:
|
||||
"""Updates the username incl. the discriminator
|
||||
"""Updates the username incl. the discriminator
|
||||
from the Discord server and saves it
|
||||
|
||||
|
||||
Returns:
|
||||
- True on success
|
||||
- None if user is no longer a member of the Discord server
|
||||
- False on error or raises exception
|
||||
"""
|
||||
|
||||
client = DiscordUser.objects._bot_client()
|
||||
"""
|
||||
|
||||
client = DiscordUser.objects._bot_client()
|
||||
user_info = client.guild_member(guild_id=DISCORD_GUILD_ID, user_id=self.uid)
|
||||
if user_info is None:
|
||||
success = None
|
||||
elif (
|
||||
user_info
|
||||
and 'user' in user_info
|
||||
and 'username' in user_info['user']
|
||||
user_info
|
||||
and 'user' in user_info
|
||||
and 'username' in user_info['user']
|
||||
and 'discriminator' in user_info['user']
|
||||
):
|
||||
):
|
||||
self.username = user_info['user']['username']
|
||||
self.discriminator = user_info['user']['discriminator']
|
||||
self.save()
|
||||
@ -191,22 +191,22 @@ class DiscordUser(models.Model):
|
||||
logger.warning('Failed to update username for %s', self.user)
|
||||
success = False
|
||||
return success
|
||||
|
||||
|
||||
def delete_user(
|
||||
self,
|
||||
notify_user: bool = False,
|
||||
self,
|
||||
notify_user: bool = False,
|
||||
is_rate_limited: bool = True,
|
||||
handle_api_exceptions: bool = False
|
||||
) -> bool:
|
||||
"""Deletes the Discount user both on the server and locally
|
||||
|
||||
Params:
|
||||
- notify_user: When True will sent a notification to the user
|
||||
- notify_user: When True will sent a notification to the user
|
||||
informing him about the deleting of his account
|
||||
- is_rate_limited: When False will disable default rate limiting (use with care)
|
||||
- handle_api_exceptions: When True method will return False
|
||||
- handle_api_exceptions: When True method will return False
|
||||
when an API exception occurs
|
||||
|
||||
|
||||
Returns True when successful, otherwise False or raises exceptions
|
||||
Return None if user does no longer exist
|
||||
"""
|
||||
@ -221,8 +221,8 @@ class DiscordUser(models.Model):
|
||||
if deleted_count > 0:
|
||||
if notify_user:
|
||||
notify(
|
||||
user=_user,
|
||||
title=gettext_lazy('Discord Account Disabled'),
|
||||
user=_user,
|
||||
title=gettext_lazy('Discord Account Disabled'),
|
||||
message=gettext_lazy(
|
||||
'Your Discord account was disabled automatically '
|
||||
'by Auth. If you think this was a mistake, '
|
||||
@ -235,18 +235,18 @@ class DiscordUser(models.Model):
|
||||
else:
|
||||
logger.debug('Account for user %s was already deleted.', _user)
|
||||
return None
|
||||
|
||||
|
||||
else:
|
||||
logger.warning(
|
||||
'Failed to remove user %s from the Discord server', _user
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
except (HTTPError, ConnectionError, DiscordApiBackoff) as ex:
|
||||
if handle_api_exceptions:
|
||||
logger.exception(
|
||||
'Failed to remove user %s from Discord server: %s',self.user, ex
|
||||
)
|
||||
return False
|
||||
return False
|
||||
else:
|
||||
raise ex
|
||||
|
@ -29,11 +29,11 @@ BULK_TASK_PRIORITY = 6
|
||||
)
|
||||
def update_groups(self, user_pk: int, state_name: str = None) -> None:
|
||||
"""Update roles on Discord for given user according to his current groups
|
||||
|
||||
|
||||
Params:
|
||||
- user_pk: PK of given user
|
||||
- state_name: optional state name to be used
|
||||
"""
|
||||
"""
|
||||
_task_perform_user_action(self, user_pk, 'update_groups', state_name=state_name)
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ def update_groups(self, user_pk: int, state_name: str = None) -> None:
|
||||
)
|
||||
def update_nickname(self, user_pk: int, nickname: str = None) -> None:
|
||||
"""Set nickname on Discord for given user to his main character name
|
||||
|
||||
|
||||
Params:
|
||||
- user_pk: PK of given user
|
||||
- nickname: optional nickname to be used instead of user's main
|
||||
@ -55,7 +55,7 @@ def update_nickname(self, user_pk: int, nickname: str = None) -> None:
|
||||
)
|
||||
def update_username(self, user_pk: int) -> None:
|
||||
"""Update locally stored Discord username from Discord server for given user
|
||||
|
||||
|
||||
Params:
|
||||
- user_pk: PK of given user
|
||||
"""
|
||||
@ -67,7 +67,7 @@ def update_username(self, user_pk: int) -> None:
|
||||
)
|
||||
def delete_user(self, user_pk: int, notify_user: bool = False) -> None:
|
||||
"""Delete Discord user
|
||||
|
||||
|
||||
Params:
|
||||
- user_pk: PK of given user
|
||||
"""
|
||||
@ -77,13 +77,13 @@ def delete_user(self, user_pk: int, notify_user: bool = False) -> None:
|
||||
def _task_perform_user_action(self, user_pk: int, method: str, **kwargs) -> None:
|
||||
"""perform a user related action incl. managing all exceptions"""
|
||||
logger.debug("Starting %s for user with pk %s", method, user_pk)
|
||||
user = User.objects.get(pk=user_pk)
|
||||
user = User.objects.get(pk=user_pk)
|
||||
# logger.debug("user %s has state %s", user, user.profile.state)
|
||||
if DiscordUser.objects.user_has_account(user):
|
||||
logger.info("Running %s for user %s", method, user)
|
||||
try:
|
||||
success = getattr(user.discord, method)(**kwargs)
|
||||
|
||||
|
||||
except DiscordApiBackoff as bo:
|
||||
logger.info(
|
||||
"API back off for %s wth user %s due to %r, retrying in %s seconds",
|
||||
@ -92,26 +92,26 @@ def _task_perform_user_action(self, user_pk: int, method: str, **kwargs) -> None
|
||||
bo,
|
||||
bo.retry_after_seconds
|
||||
)
|
||||
raise self.retry(countdown=bo.retry_after_seconds)
|
||||
|
||||
raise self.retry(countdown=bo.retry_after_seconds)
|
||||
|
||||
except AttributeError:
|
||||
raise ValueError(f'{method} not a valid method for DiscordUser')
|
||||
|
||||
except (HTTPError, ConnectionError):
|
||||
except (HTTPError, ConnectionError):
|
||||
logger.warning(
|
||||
'%s failed for user %s, retrying in %d secs',
|
||||
'%s failed for user %s, retrying in %d secs',
|
||||
method,
|
||||
user,
|
||||
user,
|
||||
DISCORD_TASKS_RETRY_PAUSE,
|
||||
exc_info=True
|
||||
)
|
||||
if self.request.retries < DISCORD_TASKS_MAX_RETRIES:
|
||||
raise self.retry(countdown=DISCORD_TASKS_RETRY_PAUSE)
|
||||
else:
|
||||
else:
|
||||
logger.error(
|
||||
'%s failed for user %s after max retries',
|
||||
method,
|
||||
user,
|
||||
user,
|
||||
exc_info=True
|
||||
)
|
||||
except Exception:
|
||||
@ -120,8 +120,8 @@ def _task_perform_user_action(self, user_pk: int, method: str, **kwargs) -> None
|
||||
method,
|
||||
user,
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
else:
|
||||
if success is None and method != 'delete_user':
|
||||
delete_user.delay(user.pk, notify_user=True)
|
||||
@ -134,14 +134,14 @@ def _task_perform_user_action(self, user_pk: int, method: str, **kwargs) -> None
|
||||
|
||||
@shared_task(name='discord.update_all_groups')
|
||||
def update_all_groups() -> None:
|
||||
"""Update roles for all known users with a Discord account."""
|
||||
"""Update roles for all known users with a Discord account."""
|
||||
discord_users_qs = DiscordUser.objects.all()
|
||||
_bulk_update_groups_for_users(discord_users_qs)
|
||||
|
||||
|
||||
@shared_task(name='discord.update_groups_bulk')
|
||||
def update_groups_bulk(user_pks: list) -> None:
|
||||
"""Update roles for list of users with a Discord account in bulk."""
|
||||
"""Update roles for list of users with a Discord account in bulk."""
|
||||
discord_users_qs = DiscordUser.objects\
|
||||
.filter(user__pk__in=user_pks)\
|
||||
.select_related()
|
||||
@ -155,7 +155,7 @@ def _bulk_update_groups_for_users(discord_users_qs: QuerySet) -> None:
|
||||
update_groups_chain = list()
|
||||
for discord_user in discord_users_qs:
|
||||
update_groups_chain.append(update_groups.si(discord_user.user.pk))
|
||||
|
||||
|
||||
chain(update_groups_chain).apply_async(priority=BULK_TASK_PRIORITY)
|
||||
|
||||
|
||||
@ -164,11 +164,11 @@ def update_all_nicknames() -> None:
|
||||
"""Update nicknames for all known users with a Discord account."""
|
||||
discord_users_qs = DiscordUser.objects.all()
|
||||
_bulk_update_nicknames_for_users(discord_users_qs)
|
||||
|
||||
|
||||
|
||||
@shared_task(name='discord.update_nicknames_bulk')
|
||||
def update_nicknames_bulk(user_pks: list) -> None:
|
||||
"""Update nicknames for list of users with a Discord account in bulk."""
|
||||
"""Update nicknames for list of users with a Discord account in bulk."""
|
||||
discord_users_qs = DiscordUser.objects\
|
||||
.filter(user__pk__in=user_pks)\
|
||||
.select_related()
|
||||
@ -177,27 +177,27 @@ def update_nicknames_bulk(user_pks: list) -> None:
|
||||
|
||||
def _bulk_update_nicknames_for_users(discord_users_qs: QuerySet) -> None:
|
||||
logger.info(
|
||||
"Starting to bulk update discord nicknames for %d users",
|
||||
"Starting to bulk update discord nicknames for %d users",
|
||||
discord_users_qs.count()
|
||||
)
|
||||
update_nicknames_chain = list()
|
||||
for discord_user in discord_users_qs:
|
||||
update_nicknames_chain.append(update_nickname.si(discord_user.user.pk))
|
||||
|
||||
|
||||
chain(update_nicknames_chain).apply_async(priority=BULK_TASK_PRIORITY)
|
||||
|
||||
|
||||
def _task_perform_users_action(self, method: str, **kwargs) -> Any:
|
||||
"""Perform an action that concerns a group of users or the whole server
|
||||
def _task_perform_users_action(self, method: str, **kwargs) -> Any:
|
||||
"""Perform an action that concerns a group of users or the whole server
|
||||
and that hits the API
|
||||
"""
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = getattr(DiscordUser.objects, method)(**kwargs)
|
||||
|
||||
|
||||
except AttributeError:
|
||||
raise ValueError(f'{method} not a valid method for DiscordUser.objects')
|
||||
|
||||
|
||||
except DiscordApiBackoff as bo:
|
||||
logger.info(
|
||||
"API back off for %s due to %r, retrying in %s seconds",
|
||||
@ -205,23 +205,23 @@ def _task_perform_users_action(self, method: str, **kwargs) -> Any:
|
||||
bo,
|
||||
bo.retry_after_seconds
|
||||
)
|
||||
raise self.retry(countdown=bo.retry_after_seconds)
|
||||
|
||||
except (HTTPError, ConnectionError):
|
||||
raise self.retry(countdown=bo.retry_after_seconds)
|
||||
|
||||
except (HTTPError, ConnectionError):
|
||||
logger.warning(
|
||||
'%s failed, retrying in %d secs',
|
||||
'%s failed, retrying in %d secs',
|
||||
method,
|
||||
DISCORD_TASKS_RETRY_PAUSE,
|
||||
exc_info=True
|
||||
)
|
||||
if self.request.retries < DISCORD_TASKS_MAX_RETRIES:
|
||||
raise self.retry(countdown=DISCORD_TASKS_RETRY_PAUSE)
|
||||
else:
|
||||
else:
|
||||
logger.error('%s failed after max retries', method, exc_info=True)
|
||||
|
||||
|
||||
except Exception:
|
||||
logger.error('%s failed due to unexpected exception', method, exc_info=True)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@ -235,17 +235,17 @@ def update_servername(self) -> None:
|
||||
|
||||
@shared_task(name='discord.update_all_usernames')
|
||||
def update_all_usernames() -> None:
|
||||
"""Update all usernames for all known users with a Discord account.
|
||||
"""Update all usernames for all known users with a Discord account.
|
||||
Also updates the server name
|
||||
"""
|
||||
update_servername.delay()
|
||||
discord_users_qs = DiscordUser.objects.all()
|
||||
_bulk_update_usernames_for_users(discord_users_qs)
|
||||
|
||||
|
||||
|
||||
@shared_task(name='discord.update_usernames_bulk')
|
||||
def update_usernames_bulk(user_pks: list) -> None:
|
||||
"""Update usernames for list of users with a Discord account in bulk."""
|
||||
"""Update usernames for list of users with a Discord account in bulk."""
|
||||
discord_users_qs = DiscordUser.objects\
|
||||
.filter(user__pk__in=user_pks)\
|
||||
.select_related()
|
||||
@ -254,13 +254,13 @@ def update_usernames_bulk(user_pks: list) -> None:
|
||||
|
||||
def _bulk_update_usernames_for_users(discord_users_qs: QuerySet) -> None:
|
||||
logger.info(
|
||||
"Starting to bulk update discord usernames for %d users",
|
||||
"Starting to bulk update discord usernames for %d users",
|
||||
discord_users_qs.count()
|
||||
)
|
||||
update_usernames_chain = list()
|
||||
for discord_user in discord_users_qs:
|
||||
update_usernames_chain.append(update_username.si(discord_user.user.pk))
|
||||
|
||||
|
||||
chain(update_usernames_chain).apply_async(priority=BULK_TASK_PRIORITY)
|
||||
|
||||
|
||||
@ -277,5 +277,5 @@ def update_all() -> None:
|
||||
update_all_chain.append(update_username.si(discord_user.user.pk))
|
||||
if DISCORD_SYNC_NAMES:
|
||||
update_all_chain.append(update_nickname.si(discord_user.user.pk))
|
||||
|
||||
|
||||
chain(update_all_chain).apply_async(priority=BULK_TASK_PRIORITY)
|
||||
|
@ -5,14 +5,14 @@ from ..discord_client.tests import ( # noqa
|
||||
TEST_USER_ID,
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_DISCRIMINATOR,
|
||||
create_role,
|
||||
ROLE_ALPHA,
|
||||
ROLE_BRAVO,
|
||||
ROLE_CHARLIE,
|
||||
ROLE_MIKE,
|
||||
create_role,
|
||||
ROLE_ALPHA,
|
||||
ROLE_BRAVO,
|
||||
ROLE_CHARLIE,
|
||||
ROLE_MIKE,
|
||||
ALL_ROLES,
|
||||
create_user_info
|
||||
)
|
||||
)
|
||||
|
||||
DEFAULT_AUTH_GROUP = 'Member'
|
||||
MODULE_PATH = 'allianceauth.services.modules.discord'
|
||||
|
@ -53,21 +53,21 @@ def run_many_updates(runs):
|
||||
logger.info('Starting piloting_tasks for %d runs', runs)
|
||||
users = list()
|
||||
all_groups = Group.objects.all()
|
||||
for i in range(runs):
|
||||
for i in range(runs):
|
||||
if not users:
|
||||
users = list(User.objects.filter(discord__isnull=False))
|
||||
user = users.pop()
|
||||
logger.info('%d/%d: Starting run with user %s', i + 1, runs, user)
|
||||
# force change of nick
|
||||
# force change of nick
|
||||
new_nick = f'Testnick {uuid1().hex}'[:32]
|
||||
logger.info(
|
||||
'%d/%d: Changing nickname of %s to "%s"', i + 1, runs, user, new_nick
|
||||
)
|
||||
user.profile.main_character.character_name = new_nick
|
||||
user.profile.main_character.save()
|
||||
|
||||
user.profile.main_character.save()
|
||||
|
||||
# force change of groups
|
||||
user_groups = user.groups.all()
|
||||
user_groups = user.groups.all()
|
||||
user.groups.remove(random.choice(user_groups))
|
||||
while True:
|
||||
new_group = random.choice(all_groups)
|
||||
|
@ -9,9 +9,9 @@ from allianceauth.eveonline.models import (
|
||||
)
|
||||
|
||||
from ....admin import (
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
user_main_organization,
|
||||
user_profile_pic,
|
||||
user_username,
|
||||
user_main_organization,
|
||||
ServicesUserAdmin,
|
||||
MainCorporationsFilter,
|
||||
MainAllianceFilter
|
||||
@ -31,7 +31,7 @@ class TestDataMixin(TestCase):
|
||||
EveAllianceInfo.objects.all().delete()
|
||||
User.objects.all().delete()
|
||||
DiscordUser.objects.all().delete()
|
||||
|
||||
|
||||
# user 1 - corp and alliance, normal user
|
||||
cls.character_1 = EveCharacter.objects.create(
|
||||
character_id=1001,
|
||||
@ -56,16 +56,16 @@ class TestDataMixin(TestCase):
|
||||
alliance = EveAllianceInfo.objects.create(
|
||||
alliance_id=3001,
|
||||
alliance_name='Wayne Enterprises',
|
||||
alliance_ticker='WE',
|
||||
alliance_ticker='WE',
|
||||
executor_corp_id=2001
|
||||
)
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2001,
|
||||
corporation_name='Wayne Technologies',
|
||||
corporation_ticker='WT',
|
||||
corporation_ticker='WT',
|
||||
member_count=42,
|
||||
alliance=alliance
|
||||
)
|
||||
)
|
||||
cls.user_1 = User.objects.create_user(
|
||||
cls.character_1.character_name.replace(' ', '_'),
|
||||
'abc@example.com',
|
||||
@ -103,7 +103,7 @@ class TestDataMixin(TestCase):
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2002,
|
||||
corporation_name='Daily Plane',
|
||||
corporation_ticker='DP',
|
||||
corporation_ticker='DP',
|
||||
member_count=99,
|
||||
alliance=None
|
||||
)
|
||||
@ -123,7 +123,7 @@ class TestDataMixin(TestCase):
|
||||
user=cls.user_2,
|
||||
uid=1002
|
||||
)
|
||||
|
||||
|
||||
# user 3 - no main, no group, superuser
|
||||
cls.character_3 = EveCharacter.objects.create(
|
||||
character_id=1101,
|
||||
@ -136,7 +136,7 @@ class TestDataMixin(TestCase):
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id=2101,
|
||||
corporation_name='Lex Corp',
|
||||
corporation_ticker='LC',
|
||||
corporation_ticker='LC',
|
||||
member_count=666,
|
||||
alliance=None
|
||||
)
|
||||
@ -160,13 +160,13 @@ class TestDataMixin(TestCase):
|
||||
user=cls.user_3,
|
||||
uid=1003
|
||||
)
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.factory = RequestFactory()
|
||||
self.modeladmin = DiscordUserAdmin(
|
||||
model=DiscordUser, admin_site=AdminSite()
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
|
||||
class TestColumnRendering(TestDataMixin, TestCase):
|
||||
|
||||
@ -230,12 +230,12 @@ class TestColumnRendering(TestDataMixin, TestCase):
|
||||
|
||||
|
||||
class TestFilters(TestDataMixin, TestCase):
|
||||
|
||||
|
||||
def test_filter_main_corporations(self):
|
||||
|
||||
class DiscordUserAdminTest(ServicesUserAdmin):
|
||||
|
||||
class DiscordUserAdminTest(ServicesUserAdmin):
|
||||
list_filter = (MainCorporationsFilter,)
|
||||
|
||||
|
||||
my_modeladmin = DiscordUserAdminTest(DiscordUser, AdminSite())
|
||||
|
||||
# Make sure the lookups are correct
|
||||
@ -244,7 +244,7 @@ class TestFilters(TestDataMixin, TestCase):
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
filters = changelist.get_filters(request)
|
||||
filterspec = filters[0][0]
|
||||
expected = [
|
||||
expected = [
|
||||
(2002, 'Daily Planet'),
|
||||
(2001, 'Wayne Technologies'),
|
||||
]
|
||||
@ -254,19 +254,19 @@ class TestFilters(TestDataMixin, TestCase):
|
||||
request = self.factory.get(
|
||||
'/', {'main_corporation_id__exact': self.character_1.corporation_id}
|
||||
)
|
||||
request.user = self.user_1
|
||||
request.user = self.user_1
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
queryset = changelist.get_queryset(request)
|
||||
expected = [self.user_1.discord]
|
||||
self.assertSetEqual(set(queryset), set(expected))
|
||||
|
||||
|
||||
def test_filter_main_alliances(self):
|
||||
|
||||
class DiscordUserAdminTest(ServicesUserAdmin):
|
||||
|
||||
class DiscordUserAdminTest(ServicesUserAdmin):
|
||||
list_filter = (MainAllianceFilter,)
|
||||
|
||||
|
||||
my_modeladmin = DiscordUserAdminTest(DiscordUser, AdminSite())
|
||||
|
||||
|
||||
# Make sure the lookups are correct
|
||||
request = self.factory.get('/')
|
||||
request.user = self.user_1
|
||||
@ -282,7 +282,7 @@ class TestFilters(TestDataMixin, TestCase):
|
||||
request = self.factory.get(
|
||||
'/', {'main_alliance_id__exact': self.character_1.alliance_id}
|
||||
)
|
||||
request.user = self.user_1
|
||||
request.user = self.user_1
|
||||
changelist = my_modeladmin.get_changelist_instance(request)
|
||||
queryset = changelist.get_queryset(request)
|
||||
expected = [self.user_1.discord]
|
||||
|
@ -19,11 +19,11 @@ logger = set_logger_to_file(MODULE_PATH + '.auth_hooks', __file__)
|
||||
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||
class TestDiscordService(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self):
|
||||
self.member = AuthUtils.create_member(TEST_USER_NAME)
|
||||
DiscordUser.objects.create(
|
||||
user=self.member,
|
||||
uid=TEST_USER_ID,
|
||||
user=self.member,
|
||||
uid=TEST_USER_ID,
|
||||
username=TEST_USER_NAME,
|
||||
discriminator='1234'
|
||||
)
|
||||
@ -34,33 +34,33 @@ class TestDiscordService(TestCase):
|
||||
Notification.objects.all().delete()
|
||||
|
||||
def test_service_enabled(self):
|
||||
service = self.service()
|
||||
service = self.service()
|
||||
self.assertTrue(service.service_active_for_user(self.member))
|
||||
self.assertFalse(service.service_active_for_user(self.none_member))
|
||||
|
||||
@patch(MODULE_PATH + '.tasks.update_all_groups')
|
||||
def test_update_all_groups(self, mock_update_all_groups):
|
||||
service = self.service()
|
||||
service.update_all_groups()
|
||||
service.update_all_groups()
|
||||
self.assertTrue(mock_update_all_groups.delay.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.tasks.update_groups_bulk')
|
||||
def test_update_groups_bulk(self, mock_update_groups_bulk):
|
||||
service = self.service()
|
||||
service = self.service()
|
||||
service.update_groups_bulk([self.member])
|
||||
self.assertTrue(mock_update_groups_bulk.delay.called)
|
||||
self.assertTrue(mock_update_groups_bulk.delay.called)
|
||||
|
||||
@patch(MODULE_PATH + '.tasks.update_groups')
|
||||
def test_update_groups_for_member(self, mock_update_groups):
|
||||
service = self.service()
|
||||
def test_update_groups_for_member(self, mock_update_groups):
|
||||
service = self.service()
|
||||
service.update_groups(self.member)
|
||||
self.assertTrue(mock_update_groups.apply_async.called)
|
||||
self.assertTrue(mock_update_groups.apply_async.called)
|
||||
|
||||
@patch(MODULE_PATH + '.tasks.update_groups')
|
||||
def test_update_groups_for_none_member(self, mock_update_groups):
|
||||
def test_update_groups_for_none_member(self, mock_update_groups):
|
||||
service = self.service()
|
||||
service.update_groups(self.none_member)
|
||||
self.assertFalse(mock_update_groups.apply_async.called)
|
||||
self.assertFalse(mock_update_groups.apply_async.called)
|
||||
|
||||
@patch(MODULE_PATH + '.models.notify')
|
||||
@patch(MODULE_PATH + '.tasks.DiscordUser')
|
||||
@ -69,52 +69,52 @@ class TestDiscordService(TestCase):
|
||||
self, mock_DiscordClient, mock_DiscordUser, mock_notify
|
||||
):
|
||||
mock_DiscordClient.return_value.remove_guild_member.return_value = True
|
||||
|
||||
|
||||
# Test member is not deleted
|
||||
service = self.service()
|
||||
service.validate_user(self.member)
|
||||
self.assertTrue(DiscordUser.objects.filter(user=self.member).exists())
|
||||
|
||||
# Test none member is deleted
|
||||
# Test none member is deleted
|
||||
DiscordUser.objects.create(user=self.none_member, uid=TEST_USER_ID)
|
||||
service.validate_user(self.none_member)
|
||||
service.validate_user(self.none_member)
|
||||
self.assertFalse(DiscordUser.objects.filter(user=self.none_member).exists())
|
||||
|
||||
@patch(MODULE_PATH + '.tasks.update_nickname')
|
||||
def test_sync_nickname(self, mock_update_nickname):
|
||||
service = self.service()
|
||||
service = self.service()
|
||||
service.sync_nickname(self.member)
|
||||
self.assertTrue(mock_update_nickname.apply_async.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.tasks.update_nicknames_bulk')
|
||||
def test_sync_nicknames_bulk(self, mock_update_nicknames_bulk):
|
||||
service = self.service()
|
||||
service.sync_nicknames_bulk([self.member])
|
||||
service = self.service()
|
||||
service.sync_nicknames_bulk([self.member])
|
||||
self.assertTrue(mock_update_nicknames_bulk.delay.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient', spec=DiscordClient)
|
||||
def test_delete_user_is_member(self, mock_DiscordClient):
|
||||
def test_delete_user_is_member(self, mock_DiscordClient):
|
||||
mock_DiscordClient.return_value.remove_guild_member.return_value = True
|
||||
|
||||
service = self.service()
|
||||
service.delete_user(self.member, notify_user=True)
|
||||
|
||||
|
||||
self.assertTrue(mock_DiscordClient.return_value.remove_guild_member.called)
|
||||
self.assertFalse(DiscordUser.objects.filter(user=self.member).exists())
|
||||
self.assertFalse(DiscordUser.objects.filter(user=self.member).exists())
|
||||
self.assertTrue(Notification.objects.filter(user=self.member).exists())
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient', spec=DiscordClient)
|
||||
def test_delete_user_is_not_member(self, mock_DiscordClient):
|
||||
mock_DiscordClient.return_value.remove_guild_member.return_value = True
|
||||
|
||||
service = self.service()
|
||||
service = self.service()
|
||||
service.delete_user(self.none_member)
|
||||
|
||||
self.assertFalse(mock_DiscordClient.return_value.remove_guild_member.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient', spec=DiscordClient)
|
||||
def test_render_services_ctrl_with_username(self, mock_DiscordClient):
|
||||
service = self.service()
|
||||
service = self.service()
|
||||
request = self.factory.get('/services/')
|
||||
request.user = self.member
|
||||
|
||||
|
@ -26,11 +26,11 @@ from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from . import (
|
||||
TEST_GUILD_ID,
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_ID,
|
||||
TEST_USER_DISCRIMINATOR,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
TEST_USER_DISCRIMINATOR,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
MODULE_PATH,
|
||||
add_permissions_to_members,
|
||||
ROLE_ALPHA,
|
||||
@ -75,11 +75,11 @@ guild_member_request = DiscordRequest(
|
||||
add_guild_member_request = DiscordRequest(
|
||||
method='PUT',
|
||||
url=f'{DISCORD_API_BASE_URL}guilds/{TEST_GUILD_ID}/members/{TEST_USER_ID}'
|
||||
)
|
||||
)
|
||||
modify_guild_member_request = DiscordRequest(
|
||||
method='PATCH',
|
||||
url=f'{DISCORD_API_BASE_URL}guilds/{TEST_GUILD_ID}/members/{TEST_USER_ID}'
|
||||
)
|
||||
)
|
||||
remove_guild_member_request = DiscordRequest(
|
||||
method='DELETE',
|
||||
url=f'{DISCORD_API_BASE_URL}guilds/{TEST_GUILD_ID}/members/{TEST_USER_ID}'
|
||||
@ -93,7 +93,7 @@ def clear_cache():
|
||||
logger.info('Cache flushed')
|
||||
|
||||
|
||||
def reset_testdata():
|
||||
def reset_testdata():
|
||||
AuthUtils.disconnect_signals()
|
||||
Group.objects.all().delete()
|
||||
User.objects.all().delete()
|
||||
@ -112,15 +112,15 @@ class TestServiceFeatures(TransactionTestCase):
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.maxDiff = None
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""All tests: Given a user with member state,
|
||||
"""All tests: Given a user with member state,
|
||||
service permission and active Discord account
|
||||
"""
|
||||
clear_cache()
|
||||
reset_testdata()
|
||||
self.group_charlie = Group.objects.create(name='charlie')
|
||||
|
||||
self.group_charlie = Group.objects.create(name='charlie')
|
||||
|
||||
# States
|
||||
self.member_state = AuthUtils.get_member_state()
|
||||
self.guest_state = AuthUtils.get_guest_state()
|
||||
@ -132,64 +132,64 @@ class TestServiceFeatures(TransactionTestCase):
|
||||
# Test user
|
||||
self.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
self.main = AuthUtils.add_main_character_2(
|
||||
self.user,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
corp_id='2',
|
||||
corp_name='test_corp',
|
||||
self.user,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
corp_id='2',
|
||||
corp_name='test_corp',
|
||||
corp_ticker='TEST',
|
||||
disconnect_signals=True
|
||||
)
|
||||
)
|
||||
self.member_state.member_characters.add(self.main)
|
||||
|
||||
|
||||
# verify user is a member and has an account
|
||||
self.user = User.objects.get(pk=self.user.pk)
|
||||
self.assertEqual(self.user.profile.state, self.member_state)
|
||||
|
||||
|
||||
self.discord_user = DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
self.assertTrue(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
|
||||
def test_when_name_of_main_changes_then_discord_nick_is_updated(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
requests_mocker.patch(modify_guild_member_request.url, status_code=204)
|
||||
|
||||
|
||||
# changing nick to trigger signals
|
||||
new_nick = f'Testnick {uuid1().hex}'[:32]
|
||||
self.user.profile.main_character.character_name = new_nick
|
||||
self.user.profile.main_character.save()
|
||||
|
||||
|
||||
# verify Discord nick was updates
|
||||
nick_updated = False
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
if my_request == modify_guild_member_request and "nick" in r.json():
|
||||
nick_updated = True
|
||||
self.assertEqual(r.json()["nick"], new_nick)
|
||||
|
||||
|
||||
self.assertTrue(nick_updated)
|
||||
self.assertTrue(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
def test_when_name_of_main_changes_and_user_deleted_then_account_is_deleted(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
requests_mocker.patch(
|
||||
modify_guild_member_request.url, status_code=404, json={'code': 10007}
|
||||
)
|
||||
)
|
||||
requests_mocker.delete(remove_guild_member_request.url, status_code=204)
|
||||
|
||||
|
||||
# changing nick to trigger signals
|
||||
new_nick = f'Testnick {uuid1().hex}'[:32]
|
||||
self.user.profile.main_character.character_name = new_nick
|
||||
self.user.profile.main_character.save()
|
||||
|
||||
|
||||
self.assertFalse(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
def test_when_name_of_main_changes_and_and_rate_limited_then_dont_call_api(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
requests_mocker.patch(modify_guild_member_request.url, status_code=204)
|
||||
|
||||
|
||||
# exhausting rate limit
|
||||
client = DiscordUser.objects._bot_client()
|
||||
client._redis.set(
|
||||
@ -197,7 +197,7 @@ class TestServiceFeatures(TransactionTestCase):
|
||||
value=0,
|
||||
px=2000
|
||||
)
|
||||
|
||||
|
||||
# changing nick to trigger signals
|
||||
new_nick = f'Testnick {uuid1().hex}'[:32]
|
||||
self.user.profile.main_character.character_name = new_nick
|
||||
@ -207,55 +207,55 @@ class TestServiceFeatures(TransactionTestCase):
|
||||
requests_made = [
|
||||
DiscordRequest(r.method, r.url) for r in requests_mocker.request_history
|
||||
]
|
||||
|
||||
|
||||
self.assertListEqual(requests_made, list())
|
||||
|
||||
|
||||
def test_when_member_is_demoted_to_guest_then_his_account_is_deleted(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
requests_mocker.patch(modify_guild_member_request.url, status_code=204)
|
||||
requests_mocker.delete(remove_guild_member_request.url, status_code=204)
|
||||
|
||||
# our user is a member and has an account
|
||||
|
||||
# our user is a member and has an account
|
||||
self.assertTrue(self.user.has_perm('discord.access_discord'))
|
||||
|
||||
# now we demote him to guest
|
||||
|
||||
# now we demote him to guest
|
||||
self.member_state.member_characters.remove(self.main)
|
||||
|
||||
|
||||
# verify user is now guest
|
||||
self.user = User.objects.get(pk=self.user.pk)
|
||||
self.user = User.objects.get(pk=self.user.pk)
|
||||
self.assertEqual(self.user.profile.state, AuthUtils.get_guest_state())
|
||||
|
||||
|
||||
# verify user has no longer access to Discord and no account
|
||||
self.assertFalse(self.user.has_perm('discord.access_discord'))
|
||||
self.assertFalse(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
self.assertFalse(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
# verify account was actually deleted from Discord server
|
||||
requests_made = [
|
||||
DiscordRequest(r.method, r.url) for r in requests_mocker.request_history
|
||||
]
|
||||
]
|
||||
self.assertIn(remove_guild_member_request, requests_made)
|
||||
|
||||
|
||||
# verify user has been notified
|
||||
self.assertTrue(Notification.objects.filter(user=self.user).exists())
|
||||
|
||||
|
||||
def test_when_member_changes_to_blue_state_then_roles_are_updated_accordingly(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
# request mocks
|
||||
requests_mocker.get(
|
||||
guild_member_request.url,
|
||||
json={'user': create_user_info(), 'roles': ['3', '13', '99']}
|
||||
)
|
||||
)
|
||||
requests_mocker.get(
|
||||
guild_roles_request.url,
|
||||
json=[
|
||||
ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE, ROLE_MEMBER, ROLE_BLUE
|
||||
]
|
||||
)
|
||||
requests_mocker.post(create_guild_role_request.url, json=ROLE_CHARLIE)
|
||||
)
|
||||
requests_mocker.post(create_guild_role_request.url, json=ROLE_CHARLIE)
|
||||
requests_mocker.patch(modify_guild_member_request.url, status_code=204)
|
||||
|
||||
|
||||
AuthUtils.disconnect_signals()
|
||||
self.user.groups.add(self.group_charlie)
|
||||
AuthUtils.connect_signals()
|
||||
@ -264,101 +264,101 @@ class TestServiceFeatures(TransactionTestCase):
|
||||
self.blue_state.member_characters.add(self.main)
|
||||
self.member_state.member_characters.remove(self.main)
|
||||
|
||||
# verify roles for user where updated
|
||||
# verify roles for user where updated
|
||||
roles_updated = False
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
if my_request == modify_guild_member_request and "roles" in r.json():
|
||||
roles_updated = True
|
||||
self.assertSetEqual(set(r.json()["roles"]), {3, 13, 98})
|
||||
break
|
||||
|
||||
|
||||
self.assertTrue(roles_updated)
|
||||
self.assertTrue(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
def test_when_group_added_to_member_and_role_known_then_his_roles_are_updated(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
requests_mocker.get(
|
||||
guild_member_request.url,
|
||||
json={
|
||||
'user': create_user_info(),
|
||||
'roles': ['13', '99']
|
||||
}
|
||||
)
|
||||
)
|
||||
requests_mocker.get(
|
||||
guild_roles_request.url,
|
||||
guild_roles_request.url,
|
||||
json=[ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE, ROLE_MEMBER]
|
||||
)
|
||||
requests_mocker.post(create_guild_role_request.url, json=ROLE_CHARLIE)
|
||||
)
|
||||
requests_mocker.post(create_guild_role_request.url, json=ROLE_CHARLIE)
|
||||
requests_mocker.patch(modify_guild_member_request.url, status_code=204)
|
||||
|
||||
|
||||
# adding new group to trigger signals
|
||||
self.user.groups.add(self.group_charlie)
|
||||
|
||||
|
||||
# verify roles for user where updated
|
||||
roles_updated = False
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
if my_request == modify_guild_member_request and "roles" in r.json():
|
||||
roles_updated = True
|
||||
self.assertSetEqual(set(r.json()["roles"]), {3, 13, 99})
|
||||
break
|
||||
|
||||
|
||||
self.assertTrue(roles_updated)
|
||||
self.assertTrue(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
def test_when_group_added_to_member_and_role_unknown_then_his_roles_are_updated(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
requests_mocker.get(
|
||||
guild_member_request.url,
|
||||
json={
|
||||
'user': {'id': str(TEST_USER_ID), 'username': TEST_MAIN_NAME},
|
||||
'roles': ['13', '99']
|
||||
}
|
||||
)
|
||||
)
|
||||
requests_mocker.get(
|
||||
guild_roles_request.url,
|
||||
guild_roles_request.url,
|
||||
json=[ROLE_ALPHA, ROLE_BRAVO, ROLE_MIKE, ROLE_MEMBER]
|
||||
)
|
||||
requests_mocker.post(create_guild_role_request.url, json=ROLE_CHARLIE)
|
||||
)
|
||||
requests_mocker.post(create_guild_role_request.url, json=ROLE_CHARLIE)
|
||||
requests_mocker.patch(modify_guild_member_request.url, status_code=204)
|
||||
|
||||
|
||||
# adding new group to trigger signals
|
||||
self.user.groups.add(self.group_charlie)
|
||||
self.user.refresh_from_db()
|
||||
|
||||
|
||||
# verify roles for user where updated
|
||||
roles_updated = False
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
my_request = DiscordRequest(r.method, r.url)
|
||||
if my_request == modify_guild_member_request and "roles" in r.json():
|
||||
roles_updated = True
|
||||
self.assertSetEqual(set(r.json()["roles"]), {3, 13, 99})
|
||||
break
|
||||
|
||||
|
||||
self.assertTrue(roles_updated)
|
||||
self.assertTrue(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
|
||||
|
||||
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||
@patch(MODULE_PATH + '.managers.DISCORD_GUILD_ID', TEST_GUILD_ID)
|
||||
@patch(MODULE_PATH + '.models.DISCORD_GUILD_ID', TEST_GUILD_ID)
|
||||
@requests_mock.Mocker()
|
||||
class StateTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def setUp(self):
|
||||
clear_cache()
|
||||
reset_testdata()
|
||||
|
||||
|
||||
self.user = AuthUtils.create_user('test_user', disconnect_signals=True)
|
||||
AuthUtils.add_main_character(
|
||||
self.user,
|
||||
'Perm Test Character', '99',
|
||||
corp_id='100',
|
||||
self.user,
|
||||
'Perm Test Character', '99',
|
||||
corp_id='100',
|
||||
alliance_id='200',
|
||||
corp_name='Perm Test Corp',
|
||||
corp_name='Perm Test Corp',
|
||||
alliance_name='Perm Test Alliance'
|
||||
)
|
||||
self.test_character = EveCharacter.objects.get(character_id='99')
|
||||
@ -369,7 +369,7 @@ class StateTestCase(TestCase):
|
||||
self.access_discord = AuthUtils.get_permission_by_name('discord.access_discord')
|
||||
self.member_state.permissions.add(self.access_discord)
|
||||
self.member_state.member_characters.add(self.test_character)
|
||||
|
||||
|
||||
def _add_discord_user(self):
|
||||
self.discord_user = DiscordUser.objects.create(
|
||||
user=self.user, uid="12345678910"
|
||||
@ -434,57 +434,57 @@ class StateTestCase(TestCase):
|
||||
@patch(MODULE_PATH + '.models.DISCORD_GUILD_ID', TEST_GUILD_ID)
|
||||
@requests_mock.Mocker()
|
||||
class TestUserFeatures(WebTest):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
clear_cache()
|
||||
reset_testdata()
|
||||
self.member = AuthUtils.create_member(TEST_USER_NAME)
|
||||
AuthUtils.add_main_character_2(
|
||||
self.member,
|
||||
TEST_MAIN_NAME,
|
||||
self.member,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
disconnect_signals=True
|
||||
)
|
||||
add_permissions_to_members()
|
||||
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.managers.OAuth2Session')
|
||||
def test_user_activation_normal(
|
||||
self, requests_mocker, mock_OAuth2Session, mock_messages
|
||||
):
|
||||
):
|
||||
# setup
|
||||
requests_mocker.get(
|
||||
guild_infos_request.url, json={'id': TEST_GUILD_ID, 'name': 'Test Guild'}
|
||||
)
|
||||
)
|
||||
requests_mocker.get(
|
||||
user_get_current_request.url,
|
||||
user_get_current_request.url,
|
||||
json=create_user_info(
|
||||
TEST_USER_ID, TEST_USER_NAME, TEST_USER_DISCRIMINATOR
|
||||
)
|
||||
)
|
||||
)
|
||||
requests_mocker.get(
|
||||
guild_roles_request.url,
|
||||
guild_roles_request.url,
|
||||
json=[ROLE_ALPHA, ROLE_BRAVO, ROLE_MIKE, ROLE_MEMBER]
|
||||
)
|
||||
)
|
||||
requests_mocker.put(add_guild_member_request.url, status_code=201)
|
||||
|
||||
authentication_code = 'auth_code'
|
||||
|
||||
authentication_code = 'auth_code'
|
||||
oauth_url = 'https://www.example.com/oauth'
|
||||
state = ''
|
||||
mock_OAuth2Session.return_value.authorization_url.return_value = \
|
||||
oauth_url, state
|
||||
|
||||
|
||||
# login
|
||||
self.app.set_user(self.member)
|
||||
|
||||
|
||||
# user opens services page
|
||||
services_page = self.app.get(reverse('services:services'))
|
||||
self.assertEqual(services_page.status_code, 200)
|
||||
|
||||
# user clicks Discord service activation link on page
|
||||
# user clicks Discord service activation link on page
|
||||
response = services_page.click(href=reverse('discord:activate'))
|
||||
|
||||
# check we got a redirect to Discord OAuth
|
||||
|
||||
# check we got a redirect to Discord OAuth
|
||||
self.assertRedirects(
|
||||
response, expected_url=oauth_url, fetch_redirect_response=False
|
||||
)
|
||||
@ -493,66 +493,66 @@ class TestUserFeatures(WebTest):
|
||||
response = self.app.get(
|
||||
reverse('discord:callback'), params={'code': authentication_code}
|
||||
)
|
||||
|
||||
|
||||
# user got a success message
|
||||
self.assertTrue(mock_messages.success.called)
|
||||
self.assertFalse(mock_messages.error.called)
|
||||
|
||||
|
||||
requests_made = list()
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
requests_made.append(obj)
|
||||
|
||||
|
||||
expected = [
|
||||
guild_infos_request,
|
||||
user_get_current_request,
|
||||
guild_roles_request,
|
||||
guild_infos_request,
|
||||
user_get_current_request,
|
||||
guild_roles_request,
|
||||
add_guild_member_request
|
||||
]
|
||||
self.assertListEqual(requests_made, expected)
|
||||
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.managers.OAuth2Session')
|
||||
def test_user_activation_failed(
|
||||
self, requests_mocker, mock_OAuth2Session, mock_messages
|
||||
):
|
||||
):
|
||||
# setup
|
||||
requests_mocker.get(
|
||||
guild_infos_request.url, json={'id': TEST_GUILD_ID, 'name': 'Test Guild'}
|
||||
)
|
||||
requests_mocker.get(
|
||||
user_get_current_request.url,
|
||||
user_get_current_request.url,
|
||||
json=create_user_info(
|
||||
TEST_USER_ID, TEST_USER_NAME, TEST_USER_DISCRIMINATOR
|
||||
)
|
||||
)
|
||||
)
|
||||
requests_mocker.get(
|
||||
guild_roles_request.url,
|
||||
guild_roles_request.url,
|
||||
json=[ROLE_ALPHA, ROLE_BRAVO, ROLE_MIKE, ROLE_MEMBER]
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
mock_exception = HTTPError('error')
|
||||
mock_exception.response = Mock()
|
||||
mock_exception.response.status_code = 503
|
||||
requests_mocker.put(add_guild_member_request.url, exc=mock_exception)
|
||||
|
||||
authentication_code = 'auth_code'
|
||||
|
||||
authentication_code = 'auth_code'
|
||||
oauth_url = 'https://www.example.com/oauth'
|
||||
state = ''
|
||||
mock_OAuth2Session.return_value.authorization_url.return_value = \
|
||||
oauth_url, state
|
||||
|
||||
|
||||
# login
|
||||
self.app.set_user(self.member)
|
||||
|
||||
# user opens services page
|
||||
services_page = self.app.get(reverse('services:services'))
|
||||
self.assertEqual(services_page.status_code, 200)
|
||||
|
||||
|
||||
# click activate on the service page
|
||||
response = services_page.click(href=reverse('discord:activate'))
|
||||
|
||||
# check we got a redirect to Discord OAuth
|
||||
|
||||
# check we got a redirect to Discord OAuth
|
||||
self.assertRedirects(
|
||||
response, expected_url=oauth_url, fetch_redirect_response=False
|
||||
)
|
||||
@ -561,124 +561,124 @@ class TestUserFeatures(WebTest):
|
||||
response = self.app.get(
|
||||
reverse('discord:callback'), params={'code': authentication_code}
|
||||
)
|
||||
|
||||
|
||||
# user got a success message
|
||||
self.assertFalse(mock_messages.success.called)
|
||||
self.assertTrue(mock_messages.error.called)
|
||||
|
||||
|
||||
requests_made = list()
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
requests_made.append(obj)
|
||||
|
||||
|
||||
expected = [
|
||||
guild_infos_request,
|
||||
user_get_current_request,
|
||||
guild_roles_request,
|
||||
user_get_current_request,
|
||||
guild_roles_request,
|
||||
add_guild_member_request
|
||||
]
|
||||
self.assertListEqual(requests_made, expected)
|
||||
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
def test_user_deactivation_normal(self, requests_mocker, mock_messages):
|
||||
def test_user_deactivation_normal(self, requests_mocker, mock_messages):
|
||||
# setup
|
||||
requests_mocker.get(
|
||||
guild_infos_request.url, json={'id': TEST_GUILD_ID, 'name': 'Test Guild'}
|
||||
)
|
||||
)
|
||||
requests_mocker.delete(remove_guild_member_request.url, status_code=204)
|
||||
DiscordUser.objects.create(user=self.member, uid=TEST_USER_ID)
|
||||
|
||||
|
||||
# login
|
||||
self.app.set_user(self.member)
|
||||
|
||||
|
||||
# user opens services page
|
||||
services_page = self.app.get(reverse('services:services'))
|
||||
self.assertEqual(services_page.status_code, 200)
|
||||
|
||||
# click deactivate on the service page
|
||||
# click deactivate on the service page
|
||||
response = services_page.click(href=reverse('discord:deactivate'))
|
||||
|
||||
|
||||
# check we got a redirect to service page
|
||||
self.assertRedirects(response, expected_url=reverse('services:services'))
|
||||
|
||||
# user got a success message
|
||||
self.assertTrue(mock_messages.success.called)
|
||||
self.assertFalse(mock_messages.error.called)
|
||||
|
||||
|
||||
requests_made = list()
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
requests_made.append(obj)
|
||||
|
||||
|
||||
expected = [guild_infos_request, remove_guild_member_request]
|
||||
self.assertListEqual(requests_made, expected)
|
||||
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
def test_user_deactivation_fails(self, requests_mocker, mock_messages):
|
||||
def test_user_deactivation_fails(self, requests_mocker, mock_messages):
|
||||
# setup
|
||||
requests_mocker.get(
|
||||
guild_infos_request.url, json={'id': TEST_GUILD_ID, 'name': 'Test Guild'}
|
||||
)
|
||||
)
|
||||
mock_exception = HTTPError('error')
|
||||
mock_exception.response = Mock()
|
||||
mock_exception.response.status_code = 503
|
||||
requests_mocker.delete(remove_guild_member_request.url, exc=mock_exception)
|
||||
|
||||
|
||||
DiscordUser.objects.create(user=self.member, uid=TEST_USER_ID)
|
||||
|
||||
|
||||
# login
|
||||
self.app.set_user(self.member)
|
||||
|
||||
|
||||
# user opens services page
|
||||
services_page = self.app.get(reverse('services:services'))
|
||||
self.assertEqual(services_page.status_code, 200)
|
||||
|
||||
# click deactivate on the service page
|
||||
# click deactivate on the service page
|
||||
response = services_page.click(href=reverse('discord:deactivate'))
|
||||
|
||||
|
||||
# check we got a redirect to service page
|
||||
self.assertRedirects(response, expected_url=reverse('services:services'))
|
||||
|
||||
# user got a success message
|
||||
self.assertFalse(mock_messages.success.called)
|
||||
self.assertTrue(mock_messages.error.called)
|
||||
|
||||
|
||||
requests_made = list()
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
for r in requests_mocker.request_history:
|
||||
obj = DiscordRequest(r.method, r.url)
|
||||
requests_made.append(obj)
|
||||
|
||||
|
||||
expected = [guild_infos_request, remove_guild_member_request]
|
||||
self.assertListEqual(requests_made, expected)
|
||||
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
def test_user_add_new_server(self, requests_mocker, mock_messages):
|
||||
# setup
|
||||
def test_user_add_new_server(self, requests_mocker, mock_messages):
|
||||
# setup
|
||||
mock_exception = HTTPError(Mock(**{"response.status_code": 400}))
|
||||
requests_mocker.get(guild_infos_request.url, exc=mock_exception)
|
||||
|
||||
# login
|
||||
|
||||
# login
|
||||
self.member.is_superuser = True
|
||||
self.member.is_staff = True
|
||||
self.member.save()
|
||||
self.app.set_user(self.member)
|
||||
|
||||
|
||||
# click deactivate on the service page
|
||||
response = self.app.get(reverse('services:services'))
|
||||
|
||||
|
||||
# check we got can see the page and the "link server" button
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertIsNotNone(response.html.find(id='btnLinkDiscordServer'))
|
||||
|
||||
|
||||
def test_when_server_name_fails_user_can_still_see_service_page(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
# setup
|
||||
requests_mocker.get(guild_infos_request.url, exc=DiscordApiBackoff(1000))
|
||||
|
||||
|
||||
# login
|
||||
self.app.set_user(self.member)
|
||||
|
||||
|
||||
# user opens services page
|
||||
services_page = self.app.get(reverse('services:services'))
|
||||
self.assertEqual(services_page.status_code, 200)
|
||||
@ -686,7 +686,7 @@ class TestUserFeatures(WebTest):
|
||||
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||
def test_server_name_is_updated_by_task(
|
||||
self, requests_mocker
|
||||
):
|
||||
):
|
||||
# setup
|
||||
requests_mocker.get(
|
||||
guild_infos_request.url, json={'id': TEST_GUILD_ID, 'name': 'Test Guild'}
|
||||
@ -696,11 +696,11 @@ class TestUserFeatures(WebTest):
|
||||
|
||||
# login
|
||||
self.app.set_user(self.member)
|
||||
|
||||
|
||||
# disable API call to make sure server name is not retrieved from API
|
||||
mock_exception = HTTPError(Mock(**{"response.status_code": 400}))
|
||||
requests_mocker.get(guild_infos_request.url, exc=mock_exception)
|
||||
|
||||
|
||||
# user opens services page
|
||||
services_page = self.app.get(reverse('services:services'))
|
||||
self.assertEqual(services_page.status_code, 200)
|
||||
|
@ -9,21 +9,21 @@ from django.test import TestCase
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from . import (
|
||||
TEST_GUILD_ID,
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_ID,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
TEST_GUILD_ID,
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_ID,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
MODULE_PATH,
|
||||
ROLE_ALPHA,
|
||||
ROLE_BRAVO,
|
||||
ROLE_CHARLIE,
|
||||
ROLE_CHARLIE,
|
||||
)
|
||||
from ..discord_client.tests import create_matched_role
|
||||
from ..app_settings import (
|
||||
DISCORD_APP_ID,
|
||||
DISCORD_APP_SECRET,
|
||||
DISCORD_CALLBACK_URL,
|
||||
DISCORD_APP_ID,
|
||||
DISCORD_APP_SECRET,
|
||||
DISCORD_CALLBACK_URL,
|
||||
)
|
||||
from ..discord_client import DiscordClient, DiscordApiBackoff
|
||||
from ..models import DiscordUser
|
||||
@ -39,32 +39,32 @@ logger = set_logger_to_file(MODULE_PATH + '.managers', __file__)
|
||||
@patch(MODULE_PATH + '.models.DiscordUser.objects.user_group_names')
|
||||
@patch(MODULE_PATH + '.models.DiscordUser.objects.user_formatted_nick')
|
||||
class TestAddUser(TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
self.user_info = {
|
||||
'id': TEST_USER_ID,
|
||||
'id': TEST_USER_ID,
|
||||
'name': TEST_USER_NAME,
|
||||
'username': TEST_USER_NAME,
|
||||
'discriminator': '1234',
|
||||
}
|
||||
self.access_token = 'accesstoken'
|
||||
|
||||
|
||||
def test_can_create_user_no_roles_no_nick(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
mock_user_formatted_nick.return_value = None
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = []
|
||||
mock_DiscordClient.return_value.add_guild_member.return_value = True
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(
|
||||
@ -79,25 +79,25 @@ class TestAddUser(TestCase):
|
||||
self.assertIsNone(kwargs['nick'])
|
||||
|
||||
def test_can_create_user_with_roles_no_nick(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles = [
|
||||
create_matched_role(ROLE_ALPHA),
|
||||
create_matched_role(ROLE_BRAVO),
|
||||
create_matched_role(ROLE_ALPHA),
|
||||
create_matched_role(ROLE_BRAVO),
|
||||
create_matched_role(ROLE_CHARLIE)
|
||||
]
|
||||
mock_user_formatted_nick.return_value = None
|
||||
mock_user_group_names.return_value = ['a', 'b', 'c']
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_user_group_names.return_value = ['a', 'b', 'c']
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = roles
|
||||
mock_DiscordClient.return_value.add_guild_member.return_value = True
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(
|
||||
@ -113,20 +113,20 @@ class TestAddUser(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DISCORD_SYNC_NAMES', True)
|
||||
def test_can_create_user_no_roles_with_nick(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_user_formatted_nick.return_value = TEST_MAIN_NAME
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = []
|
||||
mock_DiscordClient.return_value.add_guild_member.return_value = True
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(
|
||||
@ -142,20 +142,20 @@ class TestAddUser(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DISCORD_SYNC_NAMES', False)
|
||||
def test_can_create_user_no_roles_and_without_nick_if_turned_off(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_user_formatted_nick.return_value = TEST_MAIN_NAME
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = []
|
||||
mock_DiscordClient.return_value.add_guild_member.return_value = True
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(
|
||||
@ -168,44 +168,44 @@ class TestAddUser(TestCase):
|
||||
self.assertEqual(kwargs['access_token'], self.access_token)
|
||||
self.assertIsNone(kwargs['role_ids'])
|
||||
self.assertIsNone(kwargs['nick'])
|
||||
|
||||
|
||||
def test_can_activate_existing_guild_member(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
mock_user_formatted_nick.return_value = None
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = []
|
||||
mock_DiscordClient.return_value.add_guild_member.return_value = None
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(
|
||||
DiscordUser.objects.filter(user=self.user, uid=TEST_USER_ID).exists()
|
||||
)
|
||||
self.assertTrue(mock_DiscordClient.return_value.add_guild_member.called)
|
||||
|
||||
|
||||
def test_return_false_when_user_creation_fails(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_user_formatted_nick.return_value = None
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = []
|
||||
mock_DiscordClient.return_value.add_guild_member.return_value = False
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertFalse(result)
|
||||
self.assertFalse(
|
||||
@ -214,21 +214,21 @@ class TestAddUser(TestCase):
|
||||
self.assertTrue(mock_DiscordClient.return_value.add_guild_member.called)
|
||||
|
||||
def test_return_false_when_on_api_backoff(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_user_formatted_nick.return_value = None
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = []
|
||||
mock_DiscordClient.return_value.add_guild_member.side_effect = \
|
||||
DiscordApiBackoff(999)
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertFalse(result)
|
||||
self.assertFalse(
|
||||
@ -237,14 +237,14 @@ class TestAddUser(TestCase):
|
||||
self.assertTrue(mock_DiscordClient.return_value.add_guild_member.called)
|
||||
|
||||
def test_return_false_on_http_error(
|
||||
self,
|
||||
self,
|
||||
mock_user_formatted_nick,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_user_group_names,
|
||||
mock_exchange_auth_code_for_token,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_user_formatted_nick.return_value = None
|
||||
mock_user_group_names.return_value = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_exchange_auth_code_for_token.return_value = self.access_token
|
||||
mock_DiscordClient.return_value.current_user.return_value = self.user_info
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
@ -253,18 +253,18 @@ class TestAddUser(TestCase):
|
||||
mock_exception.response = Mock()
|
||||
mock_exception.response.status_code = 500
|
||||
mock_DiscordClient.return_value.add_guild_member.side_effect = mock_exception
|
||||
|
||||
|
||||
result = DiscordUser.objects.add_user(self.user, authorization_code='abcdef')
|
||||
self.assertFalse(result)
|
||||
self.assertFalse(
|
||||
DiscordUser.objects.filter(user=self.user, uid=TEST_USER_ID).exists()
|
||||
)
|
||||
self.assertTrue(mock_DiscordClient.return_value.add_guild_member.called)
|
||||
|
||||
|
||||
|
||||
class TestOauthHelpers(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DISCORD_APP_ID', '123456')
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DISCORD_APP_ID', '123456')
|
||||
def test_generate_bot_add_url(self):
|
||||
bot_add_url = DiscordUser.objects.generate_bot_add_url()
|
||||
|
||||
@ -303,18 +303,18 @@ class TestOauthHelpers(TestCase):
|
||||
|
||||
|
||||
class TestUserFormattedNick(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def setUp(self):
|
||||
self.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
|
||||
|
||||
def test_return_nick_when_user_has_main(self):
|
||||
AuthUtils.add_main_character_2(self.user, TEST_MAIN_NAME, TEST_MAIN_ID)
|
||||
result = DiscordUser.objects.user_formatted_nick(self.user)
|
||||
expected = TEST_MAIN_NAME
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_return_none_if_user_has_no_main(self):
|
||||
result = DiscordUser.objects.user_formatted_nick(self.user)
|
||||
def test_return_none_if_user_has_no_main(self):
|
||||
result = DiscordUser.objects.user_formatted_nick(self.user)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
@ -325,16 +325,16 @@ class TestUserGroupNames(TestCase):
|
||||
super().setUpClass()
|
||||
cls.group_1 = Group.objects.create(name='Group 1')
|
||||
cls.group_2 = Group.objects.create(name='Group 2')
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.user = AuthUtils.create_member(TEST_USER_NAME)
|
||||
self.user = AuthUtils.create_member(TEST_USER_NAME)
|
||||
|
||||
def test_return_groups_and_state_names_for_user(self):
|
||||
self.user.groups.add(self.group_1)
|
||||
result = DiscordUser.objects.user_group_names(self.user)
|
||||
expected = ['Group 1', 'Member']
|
||||
self.assertSetEqual(set(result), set(expected))
|
||||
|
||||
|
||||
def test_return_state_only_if_user_has_no_groups(self):
|
||||
result = DiscordUser.objects.user_group_names(self.user)
|
||||
expected = ['Member']
|
||||
@ -355,11 +355,11 @@ class TestUserHasAccount(TestCase):
|
||||
def test_return_false_if_user_has_no_account(self):
|
||||
self.assertFalse(DiscordUser.objects.user_has_account(self.user))
|
||||
|
||||
def test_return_false_if_user_does_not_exist(self):
|
||||
def test_return_false_if_user_does_not_exist(self):
|
||||
my_user = User(username='Dummy')
|
||||
self.assertFalse(DiscordUser.objects.user_has_account(my_user))
|
||||
|
||||
def test_return_false_if_not_called_with_user_object(self):
|
||||
def test_return_false_if_not_called_with_user_object(self):
|
||||
self.assertFalse(DiscordUser.objects.user_has_account('abc'))
|
||||
|
||||
|
||||
@ -371,7 +371,7 @@ class TestServerName(TestCase):
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
|
||||
|
||||
def test_returns_name_when_api_returns_it(self, mock_logger, mock_DiscordClient):
|
||||
server_name = "El Dorado"
|
||||
mock_DiscordClient.return_value.guild_name.return_value = server_name
|
||||
@ -383,7 +383,7 @@ class TestServerName(TestCase):
|
||||
self, mock_logger, mock_DiscordClient
|
||||
):
|
||||
mock_exception = HTTPError('Test exception')
|
||||
mock_exception.response = Mock(**{"status_code": 440})
|
||||
mock_exception.response = Mock(**{"status_code": 440})
|
||||
mock_DiscordClient.return_value.guild_name.side_effect = mock_exception
|
||||
|
||||
self.assertEqual(DiscordUser.objects.server_name(), "")
|
||||
@ -407,7 +407,7 @@ class TestServerName(TestCase):
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient', spec=DiscordClient)
|
||||
class TestRoleForGroup(TestCase):
|
||||
class TestRoleForGroup(TestCase):
|
||||
def test_return_role_if_found(self, mock_DiscordClient):
|
||||
mock_DiscordClient.return_value.match_role_from_name.return_value = ROLE_ALPHA
|
||||
|
||||
|
@ -7,15 +7,15 @@ from django.test import TestCase
|
||||
from allianceauth.tests.auth_utils import AuthUtils
|
||||
|
||||
from . import (
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_ID,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
TEST_USER_NAME,
|
||||
TEST_USER_ID,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
MODULE_PATH,
|
||||
ROLE_ALPHA,
|
||||
ROLE_BRAVO,
|
||||
ROLE_CHARLIE,
|
||||
ROLE_MIKE
|
||||
ROLE_ALPHA,
|
||||
ROLE_BRAVO,
|
||||
ROLE_CHARLIE,
|
||||
ROLE_MIKE
|
||||
)
|
||||
from ..discord_client import DiscordClient, DiscordApiBackoff
|
||||
from ..discord_client.tests import create_matched_role
|
||||
@ -44,39 +44,39 @@ class TestBasicsAndHelpers(TestCase):
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient', spec=DiscordClient)
|
||||
class TestUpdateNick(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self):
|
||||
self.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
self.discord_user = DiscordUser.objects.create(
|
||||
user=self.user, uid=TEST_USER_ID
|
||||
)
|
||||
|
||||
|
||||
def test_can_update(self, mock_DiscordClient):
|
||||
AuthUtils.add_main_character_2(self.user, TEST_MAIN_NAME, TEST_MAIN_ID)
|
||||
AuthUtils.add_main_character_2(self.user, TEST_MAIN_NAME, TEST_MAIN_ID)
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||
|
||||
|
||||
result = self.discord_user.update_nickname()
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
|
||||
def test_dont_update_if_user_has_no_main(self, mock_DiscordClient):
|
||||
def test_dont_update_if_user_has_no_main(self, mock_DiscordClient):
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = False
|
||||
|
||||
|
||||
result = self.discord_user.update_nickname()
|
||||
self.assertFalse(result)
|
||||
self.assertFalse(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
|
||||
|
||||
def test_return_none_if_user_no_longer_a_member(self, mock_DiscordClient):
|
||||
AuthUtils.add_main_character_2(self.user, TEST_MAIN_NAME, TEST_MAIN_ID)
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = None
|
||||
|
||||
|
||||
result = self.discord_user.update_nickname()
|
||||
self.assertIsNone(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
|
||||
def test_return_false_if_api_returns_false(self, mock_DiscordClient):
|
||||
AuthUtils.add_main_character_2(self.user, TEST_MAIN_NAME, TEST_MAIN_ID)
|
||||
AuthUtils.add_main_character_2(self.user, TEST_MAIN_NAME, TEST_MAIN_ID)
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = False
|
||||
|
||||
|
||||
result = self.discord_user.update_nickname()
|
||||
self.assertFalse(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
@ -89,16 +89,16 @@ class TestUpdateUsername(TestCase):
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def setUp(self):
|
||||
self.discord_user = DiscordUser.objects.create(
|
||||
user=self.user,
|
||||
uid=TEST_USER_ID,
|
||||
username=TEST_MAIN_NAME,
|
||||
user=self.user,
|
||||
uid=TEST_USER_ID,
|
||||
username=TEST_MAIN_NAME,
|
||||
discriminator='1234'
|
||||
)
|
||||
|
||||
def test_can_update(self, mock_DiscordClient):
|
||||
|
||||
def test_can_update(self, mock_DiscordClient):
|
||||
new_username = 'New name'
|
||||
new_discriminator = '9876'
|
||||
user_info = {
|
||||
@ -109,7 +109,7 @@ class TestUpdateUsername(TestCase):
|
||||
}
|
||||
}
|
||||
mock_DiscordClient.return_value.guild_member.return_value = user_info
|
||||
|
||||
|
||||
result = self.discord_user.update_username()
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.guild_member.called)
|
||||
@ -138,7 +138,7 @@ class TestUpdateUsername(TestCase):
|
||||
def test_return_false_if_api_returns_corrput_data_2(self, mock_DiscordClient):
|
||||
user_info = {
|
||||
'user': {
|
||||
'id': str(TEST_USER_ID),
|
||||
'id': str(TEST_USER_ID),
|
||||
'discriminator': '1234',
|
||||
}
|
||||
}
|
||||
@ -150,7 +150,7 @@ class TestUpdateUsername(TestCase):
|
||||
def test_return_false_if_api_returns_corrput_data_3(self, mock_DiscordClient):
|
||||
user_info = {
|
||||
'user': {
|
||||
'id': str(TEST_USER_ID),
|
||||
'id': str(TEST_USER_ID),
|
||||
'username': TEST_USER_NAME,
|
||||
}
|
||||
}
|
||||
@ -169,12 +169,12 @@ class TestDeleteUser(TestCase):
|
||||
super().setUpClass()
|
||||
cls.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self):
|
||||
self.discord_user = DiscordUser.objects.create(
|
||||
user=self.user, uid=TEST_USER_ID
|
||||
)
|
||||
|
||||
def test_can_delete_user(self, mock_DiscordClient, mock_notify):
|
||||
def test_can_delete_user(self, mock_DiscordClient, mock_notify):
|
||||
mock_DiscordClient.return_value.remove_guild_member.return_value = True
|
||||
result = self.discord_user.delete_user()
|
||||
self.assertTrue(result)
|
||||
@ -192,7 +192,7 @@ class TestDeleteUser(TestCase):
|
||||
|
||||
def test_can_delete_user_when_member_is_unknown(
|
||||
self, mock_DiscordClient, mock_notify
|
||||
):
|
||||
):
|
||||
mock_DiscordClient.return_value.remove_guild_member.return_value = None
|
||||
result = self.discord_user.delete_user()
|
||||
self.assertTrue(result)
|
||||
@ -219,7 +219,7 @@ class TestDeleteUser(TestCase):
|
||||
)
|
||||
self.assertTrue(mock_DiscordClient.return_value.remove_guild_member.called)
|
||||
self.assertFalse(mock_notify.called)
|
||||
|
||||
|
||||
def test_raise_exception_on_api_backoff(
|
||||
self, mock_DiscordClient, mock_notify
|
||||
):
|
||||
@ -227,7 +227,7 @@ class TestDeleteUser(TestCase):
|
||||
DiscordApiBackoff(999)
|
||||
with self.assertRaises(DiscordApiBackoff):
|
||||
self.discord_user.delete_user()
|
||||
|
||||
|
||||
def test_return_false_on_api_backoff_and_exception_handling_on(
|
||||
self, mock_DiscordClient, mock_notify
|
||||
):
|
||||
@ -244,9 +244,9 @@ class TestDeleteUser(TestCase):
|
||||
mock_exception.response.status_code = 500
|
||||
mock_DiscordClient.return_value.remove_guild_member.side_effect = \
|
||||
mock_exception
|
||||
|
||||
|
||||
with self.assertRaises(HTTPError):
|
||||
self.discord_user.delete_user()
|
||||
self.discord_user.delete_user()
|
||||
|
||||
def test_return_false_on_http_error_and_exception_handling_on(
|
||||
self, mock_DiscordClient, mock_notify
|
||||
@ -264,7 +264,7 @@ class TestDeleteUser(TestCase):
|
||||
@patch(MODULE_PATH + '.models.DiscordUser.objects.user_group_names')
|
||||
class TestUpdateGroups(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self):
|
||||
self.user = AuthUtils.create_user(TEST_USER_NAME)
|
||||
self.discord_user = DiscordUser.objects.create(
|
||||
user=self.user, uid=TEST_USER_ID
|
||||
@ -272,14 +272,14 @@ class TestUpdateGroups(TestCase):
|
||||
self.guild_roles = [ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE]
|
||||
self.roles_requested = [
|
||||
create_matched_role(ROLE_ALPHA), create_matched_role(ROLE_BRAVO)
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
def test_update_if_needed(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles_current = [1]
|
||||
roles_current = [1]
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
@ -287,7 +287,7 @@ class TestUpdateGroups(TestCase):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'roles': roles_current}
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||
|
||||
|
||||
result = self.discord_user.update_groups()
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
@ -295,11 +295,11 @@ class TestUpdateGroups(TestCase):
|
||||
self.assertEqual(set(kwargs['role_ids']), {1, 2})
|
||||
|
||||
def test_update_if_needed_and_preserve_managed_roles(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles_current = [1, 13]
|
||||
roles_current = [1, 13]
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
@ -307,7 +307,7 @@ class TestUpdateGroups(TestCase):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'roles': roles_current}
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||
|
||||
|
||||
result = self.discord_user.update_groups()
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
@ -315,28 +315,28 @@ class TestUpdateGroups(TestCase):
|
||||
self.assertEqual(set(kwargs['role_ids']), {1, 2, 13})
|
||||
|
||||
def test_dont_update_if_not_needed(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles_current = [1, 2, 13]
|
||||
roles_current = [1, 2, 13]
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
mock_DiscordClient.return_value.guild_roles.return_value = self.guild_roles
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'roles': roles_current}
|
||||
|
||||
|
||||
result = self.discord_user.update_groups()
|
||||
self.assertTrue(result)
|
||||
self.assertFalse(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
|
||||
def test_update_if_user_has_no_roles_on_discord(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles_current = []
|
||||
roles_current = []
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
@ -344,30 +344,30 @@ class TestUpdateGroups(TestCase):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'roles': roles_current}
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||
|
||||
|
||||
result = self.discord_user.update_groups()
|
||||
self.assertTrue(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
args, kwargs = mock_DiscordClient.return_value.modify_guild_member.call_args
|
||||
self.assertEqual(set(kwargs['role_ids']), {1, 2})
|
||||
|
||||
|
||||
def test_return_none_if_user_no_longer_a_member(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = None
|
||||
|
||||
|
||||
result = self.discord_user.update_groups()
|
||||
self.assertIsNone(result)
|
||||
self.assertFalse(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
|
||||
def test_return_false_if_api_returns_false(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles_current = [1]
|
||||
roles_current = [1]
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
@ -375,17 +375,17 @@ class TestUpdateGroups(TestCase):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'roles': roles_current}
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = False
|
||||
|
||||
|
||||
result = self.discord_user.update_groups()
|
||||
self.assertFalse(result)
|
||||
self.assertTrue(mock_DiscordClient.return_value.modify_guild_member.called)
|
||||
|
||||
def test_raise_exception_if_member_has_unknown_roles(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
roles_current = [99]
|
||||
roles_current = [99]
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
@ -393,21 +393,21 @@ class TestUpdateGroups(TestCase):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'roles': roles_current}
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.discord_user.update_groups()
|
||||
|
||||
def test_refresh_guild_roles_user_roles_dont_not_match(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
def my_guild_roles(guild_id, use_cache=True):
|
||||
if use_cache:
|
||||
return [ROLE_ALPHA, ROLE_BRAVO, ROLE_MIKE]
|
||||
else:
|
||||
return [ROLE_ALPHA, ROLE_BRAVO, ROLE_CHARLIE, ROLE_MIKE]
|
||||
|
||||
|
||||
roles_current = [3]
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
@ -421,10 +421,10 @@ class TestUpdateGroups(TestCase):
|
||||
self.assertEqual(mock_DiscordClient.return_value.guild_roles.call_count, 2)
|
||||
|
||||
def test_raise_exception_if_member_info_is_invalid(
|
||||
self,
|
||||
mock_user_group_names,
|
||||
self,
|
||||
mock_user_group_names,
|
||||
mock_DiscordClient
|
||||
):
|
||||
):
|
||||
mock_user_group_names.return_value = []
|
||||
mock_DiscordClient.return_value.match_or_create_roles_from_names\
|
||||
.return_value = self.roles_requested
|
||||
@ -432,6 +432,6 @@ class TestUpdateGroups(TestCase):
|
||||
mock_DiscordClient.return_value.guild_member.return_value = \
|
||||
{'user': 'dummy'}
|
||||
mock_DiscordClient.return_value.modify_guild_member.return_value = True
|
||||
|
||||
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.discord_user.update_groups()
|
||||
|
@ -23,7 +23,7 @@ logger = set_logger_to_file(MODULE_PATH, __file__)
|
||||
@patch(MODULE_PATH + '.DiscordUser.update_groups')
|
||||
@patch(MODULE_PATH + ".logger")
|
||||
class TestUpdateGroups(TestCase):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
@ -32,7 +32,7 @@ class TestUpdateGroups(TestCase):
|
||||
cls.group_2 = Group.objects.create(name='Group 2')
|
||||
cls.group_1.user_set.add(cls.user)
|
||||
cls.group_2.user_set.add(cls.user)
|
||||
|
||||
|
||||
def test_can_update_groups(self, mock_logger, mock_update_groups):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
tasks.update_groups(self.user.pk)
|
||||
@ -43,7 +43,7 @@ class TestUpdateGroups(TestCase):
|
||||
):
|
||||
tasks.update_groups(self.user.pk)
|
||||
self.assertFalse(mock_update_groups.called)
|
||||
|
||||
|
||||
def test_retries_on_api_backoff(self, mock_logger, mock_update_groups):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
mock_exception = DiscordApiBackoff(999)
|
||||
@ -63,7 +63,7 @@ class TestUpdateGroups(TestCase):
|
||||
tasks.update_groups(self.user.pk)
|
||||
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
|
||||
|
||||
def test_retry_on_http_error_404_when_user_not_deleted(
|
||||
self, mock_logger, mock_update_groups
|
||||
):
|
||||
@ -77,8 +77,8 @@ class TestUpdateGroups(TestCase):
|
||||
tasks.update_groups(self.user.pk)
|
||||
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
|
||||
def test_retry_on_non_http_error(self, mock_logger, mock_update_groups):
|
||||
|
||||
def test_retry_on_non_http_error(self, mock_logger, mock_update_groups):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
mock_update_groups.side_effect = ConnectionError
|
||||
|
||||
@ -86,23 +86,23 @@ class TestUpdateGroups(TestCase):
|
||||
tasks.update_groups(self.user.pk)
|
||||
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.DISCORD_TASKS_MAX_RETRIES', 3)
|
||||
def test_log_error_if_retries_exhausted(self, mock_logger, mock_update_groups):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
mock_task = MagicMock(**{'request.retries': 3})
|
||||
mock_update_groups.side_effect = ConnectionError
|
||||
update_groups_inner = tasks.update_groups.__wrapped__.__func__
|
||||
|
||||
|
||||
update_groups_inner(mock_task, self.user.pk)
|
||||
self.assertTrue(mock_logger.error.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.delete_user.delay')
|
||||
def test_delete_user_if_user_is_no_longer_member_of_discord_server(
|
||||
self, mock_delete_user, mock_logger, mock_update_groups
|
||||
):
|
||||
mock_update_groups.return_value = None
|
||||
|
||||
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
tasks.update_groups(self.user.pk)
|
||||
self.assertTrue(mock_update_groups.called)
|
||||
@ -111,45 +111,45 @@ class TestUpdateGroups(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.DiscordUser.update_nickname')
|
||||
class TestUpdateNickname(TestCase):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user = AuthUtils.create_member(TEST_USER_NAME)
|
||||
AuthUtils.add_main_character_2(
|
||||
cls.user,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
corp_id='2',
|
||||
corp_name='test_corp',
|
||||
cls.user,
|
||||
TEST_MAIN_NAME,
|
||||
TEST_MAIN_ID,
|
||||
corp_id='2',
|
||||
corp_name='test_corp',
|
||||
corp_ticker='TEST',
|
||||
disconnect_signals=True
|
||||
)
|
||||
cls.discord_user = DiscordUser.objects.create(user=cls.user, uid=TEST_USER_ID)
|
||||
|
||||
def test_can_update_nickname(self, mock_update_nickname):
|
||||
mock_update_nickname.return_value = True
|
||||
|
||||
def test_can_update_nickname(self, mock_update_nickname):
|
||||
mock_update_nickname.return_value = True
|
||||
|
||||
tasks.update_nickname(self.user.pk)
|
||||
self.assertTrue(mock_update_nickname.called)
|
||||
|
||||
def test_no_action_when_user_had_no_account(self, mock_update_nickname):
|
||||
my_user = AuthUtils.create_user('Dummy User')
|
||||
mock_update_nickname.return_value = False
|
||||
|
||||
|
||||
tasks.update_nickname(my_user.pk)
|
||||
self.assertFalse(mock_update_nickname.called)
|
||||
|
||||
def test_retries_on_api_backoff(self, mock_update_nickname):
|
||||
|
||||
def test_retries_on_api_backoff(self, mock_update_nickname):
|
||||
mock_exception = DiscordApiBackoff(999)
|
||||
mock_update_nickname.side_effect = mock_exception
|
||||
|
||||
|
||||
with self.assertRaises(Retry):
|
||||
tasks.update_nickname(self.user.pk)
|
||||
|
||||
def test_retries_on_general_exception(self, mock_update_nickname):
|
||||
def test_retries_on_general_exception(self, mock_update_nickname):
|
||||
mock_update_nickname.side_effect = ConnectionError
|
||||
|
||||
|
||||
with self.assertRaises(Retry):
|
||||
tasks.update_nickname(self.user.pk)
|
||||
|
||||
@ -158,9 +158,9 @@ class TestUpdateNickname(TestCase):
|
||||
mock_task = MagicMock(**{'request.retries': 3})
|
||||
mock_update_nickname.side_effect = ConnectionError
|
||||
update_nickname_inner = tasks.update_nickname.__wrapped__.__func__
|
||||
|
||||
|
||||
update_nickname_inner(mock_task, self.user.pk)
|
||||
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.DiscordUser.update_username')
|
||||
class TestUpdateUsername(TestCase):
|
||||
@ -206,7 +206,7 @@ class TestDeleteUser(TestCase):
|
||||
self, mock_delete_user_delay, mock_delete_user
|
||||
):
|
||||
mock_delete_user.return_value = None
|
||||
|
||||
|
||||
tasks.delete_user(self.user.pk)
|
||||
self.assertTrue(mock_delete_user.called)
|
||||
self.assertFalse(mock_delete_user_delay.called)
|
||||
@ -218,15 +218,15 @@ class TestTaskPerformUserAction(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.user = AuthUtils.create_member('Peter Parker')
|
||||
cls.user = AuthUtils.create_member('Peter Parker')
|
||||
cls.discord_user = DiscordUser.objects.create(user=cls.user, uid=TEST_USER_ID)
|
||||
|
||||
def test_raise_value_error_on_unknown_method(self, mock_update_groups):
|
||||
mock_task = MagicMock(**{'request.retries': 0})
|
||||
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
tasks._task_perform_user_action(mock_task, self.user.pk, 'invalid_method')
|
||||
|
||||
|
||||
def test_catch_and_log_unexpected_exceptions(self, mock_update_groups):
|
||||
mock_task = MagicMock(**{'request.retries': 0})
|
||||
mock_update_groups.side_effect = RuntimeError
|
||||
@ -253,8 +253,8 @@ class TestTaskUpdateServername(TestCase):
|
||||
|
||||
self.assertFalse(mock_logger.error.called)
|
||||
|
||||
def test_retry_on_http_error(self, mock_logger, mock_server_name):
|
||||
mock_exception = HTTPError(MagicMock(**{"response.status_code": 500}))
|
||||
def test_retry_on_http_error(self, mock_logger, mock_server_name):
|
||||
mock_exception = HTTPError(MagicMock(**{"response.status_code": 500}))
|
||||
mock_server_name.side_effect = mock_exception
|
||||
|
||||
with self.assertRaises(Retry):
|
||||
@ -262,20 +262,20 @@ class TestTaskUpdateServername(TestCase):
|
||||
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
|
||||
def test_retry_on_connection_error(self, mock_logger, mock_server_name):
|
||||
def test_retry_on_connection_error(self, mock_logger, mock_server_name):
|
||||
mock_server_name.side_effect = ConnectionError
|
||||
|
||||
with self.assertRaises(Retry):
|
||||
tasks.update_servername()
|
||||
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
|
||||
|
||||
@patch(MODULE_PATH + '.DISCORD_TASKS_MAX_RETRIES', 3)
|
||||
def test_log_error_if_retries_exhausted(self, mock_logger, mock_server_name):
|
||||
mock_task = MagicMock(**{'request.retries': 3})
|
||||
mock_server_name.side_effect = ConnectionError
|
||||
update_groups_inner = tasks.update_servername.__wrapped__.__func__
|
||||
|
||||
|
||||
update_groups_inner(mock_task)
|
||||
self.assertTrue(mock_logger.error.called)
|
||||
|
||||
@ -285,24 +285,24 @@ class TestTaskPerformUsersAction(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
super().setUpClass()
|
||||
|
||||
def test_raise_value_error_on_unknown_method(self, mock_server_name):
|
||||
mock_task = MagicMock(**{'request.retries': 0})
|
||||
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
tasks._task_perform_users_action(mock_task, 'invalid_method')
|
||||
|
||||
|
||||
def test_catch_and_log_unexpected_exceptions(self, mock_server_name):
|
||||
mock_server_name.side_effect = RuntimeError
|
||||
mock_task = MagicMock(**{'request.retries': 0})
|
||||
|
||||
|
||||
tasks._task_perform_users_action(mock_task, 'server_name')
|
||||
|
||||
|
||||
@override_settings(CELERY_ALWAYS_EAGER=True)
|
||||
class TestBulkTasks(TestCase):
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
@ -321,7 +321,7 @@ class TestBulkTasks(TestCase):
|
||||
tasks.update_groups_bulk(expected_pks)
|
||||
self.assertEqual(mock_update_groups.call_count, 2)
|
||||
current_pks = [args[0][0] for args in mock_update_groups.call_args_list]
|
||||
|
||||
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
|
||||
@patch(MODULE_PATH + '.update_groups.si')
|
||||
@ -347,7 +347,7 @@ class TestBulkTasks(TestCase):
|
||||
self.assertEqual(mock_update_nickname.call_count, 2)
|
||||
current_pks = [
|
||||
args[0][0] for args in mock_update_nickname.call_args_list
|
||||
]
|
||||
]
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
|
||||
@patch(MODULE_PATH + '.update_nickname.si')
|
||||
@ -374,11 +374,11 @@ class TestBulkTasks(TestCase):
|
||||
tasks.update_usernames_bulk(expected_pks)
|
||||
self.assertEqual(mock_update_username.call_count, 2)
|
||||
current_pks = [args[0][0] for args in mock_update_username.call_args_list]
|
||||
|
||||
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
|
||||
@patch(MODULE_PATH + '.update_username')
|
||||
@patch(MODULE_PATH + '.update_servername')
|
||||
@patch(MODULE_PATH + '.update_servername')
|
||||
def test_can_update_all_usernames(
|
||||
self, mock_update_servername, mock_update_username
|
||||
):
|
||||
@ -405,7 +405,7 @@ class TestBulkTasks(TestCase):
|
||||
du_3 = DiscordUser.objects.create(user=self.user_3, uid=789)
|
||||
|
||||
tasks.update_all()
|
||||
self.assertEqual(mock_update_groups.si.call_count, 3)
|
||||
self.assertEqual(mock_update_groups.si.call_count, 3)
|
||||
current_pks = [args[0][0] for args in mock_update_groups.si.call_args_list]
|
||||
expected_pks = [du_1.pk, du_2.pk, du_3.pk]
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
@ -415,7 +415,7 @@ class TestBulkTasks(TestCase):
|
||||
expected_pks = [du_1.pk, du_2.pk, du_3.pk]
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
|
||||
self.assertEqual(mock_update_usernames.si.call_count, 3)
|
||||
self.assertEqual(mock_update_usernames.si.call_count, 3)
|
||||
current_pks = [args[0][0] for args in mock_update_usernames.si.call_args_list]
|
||||
expected_pks = [du_1.pk, du_2.pk, du_3.pk]
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
@ -432,14 +432,14 @@ class TestBulkTasks(TestCase):
|
||||
du_3 = DiscordUser.objects.create(user=self.user_3, uid=789)
|
||||
|
||||
tasks.update_all()
|
||||
self.assertEqual(mock_update_groups.si.call_count, 3)
|
||||
self.assertEqual(mock_update_groups.si.call_count, 3)
|
||||
current_pks = [args[0][0] for args in mock_update_groups.si.call_args_list]
|
||||
expected_pks = [du_1.pk, du_2.pk, du_3.pk]
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
|
||||
self.assertEqual(mock_update_nickname.si.call_count, 0)
|
||||
|
||||
self.assertEqual(mock_update_usernames.si.call_count, 3)
|
||||
self.assertEqual(mock_update_usernames.si.call_count, 3)
|
||||
current_pks = [args[0][0] for args in mock_update_usernames.si.call_args_list]
|
||||
expected_pks = [du_1.pk, du_2.pk, du_3.pk]
|
||||
self.assertSetEqual(set(current_pks), set(expected_pks))
|
||||
|
@ -9,19 +9,19 @@ MODULE_PATH = 'allianceauth.services.modules.discord.utils'
|
||||
class TestCleanSetting(TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.settings')
|
||||
def test_default_if_not_set(self, mock_settings):
|
||||
def test_default_if_not_set(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
)
|
||||
self.assertEqual(result, False)
|
||||
|
||||
@patch(MODULE_PATH + '.settings')
|
||||
def test_default_if_not_set_for_none(self, mock_settings):
|
||||
def test_default_if_not_set_for_none(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
None,
|
||||
required_type=int
|
||||
)
|
||||
@ -31,8 +31,8 @@ class TestCleanSetting(TestCase):
|
||||
def test_true_stays_true(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = True
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
'TEST_SETTING_DUMMY',
|
||||
False,
|
||||
)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
@ -40,7 +40,7 @@ class TestCleanSetting(TestCase):
|
||||
def test_false_stays_false(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = False
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
False
|
||||
)
|
||||
self.assertEqual(result, False)
|
||||
@ -49,7 +49,7 @@ class TestCleanSetting(TestCase):
|
||||
def test_default_for_invalid_type_bool(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
False
|
||||
)
|
||||
self.assertEqual(result, False)
|
||||
@ -58,7 +58,7 @@ class TestCleanSetting(TestCase):
|
||||
def test_default_for_invalid_type_int(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
50
|
||||
)
|
||||
self.assertEqual(result, 50)
|
||||
@ -67,7 +67,7 @@ class TestCleanSetting(TestCase):
|
||||
def test_default_if_below_minimum_1(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = -5
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=50
|
||||
)
|
||||
self.assertEqual(result, 50)
|
||||
@ -76,7 +76,7 @@ class TestCleanSetting(TestCase):
|
||||
def test_default_if_below_minimum_2(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = -50
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=50,
|
||||
min_value=-10
|
||||
)
|
||||
@ -86,7 +86,7 @@ class TestCleanSetting(TestCase):
|
||||
def test_default_for_invalid_type_int_2(self, mock_settings):
|
||||
mock_settings.TEST_SETTING_DUMMY = 1000
|
||||
result = clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=50,
|
||||
max_value=100
|
||||
)
|
||||
@ -97,6 +97,6 @@ class TestCleanSetting(TestCase):
|
||||
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
|
||||
with self.assertRaises(ValueError):
|
||||
clean_setting(
|
||||
'TEST_SETTING_DUMMY',
|
||||
'TEST_SETTING_DUMMY',
|
||||
default_value=None
|
||||
)
|
||||
|
@ -11,10 +11,10 @@ from ..discord_client import DiscordClient
|
||||
from ..models import DiscordUser
|
||||
from ..utils import set_logger_to_file
|
||||
from ..views import (
|
||||
discord_callback,
|
||||
reset_discord,
|
||||
deactivate_discord,
|
||||
discord_add_bot,
|
||||
discord_callback,
|
||||
reset_discord,
|
||||
deactivate_discord,
|
||||
discord_add_bot,
|
||||
activate_discord
|
||||
)
|
||||
|
||||
@ -49,10 +49,10 @@ class TestActivateDiscord(SetupClassMixin, TestCase):
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient', spec=DiscordClient)
|
||||
class TestDeactivateDiscord(SetupClassMixin, TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
|
||||
|
||||
def test_when_successful_show_success_message(
|
||||
self, mock_DiscordClient, mock_messages
|
||||
):
|
||||
@ -81,7 +81,7 @@ class TestDeactivateDiscord(SetupClassMixin, TestCase):
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.managers.DiscordClient')
|
||||
class TestResetDiscord(SetupClassMixin, TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
|
||||
@ -93,7 +93,7 @@ class TestResetDiscord(SetupClassMixin, TestCase):
|
||||
request.user = self.user
|
||||
response = reset_discord(request)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.url, reverse("discord:activate"))
|
||||
self.assertEqual(response.url, reverse("discord:activate"))
|
||||
self.assertFalse(mock_messages.error.called)
|
||||
|
||||
def test_when_unsuccessful_message_error_and_redirect_to_service(
|
||||
@ -111,7 +111,7 @@ class TestResetDiscord(SetupClassMixin, TestCase):
|
||||
@patch(MODULE_PATH + '.views.messages')
|
||||
@patch(MODULE_PATH + '.views.DiscordUser.objects.add_user')
|
||||
class TestDiscordCallback(SetupClassMixin, TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
DiscordUser.objects.create(user=self.user, uid=TEST_USER_ID)
|
||||
|
||||
@ -126,7 +126,7 @@ class TestDiscordCallback(SetupClassMixin, TestCase):
|
||||
self.assertEqual(response.url, self.services_url)
|
||||
self.assertTrue(mock_messages.success.called)
|
||||
self.assertFalse(mock_messages.error.called)
|
||||
|
||||
|
||||
def test_handle_no_code(self, mock_add_user, mock_messages):
|
||||
mock_add_user.return_value = True
|
||||
request = self.factory.get(
|
||||
@ -138,7 +138,7 @@ class TestDiscordCallback(SetupClassMixin, TestCase):
|
||||
self.assertEqual(response.url, self.services_url)
|
||||
self.assertFalse(mock_messages.success.called)
|
||||
self.assertTrue(mock_messages.error.called)
|
||||
|
||||
|
||||
def test_error_message_when_user_creation_failed(
|
||||
self, mock_add_user, mock_messages
|
||||
):
|
||||
@ -156,7 +156,7 @@ class TestDiscordCallback(SetupClassMixin, TestCase):
|
||||
|
||||
@patch(MODULE_PATH + '.views.DiscordUser.objects.generate_bot_add_url')
|
||||
class TestDiscordAddBot(TestCase):
|
||||
|
||||
|
||||
def test_add_bot(self, mock_generate_bot_add_url):
|
||||
bot_url = 'https://www.example.com/bot'
|
||||
mock_generate_bot_add_url.return_value = bot_url
|
||||
|
@ -15,7 +15,7 @@ class LoggerAddTag(logging.LoggerAdapter):
|
||||
|
||||
def process(self, msg, kwargs):
|
||||
return '[%s] %s' % (self.prefix, msg), kwargs
|
||||
|
||||
|
||||
|
||||
def clean_setting(
|
||||
name: str,
|
||||
@ -58,7 +58,7 @@ def clean_setting(
|
||||
'You setting for %s it not valid. Please correct it. '
|
||||
'Using default for now: %s',
|
||||
name,
|
||||
default_value
|
||||
default_value
|
||||
)
|
||||
cleaned_value = default_value
|
||||
return cleaned_value
|
||||
@ -66,15 +66,15 @@ def clean_setting(
|
||||
|
||||
def set_logger_to_file(logger_name: str, name: str) -> object:
|
||||
"""set logger for current module to log into a file. Useful for tests.
|
||||
|
||||
|
||||
Args:
|
||||
- logger: current logger object
|
||||
- name: name of current module, e.g. __file__
|
||||
|
||||
|
||||
Returns:
|
||||
- amended logger
|
||||
"""
|
||||
|
||||
|
||||
# reconfigure logger so we get logging from tested module
|
||||
f_format = logging.Formatter(
|
||||
'%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s'
|
||||
|
@ -47,11 +47,11 @@ def reset_discord(request):
|
||||
):
|
||||
logger.info(
|
||||
"Successfully deleted discord user for user %s - "
|
||||
"forwarding to discord activation.",
|
||||
"forwarding to discord activation.",
|
||||
request.user
|
||||
)
|
||||
return redirect("discord:activate")
|
||||
|
||||
|
||||
logger.error(
|
||||
"Unsuccessful attempt to reset discord for user %s", request.user
|
||||
)
|
||||
@ -74,7 +74,7 @@ def discord_callback(request):
|
||||
logger.debug(
|
||||
"Received Discord callback for activation of user %s", request.user
|
||||
)
|
||||
authorization_code = request.GET.get('code', None)
|
||||
authorization_code = request.GET.get('code', None)
|
||||
if not authorization_code:
|
||||
logger.warning(
|
||||
"Did not receive OAuth code from callback for user %s", request.user
|
||||
@ -82,34 +82,34 @@ def discord_callback(request):
|
||||
success = False
|
||||
else:
|
||||
if DiscordUser.objects.add_user(
|
||||
user=request.user,
|
||||
authorization_code=authorization_code,
|
||||
user=request.user,
|
||||
authorization_code=authorization_code,
|
||||
is_rate_limited=False
|
||||
):
|
||||
logger.info(
|
||||
"Successfully activated Discord account for user %s", request.user
|
||||
)
|
||||
success = True
|
||||
|
||||
|
||||
else:
|
||||
logger.error(
|
||||
"Failed to activate Discord account for user %s", request.user
|
||||
)
|
||||
success = False
|
||||
|
||||
|
||||
if success:
|
||||
messages.success(
|
||||
request, _('Your Discord account has been successfully activated.')
|
||||
)
|
||||
else:
|
||||
messages.error(
|
||||
request,
|
||||
request,
|
||||
_(
|
||||
'An error occurred while trying to activate your Discord account. '
|
||||
'Please try again.'
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
return redirect("services:services")
|
||||
|
||||
|
||||
|
@ -6,6 +6,6 @@ from ...admin import ServicesUserAdmin
|
||||
|
||||
@admin.register(DiscourseUser)
|
||||
class DiscourseUserAdmin(ServicesUserAdmin):
|
||||
list_display = ServicesUserAdmin.list_display + (
|
||||
'enabled',
|
||||
)
|
||||
list_display = ServicesUserAdmin.list_display + (
|
||||
'enabled',
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ class ExampleService(ServicesHook):
|
||||
urls = self.Urls()
|
||||
# urls.auth_activate = 'auth_example_activate'
|
||||
# urls.auth_deactivate = 'auth_example_deactivate'
|
||||
# urls.auth_reset_password = 'auth_example_reset_password'
|
||||
# urls.auth_reset_password = 'auth_example_reset_password'
|
||||
# urls.auth_set_password = 'auth_example_set_password'
|
||||
return render_to_string(self.service_ctrl_template, {
|
||||
'service_name': self.title,
|
||||
|
@ -17,7 +17,7 @@ def migrate_service_enabled(apps, schema_editor):
|
||||
app_config.models_module = True
|
||||
create_permissions(app_config, apps=apps, verbosity=0)
|
||||
app_config.models_module = None
|
||||
|
||||
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
Ips4User = apps.get_model("ips4", "Ips4User")
|
||||
|
@ -5,12 +5,12 @@ from ...admin import ServicesUserAdmin
|
||||
|
||||
|
||||
@admin.register(MumbleUser)
|
||||
class MumbleUserAdmin(ServicesUserAdmin):
|
||||
list_display = ServicesUserAdmin.list_display + (
|
||||
'username',
|
||||
class MumbleUserAdmin(ServicesUserAdmin):
|
||||
list_display = ServicesUserAdmin.list_display + (
|
||||
'username',
|
||||
'groups',
|
||||
)
|
||||
search_fields = ServicesUserAdmin.search_fields + (
|
||||
)
|
||||
search_fields = ServicesUserAdmin.search_fields + (
|
||||
'username',
|
||||
'groups'
|
||||
)
|
||||
|
@ -44,7 +44,7 @@ class MumbleService(ServicesHook):
|
||||
logger.debug("Updating %s nickname for %s" % (self.name, user))
|
||||
if MumbleTasks.has_account(user):
|
||||
MumbleTasks.update_display_name.apply_async(args=[user.pk], countdown=5) # cooldown on this task to ensure DB clean when syncing
|
||||
|
||||
|
||||
def validate_user(self, user):
|
||||
if MumbleTasks.has_account(user) and not self.service_active_for_user(user):
|
||||
self.delete_user(user, notify_user=True)
|
||||
|
@ -18,11 +18,11 @@ class MumbleManager(models.Manager):
|
||||
def get_display_name(user):
|
||||
from .auth_hooks import MumbleService
|
||||
return NameFormatter(MumbleService(), user).format_name()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_username(user):
|
||||
return user.profile.main_character.character_name # main character as the user.username may be incorect
|
||||
|
||||
|
||||
@staticmethod
|
||||
def sanitise_username(username):
|
||||
return username.replace(" ", "_")
|
||||
|
@ -6,6 +6,6 @@ from ...admin import ServicesUserAdmin
|
||||
|
||||
@admin.register(OpenfireUser)
|
||||
class OpenfireUserAdmin(ServicesUserAdmin):
|
||||
list_display = ServicesUserAdmin.list_display + ('username',)
|
||||
list_display = ServicesUserAdmin.list_display + ('username',)
|
||||
search_fields = ServicesUserAdmin.search_fields + ('username', )
|
||||
|
||||
|
@ -17,7 +17,7 @@ def migrate_service_enabled(apps, schema_editor):
|
||||
app_config.models_module = True
|
||||
create_permissions(app_config, apps=apps, verbosity=0)
|
||||
app_config.models_module = None
|
||||
|
||||
|
||||
Group = apps.get_model("auth", "Group")
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
OpenfireUser = apps.get_model("openfire", "OpenfireUser")
|
||||
|
@ -5,5 +5,5 @@ from ...admin import ServicesUserAdmin
|
||||
|
||||
@admin.register(Phpbb3User)
|
||||
class Phpbb3UserAdmin(ServicesUserAdmin):
|
||||
list_display = ServicesUserAdmin.list_display + ('username',)
|
||||
list_display = ServicesUserAdmin.list_display + ('username',)
|
||||
search_fields = ServicesUserAdmin.search_fields + ('username', )
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user