[v2] Alliance and Corp Autogroups (#902)

* Add pseudo foreign keys to EveCharacter model

* Ensure populate alliance is called on create

* Add unit tests for model

* Add extra signal for state removal/addition

* Add unit tests for signals

* Add tests for manager

* Add migrations

* Add sync command to admin control

* Prevent whitespace being stripped from group names

* Add documentation
This commit is contained in:
Basraah 2017-12-04 12:52:05 +10:00 committed by GitHub
parent 995a44de0a
commit 70c4b17518
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1075 additions and 0 deletions

View File

@ -0,0 +1 @@
default_app_config = 'allianceauth.eveonline.autogroups.apps.EveAutogroupsConfig'

View File

@ -0,0 +1,39 @@
from django.contrib import admin
from django.db import models
from .models import AutogroupsConfig
import logging
logger = logging.getLogger(__name__)
def sync_user_groups(modeladmin, request, queryset):
for agc in queryset:
logger.debug("update_all_states_group_membership for {}".format(agc))
agc.update_all_states_group_membership()
class AutogroupsConfigAdmin(admin.ModelAdmin):
formfield_overrides = {
models.CharField: {'strip': False}
}
def get_readonly_fields(self, request, obj=None):
if obj: # This is the case when obj is already created i.e. it's an edit
return [
'corp_group_prefix', 'corp_name_source', 'alliance_group_prefix', 'alliance_name_source',
'replace_spaces', 'replace_spaces_with'
]
else:
return []
def get_actions(self, request):
actions = super(AutogroupsConfigAdmin, self).get_actions(request)
actions['sync_user_groups'] = (sync_user_groups,
'sync_user_groups',
'Sync all users groups for this Autogroup Config')
return actions
admin.site.register(AutogroupsConfig, AutogroupsConfigAdmin)

View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class EveAutogroupsConfig(AppConfig):
name = 'allianceauth.eveonline.autogroups'
label = 'eve_autogroups'

View File

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-29 14:44
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('authentication', '0015_user_profiles'),
('auth', '0008_alter_user_username_max_length'),
('eveonline', '0009_on_delete'),
]
operations = [
migrations.CreateModel(
name='AutogroupsConfig',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('corp_groups', models.BooleanField(default=False, help_text='Setting this to false will delete all the created groups.')),
('corp_group_prefix', models.CharField(blank=True, default='Corp ', max_length=50)),
('corp_name_source', models.CharField(choices=[('ticker', 'Ticker'), ('name', 'Full name')], default='name', max_length=20)),
('alliance_groups', models.BooleanField(default=False, help_text='Setting this to false will delete all the created groups.')),
('alliance_group_prefix', models.CharField(blank=True, default='Alliance ', max_length=50)),
('alliance_name_source', models.CharField(choices=[('ticker', 'Ticker'), ('name', 'Full name')], default='name', max_length=20)),
('replace_spaces', models.BooleanField(default=False)),
('replace_spaces_with', models.CharField(blank=True, default='', help_text='Any spaces in the group name will be replaced with this.', max_length=10)),
('states', models.ManyToManyField(related_name='autogroups', to='authentication.State')),
],
),
migrations.CreateModel(
name='ManagedGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
),
migrations.CreateModel(
name='ManagedAllianceGroup',
fields=[
('managedgroup_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='eve_autogroups.ManagedGroup')),
('alliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveAllianceInfo')),
],
bases=('eve_autogroups.managedgroup',),
),
migrations.CreateModel(
name='ManagedCorpGroup',
fields=[
('managedgroup_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='eve_autogroups.ManagedGroup')),
('corp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCorporationInfo')),
],
bases=('eve_autogroups.managedgroup',),
),
migrations.AddField(
model_name='managedgroup',
name='config',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eve_autogroups.AutogroupsConfig'),
),
migrations.AddField(
model_name='managedgroup',
name='group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group'),
),
migrations.AddField(
model_name='autogroupsconfig',
name='alliance_managed_groups',
field=models.ManyToManyField(help_text="A list of alliance groups created and maintained by this AutogroupConfig. You should not edit this list unless you know what you're doing.", related_name='alliance_managed_config', through='eve_autogroups.ManagedAllianceGroup', to='auth.Group'),
),
migrations.AddField(
model_name='autogroupsconfig',
name='corp_managed_groups',
field=models.ManyToManyField(help_text="A list of corporation groups created and maintained by this AutogroupConfig. You should not edit this list unless you know what you're doing.", related_name='corp_managed_config', through='eve_autogroups.ManagedCorpGroup', to='auth.Group'),
),
]

View File

@ -0,0 +1,238 @@
import logging
from django.db import models, transaction
from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist
from allianceauth.authentication.models import State
from allianceauth.eveonline.models import EveCorporationInfo, EveAllianceInfo
logger = logging.getLogger(__name__)
def get_users_for_state(state: State):
return User.objects.select_related('profile').prefetch_related('profile__main_character')\
.filter(profile__state__pk=state.pk)
class AutogroupsConfigManager(models.Manager):
def update_groups_for_state(self, state: State):
"""
Update all the Group memberships for the users
who have State
:param state: State to update for
:return:
"""
users = get_users_for_state(state)
for config in self.filter(states=state):
logger.debug("in state loop")
for user in users:
logger.debug("in user loop for {}".format(user))
config.update_group_membership_for_user(user)
def update_groups_for_user(self, user: User, state: State = None):
"""
Update the Group memberships for the given users state
:param user: User to update for
:param state: State to update user for
:return:
"""
if state is None:
state = user.profile.state
for config in self.filter(states=state):
config.update_group_membership_for_user(user)
class AutogroupsConfig(models.Model):
OPT_TICKER = 'ticker'
OPT_NAME = 'name'
NAME_OPTIONS = (
(OPT_TICKER, 'Ticker'),
(OPT_NAME, 'Full name'),
)
states = models.ManyToManyField(State, related_name='autogroups')
corp_groups = models.BooleanField(default=False,
help_text="Setting this to false will delete all the created groups.")
corp_group_prefix = models.CharField(max_length=50, default='Corp ', blank=True)
corp_name_source = models.CharField(max_length=20, choices=NAME_OPTIONS, default=OPT_NAME)
alliance_groups = models.BooleanField(default=False,
help_text="Setting this to false will delete all the created groups.")
alliance_group_prefix = models.CharField(max_length=50, default='Alliance ', blank=True)
alliance_name_source = models.CharField(max_length=20, choices=NAME_OPTIONS, default=OPT_NAME)
corp_managed_groups = models.ManyToManyField(
Group, through='ManagedCorpGroup', related_name='corp_managed_config',
help_text='A list of corporation groups created and maintained by this AutogroupConfig. '
'You should not edit this list unless you know what you\'re doing.')
alliance_managed_groups = models.ManyToManyField(
Group, through='ManagedAllianceGroup', related_name='alliance_managed_config',
help_text='A list of alliance groups created and maintained by this AutogroupConfig. '
'You should not edit this list unless you know what you\'re doing.')
replace_spaces = models.BooleanField(default=False)
replace_spaces_with = models.CharField(
max_length=10, default='', blank=True,
help_text='Any spaces in the group name will be replaced with this.')
objects = AutogroupsConfigManager()
def __init__(self, *args, **kwargs):
super(AutogroupsConfig, self).__init__(*args, **kwargs)
def __repr__(self):
return self.__class__.__name__
def __str__(self):
return 'States: ' + (' '.join(list(self.states.all().values_list('name', flat=True))) if self.pk else str(None))
def update_all_states_group_membership(self):
list(map(self.update_group_membership_for_state, self.states.all()))
def update_group_membership_for_state(self, state: State):
list(map(self.update_group_membership_for_user, get_users_for_state(state)))
@transaction.atomic
def update_group_membership_for_user(self, user: User):
self.update_alliance_group_membership(user)
self.update_corp_group_membership(user)
def user_entitled_to_groups(self, user: User) -> bool:
try:
return user.profile.state in self.states.all()
except ObjectDoesNotExist:
return False
@transaction.atomic
def update_alliance_group_membership(self, user: User):
group = None
try:
if not self.alliance_groups or not self.user_entitled_to_groups(user):
logger.debug('User {} does not have required state'.format(user))
return
else:
alliance = user.profile.main_character.alliance
if alliance is None:
return
group = self.get_alliance_group(alliance)
except EveAllianceInfo.DoesNotExist:
logger.warning('User {} main characters alliance does not exist in the database.'
' Group membership not updated'.format(user))
except AttributeError:
logger.warning('User {} does not have a main character. Group membership not updated'.format(user))
finally:
self.remove_user_from_corp_groups(user, except_group=group)
if group is not None:
user.groups.add(group)
@transaction.atomic
def update_corp_group_membership(self, user: User):
group = None
try:
if not self.corp_groups or not self.user_entitled_to_groups(user):
logger.debug('User {} does not have required state'.format(user))
else:
corp = user.profile.main_character.corporation
group = self.get_corp_group(corp)
except EveCorporationInfo.DoesNotExist:
logger.warning('User {} main characters corporation does not exist in the database.'
' Group membership not updated'.format(user))
except AttributeError:
logger.warning('User {} does not have a main character. Group membership not updated'.format(user))
finally:
self.remove_user_from_corp_groups(user, except_group=group)
if group is not None:
user.groups.add(group)
@transaction.atomic
def remove_user_from_alliance_groups(self, user: User, except_group: Group = None):
remove_groups = user.groups.filter(pk__in=self.alliance_managed_groups.all().values_list('pk', flat=True))
if except_group is not None:
remove_groups = remove_groups.exclude(pk=except_group.pk)
list(map(user.groups.remove, remove_groups))
@transaction.atomic
def remove_user_from_corp_groups(self, user: User, except_group: Group = None):
remove_groups = user.groups.filter(pk__in=self.corp_managed_groups.all().values_list('pk', flat=True))
if except_group is not None:
remove_groups = remove_groups.exclude(pk=except_group.pk)
list(map(user.groups.remove, remove_groups))
def get_alliance_group(self, alliance: EveAllianceInfo) -> Group:
return self.create_alliance_group(alliance)
def get_corp_group(self, corp: EveCorporationInfo) -> Group:
return self.create_corp_group(corp)
@transaction.atomic
def create_alliance_group(self, alliance: EveAllianceInfo) -> Group:
group, created = Group.objects.get_or_create(name=self.get_alliance_group_name(alliance))
if created:
ManagedAllianceGroup.objects.create(group=group, config=self, alliance=alliance)
return group
@transaction.atomic
def create_corp_group(self, corp: EveCorporationInfo) -> Group:
group, created = Group.objects.get_or_create(name=self.get_corp_group_name(corp))
if created:
ManagedCorpGroup.objects.create(group=group, config=self, corp=corp)
return group
def delete_alliance_managed_groups(self):
"""
Deletes ALL managed alliance groups
"""
self.alliance_managed_groups.all().delete()
def delete_corp_managed_groups(self):
"""
Deletes ALL managed corp groups
"""
self.corp_managed_groups.all().delete()
def get_alliance_group_name(self, alliance: EveAllianceInfo) -> str:
if self.alliance_name_source == self.OPT_TICKER:
name = alliance.alliance_ticker
elif self.alliance_name_source == self.OPT_NAME:
name = alliance.alliance_name
else:
raise NameSourceException('Not a valid name source')
return self._replace_spaces(self.alliance_group_prefix + name)
def get_corp_group_name(self, corp: EveCorporationInfo) -> str:
if self.corp_name_source == self.OPT_TICKER:
name = corp.corporation_ticker
elif self.corp_name_source == self.OPT_NAME:
name = corp.corporation_name
else:
raise NameSourceException('Not a valid name source')
return self._replace_spaces(self.corp_group_prefix + name)
def _replace_spaces(self, name: str) -> str:
"""
Replace the spaces in the given name based on the config
:param name: name to replace spaces in
:return: name with spaces replaced with the configured character(s) or unchanged if configured
"""
if self.replace_spaces:
return name.strip().replace(' ', str(self.replace_spaces_with))
return name
class ManagedGroup(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
config = models.ForeignKey(AutogroupsConfig, on_delete=models.CASCADE)
class ManagedCorpGroup(ManagedGroup):
corp = models.ForeignKey(EveCorporationInfo, on_delete=models.CASCADE)
class ManagedAllianceGroup(ManagedGroup):
alliance = models.ForeignKey(EveAllianceInfo, on_delete=models.CASCADE)
class NameSourceException(Exception):
pass

View File

@ -0,0 +1,66 @@
import logging
from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save, pre_delete, m2m_changed
from allianceauth.authentication.models import UserProfile, State
from .models import AutogroupsConfig
logger = logging.getLogger(__name__)
@receiver(pre_save, sender=AutogroupsConfig)
def pre_save_config(sender, instance, *args, **kwargs):
"""
Checks if enable was toggled on group config and
deletes groups if necessary.
"""
logger.debug("Received pre_save from {}".format(instance))
if not instance.pk:
# new model being created
return
try:
old_instance = AutogroupsConfig.objects.get(pk=instance.pk)
# Check if enable was toggled, delete groups?
if old_instance.alliance_groups is True and instance.alliance_groups is False:
instance.delete_alliance_managed_groups()
if old_instance.corp_groups is True and instance.corp_groups is False:
instance.delete_corp_managed_groups()
except AutogroupsConfig.DoesNotExist:
pass
@receiver(pre_delete, sender=AutogroupsConfig)
def pre_delete_config(sender, instance, *args, **kwargs):
"""
Delete groups on deleting config
"""
instance.delete_corp_managed_groups()
instance.delete_alliance_managed_groups()
@receiver(post_save, sender=UserProfile)
def check_groups_on_profile_update(sender, instance, created, *args, **kwargs):
"""
Trigger check when main character or state changes.
"""
update_fields = kwargs.pop('update_fields', []) or []
if 'main_character' in update_fields or 'state' in update_fields:
AutogroupsConfig.objects.update_groups_for_user(instance.user)
@receiver(m2m_changed, sender=AutogroupsConfig.states.through)
def autogroups_states_changed(sender, instance, action, reverse, model, pk_set, *args, **kwargs):
"""
Trigger group membership update when a state is added or removed from
an autogroup config.
"""
if action.startswith('post_'):
for pk in pk_set:
try:
state = State.objects.get(pk=pk)
instance.update_group_membership_for_state(state)
except State.DoesNotExist:
# Deleted States handled by the profile state change
pass

View File

@ -0,0 +1,27 @@
from unittest import mock
from django.db.models.signals import pre_save, post_save, pre_delete, m2m_changed
from allianceauth.authentication.models import UserProfile
from allianceauth.authentication.signals import state_changed
from allianceauth.eveonline.models import EveCharacter
from .. import signals
from ..models import AutogroupsConfig
MODULE_PATH = 'allianceauth.eveonline.autogroups'
def patch(target, *args, **kwargs):
return mock.patch('{}{}'.format(MODULE_PATH, target), *args, **kwargs)
def connect_signals():
pre_save.connect(receiver=signals.pre_save_config, sender=AutogroupsConfig)
pre_delete.connect(receiver=signals.pre_delete_config, sender=AutogroupsConfig)
post_save.connect(receiver=signals.check_groups_on_profile_update, sender=UserProfile)
m2m_changed.connect(receiver=signals.autogroups_states_changed, sender=AutogroupsConfig.states.through)
def disconnect_signals():
pre_save.disconnect(receiver=signals.pre_save_config, sender=AutogroupsConfig)
pre_delete.disconnect(receiver=signals.pre_delete_config, sender=AutogroupsConfig)
post_save.disconnect(receiver=signals.check_groups_on_profile_update, sender=UserProfile)
m2m_changed.disconnect(receiver=signals.autogroups_states_changed, sender=AutogroupsConfig.states.through)

View File

@ -0,0 +1,34 @@
from django.test import TestCase
from allianceauth.tests.auth_utils import AuthUtils
from ..models import AutogroupsConfig
from . import patch
class AutogroupsConfigManagerTestCase(TestCase):
def test_update_groups_for_state(self, ):
member = AuthUtils.create_member('test member')
obj = AutogroupsConfig.objects.create()
obj.states.add(member.profile.state)
with patch('.models.AutogroupsConfig.update_group_membership_for_user') as update_group_membership_for_user:
AutogroupsConfig.objects.update_groups_for_state(member.profile.state)
self.assertTrue(update_group_membership_for_user.called)
self.assertEqual(update_group_membership_for_user.call_count, 1)
args, kwargs = update_group_membership_for_user.call_args
self.assertEqual(args[0], member)
def test_update_groups_for_user(self):
member = AuthUtils.create_member('test member')
obj = AutogroupsConfig.objects.create()
obj.states.add(member.profile.state)
with patch('.models.AutogroupsConfig.update_group_membership_for_user') as update_group_membership_for_user:
AutogroupsConfig.objects.update_groups_for_user(member)
self.assertTrue(update_group_membership_for_user.called)
self.assertEqual(update_group_membership_for_user.call_count, 1)
args, kwargs = update_group_membership_for_user.call_args
self.assertEqual(args[0], member)

View File

@ -0,0 +1,342 @@
from django.test import TestCase
from django.contrib.auth.models import Group
from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo
from ..models import AutogroupsConfig, get_users_for_state
from . import patch, connect_signals, disconnect_signals
class AutogroupsConfigTestCase(TestCase):
def setUp(self):
# Disconnect signals
disconnect_signals()
self.member = AuthUtils.create_member('test user')
state = AuthUtils.get_member_state()
self.alliance = EveAllianceInfo.objects.create(
alliance_id='3456',
alliance_name='alliance name',
alliance_ticker='TIKR',
executor_corp_id='2345',
)
self.corp = EveCorporationInfo.objects.create(
corporation_id='2345',
corporation_name='corp name',
corporation_ticker='TIKK',
member_count=10,
alliance=self.alliance,
)
state.member_alliances.add(self.alliance)
state.member_corporations.add(self.corp)
def tearDown(self):
# Reconnect signals
connect_signals()
def test_get_users_for_state(self):
result = get_users_for_state(self.member.profile.state)
self.assertIn(self.member, result)
self.assertEqual(len(result), 1)
@patch('.models.AutogroupsConfig.update_alliance_group_membership')
@patch('.models.AutogroupsConfig.update_corp_group_membership')
def test_update_group_membership(self, update_corp, update_alliance):
agc = AutogroupsConfig.objects.create()
agc.update_group_membership_for_user(self.member)
self.assertTrue(update_corp.called)
self.assertTrue(update_alliance.called)
args, kwargs = update_corp.call_args
self.assertEqual(args[0], self.member)
args, kwargs = update_alliance.call_args
self.assertEqual(args[0], self.member)
def test_update_alliance_group_membership(self):
obj = AutogroupsConfig.objects.create(alliance_groups=True)
obj.states.add(AuthUtils.get_member_state())
char = EveCharacter.objects.create(
character_id='1234',
character_name='test character',
corporation_id='2345',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3456',
alliance_name='alliance name',
)
self.member.profile.main_character = char
self.member.profile.save()
pre_groups = self.member.groups.all()
# Act
obj.update_alliance_group_membership(self.member)
group = obj.create_alliance_group(self.alliance)
group_qs = Group.objects.filter(pk=group.pk)
self.assertIn(group, self.member.groups.all())
self.assertQuerysetEqual(self.member.groups.all(), map(repr, pre_groups | group_qs), ordered=False)
def test_update_alliance_group_membership_no_main_character(self):
obj = AutogroupsConfig.objects.create()
obj.states.add(AuthUtils.get_member_state())
# Act
obj.update_alliance_group_membership(self.member)
group = obj.get_alliance_group(self.alliance)
self.assertNotIn(group, self.member.groups.all())
def test_update_alliance_group_membership_no_alliance_model(self):
obj = AutogroupsConfig.objects.create()
obj.states.add(AuthUtils.get_member_state())
char = EveCharacter.objects.create(
character_id='1234',
character_name='test character',
corporation_id='2345',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3459',
alliance_name='alliance name',
)
self.member.profile.main_character = char
self.member.profile.save()
# Act
obj.update_alliance_group_membership(self.member)
group = obj.get_alliance_group(self.alliance)
self.assertNotIn(group, self.member.groups.all())
def test_update_corp_group_membership(self):
obj = AutogroupsConfig.objects.create(corp_groups=True)
obj.states.add(AuthUtils.get_member_state())
char = EveCharacter.objects.create(
character_id='1234',
character_name='test character',
corporation_id='2345',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3456',
alliance_name='alliance name',
)
self.member.profile.main_character = char
self.member.profile.save()
pre_groups = self.member.groups.all()
# Act
obj.update_corp_group_membership(self.member)
group = obj.get_corp_group(self.corp)
group_qs = Group.objects.filter(pk=group.pk)
self.assertIn(group, self.member.groups.all())
self.assertQuerysetEqual(self.member.groups.all(), map(repr, pre_groups | group_qs), ordered=False)
def test_update_corp_group_membership_no_state(self):
obj = AutogroupsConfig.objects.create(corp_groups=True)
char = EveCharacter.objects.create(
character_id='1234',
character_name='test character',
corporation_id='2345',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3456',
alliance_name='alliance name',
)
self.member.profile.main_character = char
self.member.profile.save()
pre_groups = list(self.member.groups.all())
# Act
obj.update_corp_group_membership(self.member)
group = obj.get_corp_group(self.corp)
post_groups = list(self.member.groups.all())
self.assertNotIn(group, post_groups)
self.assertListEqual(pre_groups, post_groups)
def test_update_corp_group_membership_no_main_character(self):
obj = AutogroupsConfig.objects.create()
obj.states.add(AuthUtils.get_member_state())
# Act
obj.update_corp_group_membership(self.member)
group = obj.get_corp_group(self.corp)
self.assertNotIn(group, self.member.groups.all())
def test_update_corp_group_membership_no_corp_model(self):
obj = AutogroupsConfig.objects.create()
obj.states.add(AuthUtils.get_member_state())
char = EveCharacter.objects.create(
character_id='1234',
character_name='test character',
corporation_id='2348',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3456',
alliance_name='alliance name',
)
self.member.profile.main_character = char
self.member.profile.save()
# Act
obj.update_corp_group_membership(self.member)
group = obj.get_corp_group(self.corp)
self.assertNotIn(group, self.member.groups.all())
def test_remove_user_from_alliance_groups(self):
obj = AutogroupsConfig.objects.create()
result = obj.get_alliance_group(self.alliance)
result.user_set.add(self.member)
self.assertIn(result, self.member.groups.all())
# Act
obj.remove_user_from_alliance_groups(self.member)
self.assertNotIn(result, self.member.groups.all())
def test_remove_user_from_corp_groups(self):
obj = AutogroupsConfig.objects.create()
result = obj.get_corp_group(self.corp)
result.user_set.add(self.member)
self.assertIn(result, self.member.groups.all())
# Act
obj.remove_user_from_corp_groups(self.member)
self.assertNotIn(result, self.member.groups.all())
def test_get_alliance_group(self):
obj = AutogroupsConfig.objects.create()
result = obj.get_alliance_group(self.alliance)
group = Group.objects.get(name='Alliance alliance name')
self.assertEqual(result, group)
self.assertEqual(obj.get_alliance_group_name(self.alliance), result.name)
self.assertTrue(obj.alliance_managed_groups.filter(pk=result.pk).exists())
def test_get_corp_group(self):
obj = AutogroupsConfig.objects.create()
result = obj.get_corp_group(self.corp)
group = Group.objects.get(name='Corp corp name')
self.assertEqual(result, group)
self.assertEqual(obj.get_corp_group_name(self.corp), group.name)
self.assertTrue(obj.corp_managed_groups.filter(pk=group.pk).exists())
def test_create_alliance_group(self):
obj = AutogroupsConfig.objects.create()
result = obj.create_alliance_group(self.alliance)
group = Group.objects.get(name='Alliance alliance name')
self.assertEqual(result, group)
self.assertEqual(obj.get_alliance_group_name(self.alliance), group.name)
self.assertTrue(obj.alliance_managed_groups.filter(pk=group.pk).exists())
def test_create_corp_group(self):
obj = AutogroupsConfig.objects.create()
result = obj.create_corp_group(self.corp)
group = Group.objects.get(name='Corp corp name')
self.assertEqual(result, group)
self.assertEqual(obj.get_corp_group_name(self.corp), group.name)
self.assertTrue(obj.corp_managed_groups.filter(pk=group.pk).exists())
def test_delete_alliance_managed_groups(self):
obj = AutogroupsConfig.objects.create()
obj.create_alliance_group(self.alliance)
self.assertTrue(obj.alliance_managed_groups.all().exists())
obj.delete_alliance_managed_groups()
self.assertFalse(obj.alliance_managed_groups.all().exists())
def test_delete_corp_managed_groups(self):
obj = AutogroupsConfig.objects.create()
obj.create_corp_group(self.corp)
self.assertTrue(obj.corp_managed_groups.all().exists())
obj.delete_corp_managed_groups()
self.assertFalse(obj.corp_managed_groups.all().exists())
def test_get_alliance_group_name(self):
obj = AutogroupsConfig()
obj.replace_spaces = True
obj.replace_spaces_with = '_'
result = obj.get_alliance_group_name(self.alliance)
self.assertEqual(result, 'Alliance_alliance_name')
def test_get_alliance_group_name_ticker(self):
obj = AutogroupsConfig()
obj.replace_spaces = True
obj.replace_spaces_with = '_'
obj.alliance_name_source = obj.OPT_TICKER
result = obj.get_alliance_group_name(self.alliance)
self.assertEqual(result, 'Alliance_TIKR')
def test_get_corp_group_name(self):
obj = AutogroupsConfig()
obj.replace_spaces = True
obj.replace_spaces_with = '_'
result = obj.get_corp_group_name(self.corp)
self.assertEqual(result, 'Corp_corp_name')
def test_get_corp_group_name_ticker(self):
obj = AutogroupsConfig()
obj.replace_spaces = True
obj.replace_spaces_with = '_'
obj.corp_name_source = obj.OPT_TICKER
result = obj.get_corp_group_name(self.corp)
self.assertEqual(result, 'Corp_TIKK')
def test__replace_spaces(self):
obj = AutogroupsConfig()
obj.replace_spaces = True
obj.replace_spaces_with = '*'
name = ' test name '
result = obj._replace_spaces(name)
self.assertEqual(result, 'test*name')

View File

@ -0,0 +1,208 @@
from django.test import TestCase
from django.contrib.auth.models import Group, User
from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo
from ..models import AutogroupsConfig, ManagedAllianceGroup
from . import patch, disconnect_signals, connect_signals
class SignalsTestCase(TestCase):
def setUp(self):
disconnect_signals()
self.member = AuthUtils.create_member('test user')
state = AuthUtils.get_member_state()
self.char = EveCharacter.objects.create(
character_id='1234',
character_name='test character',
corporation_id='2345',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3456',
alliance_name='alliance name',
)
self.member.profile.main_character = self.char
self.member.profile.save()
self.alliance = EveAllianceInfo.objects.create(
alliance_id='3456',
alliance_name='alliance name',
alliance_ticker='TIKR',
executor_corp_id='2345',
)
self.corp = EveCorporationInfo.objects.create(
corporation_id='2345',
corporation_name='corp name',
corporation_ticker='TIKK',
member_count=10,
alliance=self.alliance,
)
state.member_alliances.add(self.alliance)
state.member_corporations.add(self.corp)
connect_signals()
@patch('.models.AutogroupsConfigManager.update_groups_for_user')
def test_check_groups_on_profile_update_state(self, update_groups_for_user):
# Trigger signal
self.member.profile.state = AuthUtils.get_guest_state()
self.member.profile.save()
self.assertTrue(update_groups_for_user.called)
self.assertEqual(update_groups_for_user.call_count, 1)
args, kwargs = update_groups_for_user.call_args
self.assertEqual(args[0], self.member)
@patch('.models.AutogroupsConfigManager.update_groups_for_user')
def test_check_groups_on_profile_update_main_character(self, update_groups_for_user):
char = EveCharacter.objects.create(
character_id='1266',
character_name='test character2',
corporation_id='2345',
corporation_name='test corp',
corporation_ticker='tickr',
alliance_id='3456',
alliance_name='alliance name',
)
# Trigger signal
self.member.profile.main_character = char
self.member.profile.save()
self.assertTrue(update_groups_for_user.called)
self.assertEqual(update_groups_for_user.call_count, 1)
args, kwargs = update_groups_for_user.call_args
self.assertEqual(args[0], self.member)
member = User.objects.get(pk=self.member.pk)
self.assertEqual(member.profile.state, AuthUtils.get_member_state())
@patch('.models.AutogroupsConfigManager.update_groups_for_user')
def test_check_groups_on_character_update(self, update_groups_for_user):
"""
Test update_groups_for_user is called when main_character properties
are changed.
"""
# Trigger signal
self.member.profile.main_character.corporation_id = '2300'
self.member.profile.main_character.save()
self.assertTrue(update_groups_for_user.called)
self.assertEqual(update_groups_for_user.call_count, 1)
args, kwargs = update_groups_for_user.call_args
self.assertEqual(args[0], self.member)
member = User.objects.get(pk=self.member.pk)
self.assertEqual(member.profile.state, AuthUtils.get_member_state())
@patch('.models.AutogroupsConfig.delete_corp_managed_groups')
@patch('.models.AutogroupsConfig.delete_alliance_managed_groups')
def test_pre_save_config_deletes_alliance_groups(self, delete_alliance_managed_groups, delete_corp_managed_groups):
"""
Test that delete_alliance_managed_groups is called when the alliance_groups
setting is toggled to False
"""
obj = AutogroupsConfig.objects.create(alliance_groups=True)
obj.create_alliance_group(self.alliance)
# Trigger signal
obj.alliance_groups = False
obj.save()
self.assertTrue(delete_alliance_managed_groups.called)
self.assertFalse(delete_corp_managed_groups.called)
self.assertEqual(delete_alliance_managed_groups.call_count, 1)
@patch('.models.AutogroupsConfig.delete_alliance_managed_groups')
@patch('.models.AutogroupsConfig.delete_corp_managed_groups')
def test_pre_save_config_deletes_corp_groups(self, delete_corp_managed_groups, delete_alliance_managed_groups):
"""
Test that delete_corp_managed_groups is called when the corp_groups
setting is toggled to False
"""
obj = AutogroupsConfig.objects.create(corp_groups=True)
obj.create_corp_group(self.corp)
# Trigger signal
obj.corp_groups = False
obj.save()
self.assertTrue(delete_corp_managed_groups.called)
self.assertFalse(delete_alliance_managed_groups.called)
self.assertEqual(delete_corp_managed_groups.call_count, 1)
@patch('.models.AutogroupsConfig.delete_alliance_managed_groups')
@patch('.models.AutogroupsConfig.delete_corp_managed_groups')
def test_pre_save_config_does_nothing(self, delete_corp_managed_groups, delete_alliance_managed_groups):
"""
Test groups arent deleted if we arent setting the enabled params to False
"""
obj = AutogroupsConfig.objects.create(corp_groups=True)
obj.create_corp_group(self.corp)
# Trigger signal
obj.alliance_groups = True
obj.save()
self.assertFalse(delete_corp_managed_groups.called)
self.assertFalse(delete_alliance_managed_groups.called)
@patch('.models.AutogroupsConfig.delete_alliance_managed_groups')
@patch('.models.AutogroupsConfig.delete_corp_managed_groups')
def test_pre_delete_config(self, delete_corp_managed_groups, delete_alliance_managed_groups):
"""
Test groups are deleted if config is deleted
"""
obj = AutogroupsConfig.objects.create()
# Trigger signal
obj.delete()
self.assertTrue(delete_corp_managed_groups.called)
self.assertTrue(delete_alliance_managed_groups.called)
@patch('.models.AutogroupsConfig.update_group_membership_for_state')
def test_autogroups_states_changed_add(self, update_group_membership_for_state):
"""
Test update_group_membership_for_state is called when a state is added to
the AutogroupsConfig
"""
obj = AutogroupsConfig.objects.create(alliance_groups=True)
state = AuthUtils.get_member_state()
# Trigger
obj.states.add(state)
self.assertTrue(update_group_membership_for_state.called)
self.assertEqual(update_group_membership_for_state.call_count, 1)
args, kwargs = update_group_membership_for_state.call_args
self.assertEqual(args[0], state)
@patch('.models.AutogroupsConfig.update_group_membership_for_state')
def test_autogroups_states_changed_remove(self, update_group_membership_for_state):
"""
Test update_group_membership_for_state is called when a state is removed from
the AutogroupsConfig
"""
obj = AutogroupsConfig.objects.create(alliance_groups=True)
state = AuthUtils.get_member_state()
disconnect_signals()
obj.states.add(state)
connect_signals()
# Trigger
obj.states.remove(state)
self.assertTrue(update_group_membership_for_state.called)
self.assertEqual(update_group_membership_for_state.call_count, 1)
args, kwargs = update_group_membership_for_state.call_args
self.assertEqual(args[0], state)

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,35 @@
# Auto Groups
```eval_rst
.. note::
New in 2.0
```
Auto groups allows you to automatically place users of certain states into Corp or Alliance based groups. These groups are created when the first user is added to them and removed when the configuration is deleted.
## Installation
Add `allianceauth.eveonline.autogroups` to your `INSTALLED_APPS` and run migrations. All other settings are controlled via the admin panel under the `Eve_Autogroups` section.
## Configuring a group
When you create an autogroup config you will be given the following options:
![Create Autogroup page](/_static/images/features/autogroups/group-creation.png)
```eval_rst
.. warning::
After creating a group you wont be able to change the Corp and Alliance group prefixes, name source and the replace spaces settings. Make sure you configure these the way you want before creating the config. If you need to change these you will have to create a new autogroup config.
```
- States selects which states will be added to automatic corp/alliance groups
- Corp/Alliance groups checkbox toggles corp/alliance autogroups on or off for this config.
- Corp/Alliance group prefix sets the prefix for the group name, e.g. if your corp was called `MyCorp` and your prefix was `Corp `, your autogroup name would be created as `Corp MyCorp`. This field accepts leading/trailing spaces.
- Corp/Alliance name source sets the source of the corp/alliance name used in creating the group name. Currently the options are Full name and Ticker.
- Replace spaces allows you to replace spaces in the autogroup name with the value in the Replace spaces with field. This can be blank.

View File

@ -8,6 +8,7 @@
hrapplications
corpstats
groups
autogroups
permissions_tool
nameformats
```

View File

@ -17,6 +17,7 @@ NOSE_ARGS = [
CELERY_ALWAYS_EAGER = True # Forces celery to run locally for testing
INSTALLED_APPS += [
'allianceauth.eveonline.autogroups',
'allianceauth.hrapplications',
'allianceauth.timerboard',
'allianceauth.srp',