remove trailing whitespaces

This commit is contained in:
Peter Pfeufer 2021-05-17 09:46:11 +02:00
parent 10bd77d761
commit 8c3df89d52
No known key found for this signature in database
GPG Key ID: 6051D2C6AD4EBC27
146 changed files with 2088 additions and 2061 deletions

View File

@ -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

View File

@ -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
View 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)$

View File

@ -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,
)

View File

@ -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

View File

@ -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()

View File

@ -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):

View File

@ -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}),

View File

@ -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 ;
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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
)

View File

@ -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

View File

@ -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)

View File

@ -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'),
]

View File

@ -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,

View File

@ -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):

View File

@ -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
),
)

View File

@ -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
)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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"""

View File

@ -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(

View File

@ -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
)

View File

@ -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:

View File

@ -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 %}

View File

@ -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'

View File

@ -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
)

View File

@ -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'

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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">

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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():

View File

@ -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)

View File

@ -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:

View File

@ -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.')

View File

@ -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)

View File

@ -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)
)

View File

@ -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')

View File

@ -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)

View File

@ -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)

View File

@ -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'
),
]

View File

@ -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)

View File

@ -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>

View File

@ -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',
],
},

View File

@ -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.

View File

@ -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'

View File

@ -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

View File

@ -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):

View File

@ -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'

View File

@ -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)

View File

@ -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

View File

@ -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"""

View File

@ -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.
"""
"""

View File

@ -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"""

View File

@ -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 {

View File

@ -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,))

View File

@ -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(

View File

@ -1,8 +1,8 @@
from unittest import TestCase
from ..exceptions import (
DiscordApiBackoff,
DiscordClientException,
DiscordApiBackoff,
DiscordClientException,
DiscordRateLimitExhausted,
DiscordTooManyRequestsError
)

View File

@ -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):

View File

@ -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 = ""

View File

@ -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

View File

@ -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)

View File

@ -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'

View File

@ -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)

View File

@ -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]

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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))

View File

@ -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
)

View File

@ -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

View File

@ -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'

View File

@ -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")

View File

@ -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',
)

View File

@ -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,

View File

@ -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")

View File

@ -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'
)

View File

@ -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)

View File

@ -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(" ", "_")

View File

@ -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', )

View File

@ -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")

View File

@ -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