Merge branch 'v2-dev' of https://github.com/allianceauth/allianceauth into sso_registration

Conflicts:
	alliance_auth/settings.py.example
	test_allianceauth/settings.py
This commit is contained in:
Adarnof 2017-09-28 20:40:02 -04:00
commit 67ff9eb379
66 changed files with 853 additions and 416 deletions

View File

@ -6,7 +6,7 @@ from django.contrib.auth import login, authenticate
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core import signing
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _
from esi.decorators import token_required

View File

@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
class CorpStats(models.Model):
token = models.ForeignKey(Token, on_delete=models.CASCADE)
corp = models.OneToOneField(EveCorporationInfo)
corp = models.OneToOneField(EveCorporationInfo, on_delete=models.CASCADE)
last_update = models.DateTimeField(auto_now=True)
class Meta:

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-28 02:16
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0008_remove_apikeys'),
]
operations = [
migrations.AlterField(
model_name='evecorporationinfo',
name='alliance',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveAllianceInfo'),
),
]

View File

@ -1,4 +1,5 @@
from django.db import models
from typing import Union
from .managers import EveCharacterManager, EveCharacterProviderManager
from .managers import EveCorporationManager, EveCorporationProviderManager
@ -6,34 +7,6 @@ from .managers import EveAllianceManager, EveAllianceProviderManager
from . import providers
class EveCharacter(models.Model):
character_id = models.CharField(max_length=254, unique=True)
character_name = models.CharField(max_length=254, unique=True)
corporation_id = models.CharField(max_length=254)
corporation_name = models.CharField(max_length=254)
corporation_ticker = models.CharField(max_length=254)
alliance_id = models.CharField(max_length=254, blank=True, null=True, default='')
alliance_name = models.CharField(max_length=254, blank=True, null=True, default='')
objects = EveCharacterManager()
provider = EveCharacterProviderManager()
def update_character(self, character: providers.Character = None):
if character is None:
character = self.provider.get_character(self.character_id)
self.character_name = character.name
self.corporation_id = character.corp.id
self.corporation_name = character.corp.name
self.corporation_ticker = character.corp.ticker
self.alliance_id = character.alliance.id
self.alliance_name = character.alliance.name
self.save()
return self
def __str__(self):
return self.character_name
class EveAllianceInfo(models.Model):
alliance_id = models.CharField(max_length=254, unique=True)
alliance_name = models.CharField(max_length=254, unique=True)
@ -68,7 +41,7 @@ class EveCorporationInfo(models.Model):
corporation_name = models.CharField(max_length=254, unique=True)
corporation_ticker = models.CharField(max_length=254)
member_count = models.IntegerField()
alliance = models.ForeignKey(EveAllianceInfo, blank=True, null=True)
alliance = models.ForeignKey(EveAllianceInfo, blank=True, null=True, on_delete=models.SET_NULL)
objects = EveCorporationManager()
provider = EveCorporationProviderManager()
@ -86,3 +59,51 @@ class EveCorporationInfo(models.Model):
def __str__(self):
return self.corporation_name
class EveCharacter(models.Model):
character_id = models.CharField(max_length=254, unique=True)
character_name = models.CharField(max_length=254, unique=True)
corporation_id = models.CharField(max_length=254)
corporation_name = models.CharField(max_length=254)
corporation_ticker = models.CharField(max_length=254)
alliance_id = models.CharField(max_length=254, blank=True, null=True, default='')
alliance_name = models.CharField(max_length=254, blank=True, null=True, default='')
objects = EveCharacterManager()
provider = EveCharacterProviderManager()
@property
def alliance(self) -> Union[EveAllianceInfo, None]:
"""
Pseudo foreign key from alliance_id to EveAllianceInfo
:raises: EveAllianceInfo.DoesNotExist
:return: EveAllianceInfo or None
"""
if self.alliance_id is None:
return None
return EveAllianceInfo.objects.get(alliance_id=self.alliance_id)
@property
def corporation(self) -> EveCorporationInfo:
"""
Pseudo foreign key from corporation_id to EveCorporationInfo
:raises: EveCorporationInfo.DoesNotExist
:return: EveCorporationInfo
"""
return EveCorporationInfo.objects.get(corporation_id=self.corporation_id)
def update_character(self, character: providers.Character = None):
if character is None:
character = self.provider.get_character(self.character_id)
self.character_name = character.name
self.corporation_id = character.corp.id
self.corporation_name = character.corp.name
self.corporation_ticker = character.corp.ticker
self.alliance_id = character.alliance.id
self.alliance_name = character.alliance.name
self.save()
return self
def __str__(self):
return self.character_name

View File

@ -0,0 +1,121 @@
from django.test import TestCase
from ..models import EveCharacter, EveCorporationInfo, EveAllianceInfo
class EveCharacterTestCase(TestCase):
def test_corporation_prop(self):
"""
Test that the correct corporation is returned by the corporation property
"""
character = EveCharacter.objects.create(
character_id='1234',
character_name='character.name',
corporation_id='2345',
corporation_name='character.corp.name',
corporation_ticker='character.corp.ticker',
alliance_id='character.alliance.id',
alliance_name='character.alliance.name',
)
expected = EveCorporationInfo.objects.create(
corporation_id='2345',
corporation_name='corp.name',
corporation_ticker='corp.ticker',
member_count=10,
alliance=None,
)
incorrect = EveCorporationInfo.objects.create(
corporation_id='9999',
corporation_name='corp.name1',
corporation_ticker='corp.ticker1',
member_count=10,
alliance=None,
)
self.assertEqual(character.corporation, expected)
self.assertNotEqual(character.corporation, incorrect)
def test_corporation_prop_exception(self):
"""
Check that an exception is raised when the expected
object is not in the database
"""
character = EveCharacter.objects.create(
character_id='1234',
character_name='character.name',
corporation_id='2345',
corporation_name='character.corp.name',
corporation_ticker='character.corp.ticker',
alliance_id='character.alliance.id',
alliance_name='character.alliance.name',
)
with self.assertRaises(EveCorporationInfo.DoesNotExist):
result = character.corporation
def test_alliance_prop(self):
"""
Test that the correct alliance is returned by the alliance property
"""
character = EveCharacter.objects.create(
character_id='1234',
character_name='character.name',
corporation_id='2345',
corporation_name='character.corp.name',
corporation_ticker='character.corp.ticker',
alliance_id='3456',
alliance_name='character.alliance.name',
)
expected = EveAllianceInfo.objects.create(
alliance_id='3456',
alliance_name='alliance.name',
alliance_ticker='alliance.ticker',
executor_corp_id='alliance.executor_corp_id',
)
incorrect = EveAllianceInfo.objects.create(
alliance_id='9001',
alliance_name='alliance.name1',
alliance_ticker='alliance.ticker1',
executor_corp_id='alliance.executor_corp_id1',
)
self.assertEqual(character.alliance, expected)
self.assertNotEqual(character.alliance, incorrect)
def test_alliance_prop_exception(self):
"""
Check that an exception is raised when the expected
object is not in the database
"""
character = EveCharacter.objects.create(
character_id='1234',
character_name='character.name',
corporation_id='2345',
corporation_name='character.corp.name',
corporation_ticker='character.corp.ticker',
alliance_id='3456',
alliance_name='character.alliance.name',
)
with self.assertRaises(EveAllianceInfo.DoesNotExist):
result = character.alliance
def test_alliance_prop_none(self):
"""
Check that None is returned when the character has no alliance
"""
character = EveCharacter.objects.create(
character_id='1234',
character_name='character.name',
corporation_id='2345',
corporation_name='character.corp.name',
corporation_ticker='character.corp.ticker',
alliance_id=None,
alliance_name=None,
)
self.assertIsNone(character.alliance)

View File

@ -23,11 +23,11 @@ class Fatlink(models.Model):
class Fat(models.Model):
character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE)
fatlink = models.ForeignKey(Fatlink)
fatlink = models.ForeignKey(Fatlink, on_delete=models.CASCADE)
system = models.CharField(max_length=30)
shiptype = models.CharField(max_length=30)
station = models.CharField(max_length=125)
user = models.ForeignKey(User)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
unique_together = (('character', 'fatlink'),)

View File

@ -2,7 +2,6 @@
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% load bootstrap_pagination %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "Fatlink view" %}{% endblock page_title %}
@ -20,9 +19,6 @@
<div class="panel panel-default">
<div class="panel-heading">{% trans "Registered characters" %}</div>
<div class="panel-body">
<div class="text-center">
{% bootstrap_paginate registered_fats range=10 %}
</div>
<table class="table table-responsive table-hover">
<tr>
<th class="text-center">{% trans "User" %}</th>

View File

@ -29,19 +29,6 @@ SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'sw
logger = logging.getLogger(__name__)
FATS_PER_PAGE = int(getattr(settings, 'FATS_PER_PAGE', 20))
def get_page(model_list, page_num):
p = Paginator(model_list, FATS_PER_PAGE)
try:
fats = p.page(page_num)
except PageNotAnInteger:
fats = p.page(1)
except EmptyPage:
fats = p.page(p.num_pages)
return fats
class CorpStat(object):
def __init__(self, corp_id, start_of_month, start_of_next_month, corp=None):
@ -359,8 +346,6 @@ def modify_fatlink_view(request, hash=""):
registered_fats = Fat.objects.filter(fatlink=fatlink).order_by('character__character_name')
fat_page = get_page(registered_fats, request.GET.get('page', 1))
context = {'fatlink': fatlink, 'registered_fats': fat_page}
context = {'fatlink': fatlink, 'registered_fats': registered_fats}
return render(request, 'fleetactivitytracking/fatlinkmodify.html', context=context)

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-28 02:16
from __future__ import unicode_literals
import django.contrib.auth.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('auth', '0008_alter_user_username_max_length'),
('groupmanagement', '0006_request_groups_perm'),
]
operations = [
migrations.CreateModel(
name='ProxyGroup',
fields=[
],
options={
'verbose_name': 'group',
'indexes': [],
'proxy': True,
'verbose_name_plural': 'groups',
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
migrations.RemoveField(
model_name='grouprequest',
name='main_char',
),
]

View File

@ -10,9 +10,16 @@ from allianceauth.eveonline.models import EveCharacter
class GroupRequest(models.Model):
status = models.CharField(max_length=254)
leave_request = models.BooleanField(default=0)
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
main_char = models.ForeignKey(EveCharacter)
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
@property
def main_char(self):
"""
Legacy property for main character
:return: self.users main character
"""
return self.user.profile.main_character
def __str__(self):
return self.user.username + ":" + self.group.name

View File

@ -23,7 +23,7 @@ class ApplicationChoice(models.Model):
class ApplicationForm(models.Model):
questions = SortedManyToManyField(ApplicationQuestion)
corp = models.OneToOneField(EveCorporationInfo)
corp = models.OneToOneField(EveCorporationInfo, on_delete=models.CASCADE)
def __str__(self):
return str(self.corp)

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-28 02:16
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('optimer', '0003_make_strings_more_stringy'),
]
operations = [
migrations.AlterField(
model_name='optimer',
name='eve_character',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter'),
),
]

View File

@ -17,7 +17,7 @@ class OpTimer(models.Model):
operation_name = models.CharField(max_length=254, default="")
fc = models.CharField(max_length=254, default="")
post_time = models.DateTimeField(default=timezone.now)
eve_character = models.ForeignKey(EveCharacter)
eve_character = models.ForeignKey(EveCharacter, null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.operation_name

View File

@ -10,7 +10,6 @@ from allianceauth.tests.auth_utils import AuthUtils
class PermissionsToolViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
self.none_user = AuthUtils.create_user('none_user', disconnect_signals=True)
@ -18,7 +17,6 @@ class PermissionsToolViewsTestCase(TestCase):
self.none_user3 = AuthUtils.create_user('none_user3', disconnect_signals=True)
self.no_perm_user = AuthUtils.create_user('no_perm_user', disconnect_signals=True)
self.no_perm_user.set_password('password')
AuthUtils.disconnect_signals()
self.no_perm_group = Group.objects.create(name="No Permission Group")
@ -38,17 +36,16 @@ class PermissionsToolViewsTestCase(TestCase):
AuthUtils.connect_signals()
def test_menu_item(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
response = self.client.get(urls.reverse('permissions_tool:overview'))
response_content = str(response.content, encoding='utf8')
response_content = response.content.decode('utf-8')
self.assertInHTML(
'<li><a class="active" href="/permissions/overview/"><i class="fa fa-key fa-id-card"></i> Permissions Audit</a></li>',
response_content)
self.assertInHTML('<li><a class="active" href="/permissions/overview/">'
'<i class="fa fa-key fa-id-card"></i> Permissions Audit</a></li>', response_content)
def test_permissions_overview(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
response = self.client.get(urls.reverse('permissions_tool:overview'))
@ -63,22 +60,25 @@ class PermissionsToolViewsTestCase(TestCase):
for perm in response.context['permissions']:
if perm['permission'] == self.permission:
tested_context = True
self.assertDictContainsSubset({'users': 1}, perm)
self.assertDictContainsSubset({'groups': 1}, perm)
self.assertDictContainsSubset({'group_users': 3}, perm)
self.assertIn('users', perm)
self.assertEqual(perm['users'], 1)
self.assertIn('groups', perm)
self.assertEqual(perm['groups'], 1)
self.assertIn('group_users', perm)
self.assertEqual(perm['group_users'], 3)
break
self.assertTrue(tested_context)
def test_permissions_overview_perms(self):
# Ensure permission effectively denys access
self.client.login(username=self.no_perm_user.username, password='password')
self.client.force_login(self.no_perm_user)
response = self.client.get(urls.reverse('permissions_tool:overview'))
self.assertEqual(response.status_code, 302)
def test_permissions_audit(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
response = self.client.get(urls.reverse('permissions_tool:audit',
kwargs={
@ -99,7 +99,7 @@ class PermissionsToolViewsTestCase(TestCase):
def test_permissions_audit_perms(self):
# Ensure permission effectively denys access
self.client.login(username=self.no_perm_user.username, password='password')
self.client.force_login(self.no_perm_user)
response = self.client.get(urls.reverse('permissions_tool:audit',
kwargs={

View File

@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-05 21:40
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0008_alter_user_username_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='DiscordAuthToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.CharField(max_length=254, unique=True)),
('token', models.CharField(max_length=254)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='GroupCache',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('groups', models.TextField(default={})),
('service', models.CharField(choices=[(b'discourse', b'discourse'), (b'discord', b'discord')], max_length=254, unique=True)),
],
),
]

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-10-16 01:35
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('services', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='discordauthtoken',
name='user',
),
migrations.DeleteModel(
name='DiscordAuthToken',
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-09-02 06:07
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('services', '0002_auto_20161016_0135'),
]
operations = [
migrations.DeleteModel(
name='GroupCache',
),
]

View File

@ -141,12 +141,10 @@ class DiscordHooksTestCase(TestCase):
class DiscordViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.save()
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.views.DiscordOAuthManager')
def test_activate(self, manager):

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'discord'
module_urls = [
# Discord Service Control
url(r'^activate/$', views.activate_discord, name='activate'),
@ -12,5 +14,5 @@ module_urls = [
]
urlpatterns = [
url(r'^discord/', include(module_urls, namespace='discord'))
url(r'^discord/', include((module_urls, app_name), namespace=app_name))
]

View File

@ -113,14 +113,12 @@ class DiscourseHooksTestCase(TestCase):
class DiscourseViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
@mock.patch(MODULE_PATH + '.tasks.DiscourseManager')
def test_sso_member(self, manager):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
data = {'sso': 'bm9uY2U9Y2I2ODI1MWVlZmI1MjExZTU4YzAwZmYxMzk1ZjBjMGI%3D%0A',
'sig': '2828aa29899722b35a2f191d34ef9b3ce695e0e6eeec47deb46d588d70c7cb56'}
response = self.client.get('/discourse/sso', data=data, follow=False)

View File

@ -1,9 +1,11 @@
from django.conf.urls import url, include
app_name = 'example'
module_urls = [
# Add your module URLs here
]
urlpatterns = [
url(r'^example/', include(module_urls, namespace='example')),
url(r'^example/', include((module_urls, app_name), namespace=app_name)),
]

View File

@ -68,14 +68,13 @@ class Ips4HooksTestCase(TestCase):
class Ips4ViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.views.Ips4Manager')
def test_activate(self, manager):

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'ips4'
module_urls = [
# IPS4 Service Control
url(r'^activate/$', views.activate_ips4, name='activate'),
@ -11,5 +13,5 @@ module_urls = [
]
urlpatterns = [
url(r'^ips4/', include(module_urls, namespace='ips4'))
url(r'^ips4/', include((module_urls, app_name), namespace=app_name))
]

View File

@ -96,14 +96,13 @@ class MarketHooksTestCase(TestCase):
class MarketViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.views.MarketManager')
def test_activate(self, manager):

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'evernusmarket'
module_urls = [
# Alliance Market Control
url(r'^activate/$', views.activate_market, name='activate'),
@ -11,5 +13,5 @@ module_urls = [
]
urlpatterns = [
url(r'^evernus-market/', include(module_urls, namespace='evernusmarket'))
url(r'^evernus-market/', include((module_urls, app_name), namespace=app_name))
]

View File

@ -2,7 +2,7 @@ from django.db import models
class MumbleUser(models.Model):
user = models.OneToOneField('auth.User', related_name='mumble', null=True)
user = models.OneToOneField('auth.User', related_name='mumble', null=True, on_delete=models.CASCADE)
username = models.CharField(max_length=254, unique=True)
pwhash = models.CharField(max_length=80)
hashfn = models.CharField(max_length=20, default='sha1')

View File

@ -119,7 +119,6 @@ class MumbleHooksTestCase(TestCase):
class MumbleViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation',
@ -127,7 +126,7 @@ class MumbleViewsTestCase(TestCase):
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
def test_activate(self):
self.login()

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'mumble'
module_urls = [
# Mumble service control
url(r'^activate/$', views.activate_mumble, name='activate'),
@ -12,5 +14,5 @@ module_urls = [
]
urlpatterns = [
url(r'^mumble/', include(module_urls, namespace='mumble'))
url(r'^mumble/', include((module_urls, app_name), namespace=app_name))
]

View File

@ -123,14 +123,13 @@ class OpenfireHooksTestCase(TestCase):
class OpenfireViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.tasks.OpenfireManager')
@mock.patch(MODULE_PATH + '.views.OpenfireManager')

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'openfire'
module_urls = [
# Jabber Service Control
url(r'^activate/$', views.activate_jabber, name='activate'),
@ -12,5 +14,5 @@ module_urls = [
]
urlpatterns = [
url(r'^openfire/', include(module_urls, namespace='openfire')),
url(r'^openfire/', include((module_urls, app_name), namespace=app_name)),
]

View File

@ -123,14 +123,13 @@ class Phpbb3HooksTestCase(TestCase):
class Phpbb3ViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.tasks.Phpbb3Manager')
@mock.patch(MODULE_PATH + '.views.Phpbb3Manager')

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'phpbb3'
module_urls = [
# Forum Service Control
url(r'^activate/$', views.activate_forum, name='activate'),
@ -11,5 +13,5 @@ module_urls = [
]
urlpatterns = [
url(r'^phpbb3/', include(module_urls, namespace='phpbb3'))
url(r'^phpbb3/', include((module_urls, app_name), namespace=app_name))
]

View File

@ -127,14 +127,13 @@ class SeatHooksTestCase(TestCase):
class SeatViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.tasks.SeatManager')
@mock.patch(MODULE_PATH + '.views.SeatManager')

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name='seat'
module_urls = [
# SeAT Service Control
url(r'^activate/$', views.activate_seat, name='activate'),
@ -11,5 +13,5 @@ module_urls = [
]
urlpatterns = [
url(r'^seat/', include(module_urls, namespace='seat')),
url(r'^seat/', include((module_urls, app_name), namespace=app_name)),
]

View File

@ -123,14 +123,13 @@ class SmfHooksTestCase(TestCase):
class SmfViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.tasks.SmfManager')
@mock.patch(MODULE_PATH + '.views.SmfManager')

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'smf'
module_urls = [
# SMF Service Control
url(r'^activate/$', views.activate_smf, name='activate'),
@ -11,5 +13,5 @@ module_urls = [
]
urlpatterns = [
url(r'^smf/', include(module_urls, namespace='smf')),
url(r'^smf/', include((module_urls, app_name), namespace=app_name)),
]

View File

@ -24,7 +24,7 @@ class Migration(migrations.Migration):
('auth_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
],
options={
'db_table': 'services_authts',
'db_table': 'teamspeak3_authts',
'verbose_name': 'Auth / TS Group',
},
),
@ -35,7 +35,7 @@ class Migration(migrations.Migration):
('ts_group_name', models.CharField(max_length=30)),
],
options={
'db_table': 'services_tsgroup',
'db_table': 'teamspeak3_tsgroup',
'verbose_name': 'TS Group',
},
),
@ -47,7 +47,7 @@ class Migration(migrations.Migration):
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'services_usertsgroup',
'db_table': 'teamspeak3_usertsgroup',
'verbose_name': 'User TS Group',
},
),

View File

@ -1,8 +1,9 @@
from django.db import models
from django.contrib.auth.models import User, Group
class Teamspeak3User(models.Model):
user = models.OneToOneField('auth.User',
user = models.OneToOneField(User,
primary_key=True,
on_delete=models.CASCADE,
related_name='teamspeak3')
@ -30,7 +31,7 @@ class TSgroup(models.Model):
class AuthTS(models.Model):
auth_group = models.ForeignKey('auth.Group')
auth_group = models.ForeignKey(Group, on_delete=models.CASCADE)
ts_group = models.ManyToManyField(TSgroup)
class Meta:
@ -41,7 +42,7 @@ class AuthTS(models.Model):
class UserTSgroup(models.Model):
user = models.ForeignKey('auth.User')
user = models.ForeignKey(User, on_delete=models.CASCADE)
ts_group = models.ManyToManyField(TSgroup)
class Meta:

View File

@ -136,7 +136,6 @@ class Teamspeak3ViewsTestCase(TestCase):
# Inert signals before setup begins
with mock.patch(MODULE_PATH + '.signals.trigger_all_ts_update') as trigger_all_ts_update:
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
@ -150,7 +149,7 @@ class Teamspeak3ViewsTestCase(TestCase):
def login(self, user=None, password=None):
if user is None:
user = self.member
self.client.login(username=user.username, password=password if password else 'password')
self.client.force_login(user)
@mock.patch(MODULE_PATH + '.forms.Teamspeak3Manager')
@mock.patch(MODULE_PATH + '.views.Teamspeak3Manager')

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'teamspeak3'
module_urls = [
# Teamspeak3 service control
url(r'^activate/$', views.activate_teamspeak3,
@ -16,5 +18,5 @@ module_urls = [
]
urlpatterns = [
url(r'^teamspeak3/', include(module_urls, namespace='teamspeak3')),
url(r'^teamspeak3/', include((module_urls, app_name), namespace=app_name)),
]

View File

@ -99,14 +99,13 @@ class XenforoHooksTestCase(TestCase):
class XenforoViewsTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation')
add_permissions()
def login(self):
self.client.login(username=self.member.username, password='password')
self.client.force_login(self.member)
@mock.patch(MODULE_PATH + '.tasks.XenForoManager')
@mock.patch(MODULE_PATH + '.views.XenForoManager')

View File

@ -2,6 +2,8 @@ from django.conf.urls import url, include
from . import views
app_name = 'xenforo'
module_urls = [
# XenForo service control
url(r'^activate/$', views.activate_xenforo_forum, name='activate'),
@ -11,5 +13,5 @@ module_urls = [
]
urlpatterns = [
url(r'^xenforo/', include(module_urls, namespace='xenforo')),
url(r'^xenforo/', include((module_urls, app_name), namespace=app_name)),
]

View File

@ -5,11 +5,11 @@ from . import views
urlpatterns = [
# Services
url(r'^services/', include([
url(r'^services/', include(([
url(r'^$', views.services_view, name='services'),
# Tools
url(r'^tool/fleet_formatter_tool/$', views.fleet_formatter_view, name='fleet_format_tool'),
], namespace='services')),
], 'services'), namespace='services')),
]
# Append hooked service urls

View File

@ -27,7 +27,6 @@ INSTALLED_APPS = [
'django.contrib.humanize',
'django_celery_beat',
'bootstrapform',
'bootstrap_pagination',
'sortedm2m',
'esi',
'allianceauth',

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-28 02:16
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('srp', '0003_make_strings_more_stringy'),
]
operations = [
migrations.AlterField(
model_name='srpfleetmain',
name='fleet_commander',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter'),
),
migrations.AlterField(
model_name='srpuserrequest',
name='character',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter'),
),
]

View File

@ -10,7 +10,7 @@ class SrpFleetMain(models.Model):
fleet_time = models.DateTimeField()
fleet_srp_code = models.CharField(max_length=254, default="")
fleet_srp_status = models.CharField(max_length=254, default="")
fleet_commander = models.ForeignKey(EveCharacter)
fleet_commander = models.ForeignKey(EveCharacter, null=True, on_delete=models.SET_NULL)
fleet_srp_aar_link = models.CharField(max_length=254, default="")
def __str__(self):
@ -40,8 +40,8 @@ class SrpUserRequest(models.Model):
additional_info = models.CharField(max_length=254, default="")
srp_status = models.CharField(max_length=8, default="Pending", choices=SRP_STATUS_CHOICES)
srp_total_amount = models.BigIntegerField(default=0)
character = models.ForeignKey(EveCharacter)
srp_fleet_main = models.ForeignKey(SrpFleetMain)
character = models.ForeignKey(EveCharacter, null=True, on_delete=models.SET_NULL)
srp_fleet_main = models.ForeignKey(SrpFleetMain, on_delete=models.CASCADE)
kb_total_loss = models.BigIntegerField(default=0)
srp_ship_name = models.CharField(max_length=254, default="")
post_time = models.DateTimeField(default=timezone.now)

View File

@ -142,7 +142,6 @@ class AuthUtils:
class BaseViewTestCase(TestCase):
def setUp(self):
self.member = AuthUtils.create_member('auth_member')
self.member.set_password('password')
self.member.email = 'auth_member@example.com'
self.member.save()
AuthUtils.add_main_character(self.member, 'auth_member', '12345', corp_id='111', corp_name='Test Corporation',
@ -150,4 +149,4 @@ class BaseViewTestCase(TestCase):
def login(self):
token = Token.objects.create(character_id='12345', character_name='auth_member', character_owner_hash='1', user=self.member, access_token='1')
self.client.login(token=token)
self.client.login(token=token)

View File

@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
from django.template import Library
from django.core.urlresolvers import resolve
from django.urls import resolve
from django.conf import settings
import re

View File

@ -1,9 +1,37 @@
import logging
import datetime
from django import forms
from django.utils import timezone
from django.core.validators import MaxValueValidator, MinValueValidator
from django.utils.translation import ugettext_lazy as _
from .models import Timer
logger = logging.getLogger(__name__)
class TimerForm(forms.ModelForm):
class Meta:
model = Timer
fields = ('details', 'system', 'planet_moon', 'structure', 'objective', 'important', 'corp_timer')
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
if 'instance' in kwargs and kwargs['instance'] is not None:
# Do conversion from db datetime to days/hours/minutes
# for appropriate fields
current_time = timezone.now()
td = kwargs['instance'].eve_time - current_time
initial = kwargs.pop('initial', dict())
if 'days_left' not in initial:
initial.update({'days_left': td.days})
if 'hours_left' not in initial:
initial.update({'hours_left': td.seconds // 3600})
if 'minutes_left' not in initial:
initial.update({'minutes_left': td.seconds // 60 % 60})
kwargs.update({'initial': initial})
super(TimerForm, self).__init__(*args, **kwargs)
class TimerForm(forms.Form):
structure_choices = [('POCO', 'POCO'), ('I-HUB', 'I-HUB'), ('POS[S]', 'POS[S]'),
('POS[M]', 'POS[M]'), ('POS[L]', 'POS[L]'), ('Citadel[M]', 'Citadel[M]'),
('Citadel[L]', 'Citadel[L]'), ('Citadel[XL]', 'Citadel[XL]'),
@ -25,3 +53,27 @@ class TimerForm(forms.Form):
validators=[MinValueValidator(0), MaxValueValidator(59)])
important = forms.BooleanField(label=_("Important"), required=False)
corp_timer = forms.BooleanField(label=_("Corp-Restricted"), required=False)
def save(self, commit=True):
timer = super(TimerForm, self).save(commit=False)
# Get character
character = self.user.profile.main_character
corporation = character.corporation
logger.debug("Determined timer save request on behalf "
"of character {} corporation {}".format(character, corporation))
# calculate future time
future_time = datetime.timedelta(days=self.cleaned_data['days_left'], hours=self.cleaned_data['hours_left'],
minutes=self.cleaned_data['minutes_left'])
current_time = timezone.now()
eve_time = current_time + future_time
logger.debug(
"Determined timer eve time is %s - current time %s, adding %s" % (eve_time, current_time, future_time))
timer.eve_time = eve_time
timer.eve_character = character
timer.eve_corp = corporation
timer.user = self.user
if commit:
timer.save()
return timer

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-09-28 02:16
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('timerboard', '0002_make_strings_more_stringy'),
]
operations = [
migrations.AlterField(
model_name='timer',
name='eve_character',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter'),
),
migrations.AlterField(
model_name='timer',
name='user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -16,10 +16,10 @@ class Timer(models.Model):
objective = models.CharField(max_length=254, default="")
eve_time = models.DateTimeField()
important = models.BooleanField(default=False)
eve_character = models.ForeignKey(EveCharacter)
eve_corp = models.ForeignKey(EveCorporationInfo)
eve_character = models.ForeignKey(EveCharacter, null=True, on_delete=models.SET_NULL)
eve_corp = models.ForeignKey(EveCorporationInfo, on_delete=models.CASCADE)
corp_timer = models.BooleanField(default=False)
user = models.ForeignKey(User)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
def __str__(self):
return str(self.system) + ' ' + str(self.objective)
return str(self.system) + ' ' + str(self.details)

View File

@ -1,29 +0,0 @@
{% extends "allianceauth/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% block title %}{% trans "Alliance Auth - Structure Timer Create" %}{% endblock %}
{% block page_title %}{% trans "Timer Create" %}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Create Structure Timer" %}</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Create Timer" %}</button>
</form>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,33 @@
{% extends "allianceauth/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block page_title %}
{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">
{% block page_header %}
{% endblock %}
{% include 'timerboard/index_button.html' %}
</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<form id="add-timer-form" class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">
{% block submit_button_text %}
{% endblock %}
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
<div class="text-right">
<a href="{% url 'timerboard:view' %}" class="btn btn-default">Back</a>
</div>

View File

@ -0,0 +1,26 @@
{% extends "allianceauth/base.html" %}
{% load i18n %}
{% block page_title %}
Delete Timer
{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">
{% trans "Delete Timer" %}
{% include 'timerboard/index_button.html' %}
</h1>
<div class="container-fluid">
<div class="col-md-2 col-md-offset-5">
<div class="row">
<form action="" method="post">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete timer "{{ object }}"?{% endblocktrans %}</p>
<input class="btn btn-danger btn-block" type="submit" value="Confirm" />
</form>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -0,0 +1,14 @@
{% extends "timerboard/form.html" %}
{% load i18n %}
{% block page_title %}
{% trans "Create Timer" %}
{% endblock page_title %}
{% block page_header %}
{% trans "Create Structure Timer" %}
{% endblock %}
{% block submit_button_text %}
{% trans "Create Timer" %}
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "timerboard/form.html" %}
{% load i18n %}
{% block page_title %}
{% trans "Update Structure Timer" %}
{% endblock page_title %}
{% block page_header %}
{% trans "Update Structure Timer" %}
{% endblock %}
{% block submit_button_text %}
{% trans "Update Structure Timer" %}
{% endblock %}

View File

@ -1,35 +0,0 @@
{% extends "allianceauth/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Alliance Auth - Update Structure Timer {% endblock %}
{% block page_title %}{% trans "Update AAR Link" %}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Update Structure Timer" %}</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
{% if no_fleet_id %}
<div class="alert alert-danger" role="alert">{% trans "Structure Timer Does Not Exist" %}</div>
{% else %}
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Update Structure Timer" %}
</button>
</form>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -3,8 +3,6 @@
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "Structure Timer Management" %}{% endblock page_title %}
{% block extra_css %}{% endblock extra_css %}
@ -145,7 +143,7 @@
<td class="text-center">{{ timer.eve_character.character_name }}</td>
{% if perms.auth.timer_management %}
<td class="text-center">
<a href="{% url 'timerboard:remove' timer.id %}" class="btn btn-danger">
<a href="{% url 'timerboard:delete' timer.id %}" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
<a href="{% url 'timerboard:edit' timer.id %}" class="btn btn-info">
@ -279,7 +277,7 @@
<td class="text-center">{{ timer.eve_character.character_name }}</td>
{% if perms.auth.timer_management %}
<td class="text-center">
<a href="{% url 'timerboard:remove' timer.id %}" class="btn btn-danger">
<a href="{% url 'timerboard:delete' timer.id %}" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
<a href="{% url 'timerboard:edit' timer.id %}" class="btn btn-info">
@ -415,7 +413,7 @@
<td class="text-center">{{ timer.eve_character.character_name }}</td>
{% if perms.auth.timer_management %}
<td class="text-center">
<a href="{% url 'timerboard:remove' timer.id %}" class="btn btn-danger">
<a href="{% url 'timerboard:delete' timer.id %}" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
<a href="{% url 'timerboard:edit' timer.id %}" class="btn btn-info">

232
allianceauth/timerboard/tests.py Executable file → Normal file
View File

@ -1 +1,231 @@
# Create your tests here.
from django_webtest import WebTest
from django.utils import timezone
from django.urls import reverse
from django.contrib.auth.models import Permission, User
from django.conf import settings
from datetime import timedelta
from allianceauth.tests.auth_utils import AuthUtils
from allianceauth.eveonline.models import EveCorporationInfo
from .models import Timer
from .form import TimerForm
class TimerboardViewsTestCase(WebTest):
csrf_checks = False
def setUp(self):
corp = EveCorporationInfo.objects.create(corporation_id='2345', corporation_name='test corp',
corporation_ticker='testc', member_count=24)
other_corp = EveCorporationInfo.objects.create(corporation_id='9345', corporation_name='other test corp',
corporation_ticker='testd', member_count=1)
self.user = AuthUtils.create_user('test_user')
AuthUtils.add_main_character(self.user, 'test character', '1234', '2345', 'test corp', 'testc')
self.user = User.objects.get_by_natural_key('test_user')
character = self.user.profile.main_character
self.other_user = AuthUtils.create_user('other_test_user')
AuthUtils.add_main_character(self.other_user, 'test character 2', '9234', '9345', 'other test corp', 'testd')
self.other_user = User.objects.get_by_natural_key('other_test_user')
other_character = self.other_user.profile.main_character
self.timer = Timer.objects.create(
details='details',
system='system',
planet_moon='planet_moon',
structure='structure',
objective='objective',
eve_time=timezone.now() + timedelta(days=30),
important=True,
corp_timer=False,
eve_character=character,
eve_corp=character.corporation,
user=self.user,
)
self.corp_timer = Timer.objects.create(
details='details',
system='system',
planet_moon='planet_moon',
structure='structure',
objective='objective',
eve_time=timezone.now() + timedelta(days=30),
important=False,
corp_timer=True,
eve_character=character,
eve_corp=character.corporation,
user=self.user,
)
self.other_corp_timer = Timer.objects.create(
details='details',
system='system',
planet_moon='planet_moon',
structure='structure',
objective='objective',
eve_time=timezone.now() + timedelta(days=30),
important=False,
corp_timer=True,
eve_character=other_character,
eve_corp=other_character.corporation,
user=self.user,
)
self.expired_timer = Timer.objects.create(
details='details',
system='system',
planet_moon='planet_moon',
structure='structure',
objective='objective',
eve_time=timezone.now() - timedelta(days=30),
important=True,
corp_timer=False,
eve_character=character,
eve_corp=character.corporation,
user=self.user,
)
self.view_permission = Permission.objects.get(codename='timer_view')
self.edit_permission = Permission.objects.get(codename='timer_management')
self.view_url = reverse('timerboard:view')
self.add_url = reverse('timerboard:add')
self.edit_url_name = 'timerboard:edit'
self.delete_url_name = 'timerboard:delete'
def test_timer_view(self):
self.user.user_permissions.add(self.view_permission)
self.app.set_user(self.user)
response = self.app.get(self.view_url)
context = response.context[-1]
timers = context['timers']
corp_timers = context['corp_timers']
future_timers = context['future_timers']
past_timers = context['past_timers']
self.assertTemplateUsed(response, 'timerboard/view.html')
self.assertIn(self.timer, timers)
self.assertIn(self.expired_timer, timers)
self.assertNotIn(self.corp_timer, timers)
self.assertNotIn(self.other_corp_timer, timers)
self.assertNotIn(self.timer, corp_timers)
self.assertNotIn(self.expired_timer, corp_timers)
self.assertIn(self.corp_timer, corp_timers)
self.assertNotIn(self.other_corp_timer, corp_timers)
self.assertIn(self.timer, future_timers)
self.assertNotIn(self.expired_timer, future_timers)
self.assertNotIn(self.corp_timer, future_timers)
self.assertNotIn(self.other_corp_timer, future_timers)
self.assertNotIn(self.timer, past_timers)
self.assertIn(self.expired_timer, past_timers)
self.assertNotIn(self.corp_timer, past_timers)
self.assertNotIn(self.other_corp_timer, past_timers)
def test_timer_view_permission(self):
self.client.force_login(self.user)
response = self.app.get(self.view_url)
self.assertRedirects(response, expected_url=reverse(settings.LOGIN_URL) + '?next=' + self.view_url)
def test_timer_view_login(self):
response = self.app.get(self.view_url)
self.assertRedirects(response, expected_url=reverse(settings.LOGIN_URL) + '?next=' + self.view_url)
def test_add_timer_get(self):
self.user.user_permissions.add(self.edit_permission)
self.app.set_user(self.user)
response = self.app.get(self.add_url)
self.assertTemplateUsed(response, 'timerboard/timer_create_form.html')
context = response.context[-1]
self.assertIs(TimerForm, type(context['form']))
def test_add_timer_form_error(self):
self.user.user_permissions.add(self.edit_permission)
self.app.set_user(self.user)
page = self.app.post(self.add_url)
page = page.forms['add-timer-form'].submit()
self.assertContains(page, "This field is required.")
def test_add_timer_post(self):
self.user.user_permissions.add(self.edit_permission)
self.user.user_permissions.add(self.view_permission)
self.app.set_user(self.user)
page = self.app.post(self.add_url)
form = page.forms['add-timer-form']
form['details'] = 'details'
form['system'] = 'jita'
form['planet_moon'] = '4-4'
form['structure'] = TimerForm.structure_choices[0][0]
form['objective'] = TimerForm.objective_choices[0][0]
form['days_left'] = 1
form['hours_left'] = 2
form['minutes_left'] = 3
form['important'] = True
form['corp_timer'] = False
response = form.submit()
self.assertRedirects(response, self.view_url)
self.assertTrue(Timer.objects.filter(system='jita', details='details').exists())
def test_edit_timer_get(self):
self.user.user_permissions.add(self.edit_permission)
self.app.set_user(self.user)
response = self.app.get(reverse(self.edit_url_name, args=[self.timer.id]))
context = response.context[-1]
form = context['form']
data = form.instance
self.assertTemplateUsed(response, 'timerboard/timer_update_form.html')
self.assertIs(TimerForm, type(form))
self.assertEqual(data, self.timer)
def test_edit_timer_post(self):
self.user.user_permissions.add(self.edit_permission)
self.user.user_permissions.add(self.view_permission)
self.app.set_user(self.user)
page = self.app.post(reverse(self.edit_url_name, args=[self.timer.id]))
form = page.forms['add-timer-form']
form['details'] = 'detailsUNIQUE'
form['system'] = 'jita'
form['planet_moon'] = '4-4'
form['structure'] = TimerForm.structure_choices[0][0]
form['objective'] = TimerForm.objective_choices[0][0]
form['days_left'] = 1
form['hours_left'] = 2
form['minutes_left'] = 3
form['important'] = True
form['corp_timer'] = False
response = form.submit()
self.assertRedirects(response, self.view_url)
self.assertTrue(Timer.objects.filter(system='jita', details='detailsUNIQUE').exists())
def test_delete_timer_get(self):
self.user.user_permissions.add(self.edit_permission)
self.app.set_user(self.user)
response = self.app.get(reverse(self.delete_url_name, args=[self.timer.id]))
self.assertTemplateUsed(response, 'timerboard/timer_confirm_delete.html')
self.assertContains(response, 'Are you sure you want to delete timer "'+str(self.timer))

View File

@ -5,8 +5,8 @@ from . import views
app_name = 'timerboard'
urlpatterns = [
url(r'^$', views.timer_view, name='view'),
url(r'^add/$', views.add_timer_view, name='add'),
url(r'^remove/(\w+)$', views.remove_timer, name='remove'),
url(r'^edit/(\w+)$', views.edit_timer, name='edit'),
]
url(r'^$', views.TimerView.as_view(), name='view'),
url(r'^add/$', views.AddTimerView.as_view(), name='add'),
url(r'^remove/(?P<pk>\w+)$', views.RemoveTimerView.as_view(), name='delete'),
url(r'^edit/(?P<pk>\w+)$', views.EditTimerView.as_view(), name='edit'),
]

View File

@ -2,13 +2,14 @@ import datetime
import logging
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.shortcuts import get_object_or_404
from django.shortcuts import render, redirect
from django.views import View
from django.urls import reverse_lazy
from django.views.generic import CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from allianceauth.eveonline.models import EveCorporationInfo
from .form import TimerForm
from .models import Timer
@ -16,136 +17,72 @@ from .models import Timer
logger = logging.getLogger(__name__)
@login_required
@permission_required('auth.timer_view')
def timer_view(request):
logger.debug("timer_view called by user %s" % request.user)
char = request.user.profile.main_character
if char:
corp = EveCorporationInfo.objects.get(corporation_id=char.corporation_id)
else:
corp = None
if corp:
corp_timers = Timer.objects.all().filter(corp_timer=True).filter(eve_corp=corp)
else:
corp_timers = []
render_items = {'timers': Timer.objects.all().filter(corp_timer=False),
'corp_timers': corp_timers,
'future_timers': Timer.objects.all().filter(corp_timer=False).filter(
eve_time__gte=datetime.datetime.now()),
'past_timers': Timer.objects.all().filter(corp_timer=False).filter(
eve_time__lt=datetime.datetime.now())}
return render(request, 'timerboard/management.html', context=render_items)
class BaseTimerView(LoginRequiredMixin, PermissionRequiredMixin, View):
pass
@login_required
@permission_required('auth.timer_management')
def add_timer_view(request):
logger.debug("add_timer_view called by user %s" % request.user)
if request.method == 'POST':
form = TimerForm(request.POST)
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
if form.is_valid():
# Get character
character = request.user.profile.main_character
corporation = EveCorporationInfo.get_corporation_info_by_id(character.corporation_id)
logger.debug(
"Determined timer add request on behalf of character %s corporation %s" % (character, corporation))
# calculate future time
future_time = datetime.timedelta(days=form.cleaned_data['days_left'], hours=form.cleaned_data['hours_left'],
minutes=form.cleaned_data['minutes_left'])
current_time = timezone.now()
eve_time = current_time + future_time
logger.debug(
"Determined timer eve time is %s - current time %s, adding %s" % (eve_time, current_time, future_time))
# handle valid form
timer = Timer()
timer.details = form.cleaned_data['details']
timer.system = form.cleaned_data['system']
timer.planet_moon = form.cleaned_data['planet_moon']
timer.structure = form.cleaned_data['structure']
timer.objective = form.cleaned_data['objective']
timer.eve_time = eve_time
timer.important = form.cleaned_data['important']
timer.corp_timer = form.cleaned_data['corp_timer']
timer.eve_character = character
timer.eve_corp = corporation
timer.user = request.user
timer.save()
logger.info("Created new timer in %s at %s by user %s" % (timer.system, timer.eve_time, request.user))
messages.success(request, _('Added new timer in %(system)s at %(time)s.') % {"system": timer.system, "time": timer.eve_time})
return redirect("timerboard:view")
else:
logger.debug("Returning new TimerForm")
form = TimerForm()
class TimerView(BaseTimerView):
template_name = 'timerboard/view.html'
permission_required = 'auth.timer_view'
render_items = {'form': form}
def get(self, request):
logger.debug("timer_view called by user {}".format(request.user))
char = request.user.profile.main_character
if char:
corp = char.corporation
else:
corp = None
return render(request, 'timerboard/add.html', context=render_items)
@login_required
@permission_required('auth.timer_management')
def remove_timer(request, timer_id):
logger.debug("remove_timer called by user %s for timer id %s" % (request.user, timer_id))
timer = get_object_or_404(Timer, id=timer_id)
timer.delete()
logger.debug("Deleting timer id %s by user %s" % (timer_id, request.user))
messages.success(request, _('Deleted timer in %(system)s at %(time)s.') % {'system': timer.system,
'time': timer.eve_time})
return redirect("timerboard:view")
@login_required
@permission_required('auth.timer_management')
def edit_timer(request, timer_id):
logger.debug("edit_timer called by user %s for timer id %s" % (request.user, timer_id))
timer = get_object_or_404(Timer, id=timer_id)
if request.method == 'POST':
form = TimerForm(request.POST)
logger.debug("Received POST request containing updated timer form, is valid: %s" % form.is_valid())
if form.is_valid():
character = request.user.profile.main_character
corporation = EveCorporationInfo.get_corporation_info_by_id(character.corporation_id)
logger.debug(
"Determined timer edit request on behalf of character %s corporation %s" % (character, corporation))
# calculate future time
future_time = datetime.timedelta(days=form.cleaned_data['days_left'], hours=form.cleaned_data['hours_left'],
minutes=form.cleaned_data['minutes_left'])
current_time = datetime.datetime.utcnow()
eve_time = current_time + future_time
logger.debug(
"Determined timer eve time is %s - current time %s, adding %s" % (eve_time, current_time, future_time))
timer.details = form.cleaned_data['details']
timer.system = form.cleaned_data['system']
timer.planet_moon = form.cleaned_data['planet_moon']
timer.structure = form.cleaned_data['structure']
timer.objective = form.cleaned_data['objective']
timer.eve_time = eve_time
timer.important = form.cleaned_data['important']
timer.corp_timer = form.cleaned_data['corp_timer']
timer.eve_character = character
timer.eve_corp = corporation
logger.info("User %s updating timer id %s " % (request.user, timer_id))
messages.success(request, _('Saved changes to the timer.'))
timer.save()
return redirect("timerboard:view")
else:
current_time = timezone.now()
td = timer.eve_time - current_time
tddays, tdhours, tdminutes = td.days, td.seconds // 3600, td.seconds // 60 % 60
data = {
'details': timer.details,
'system': timer.system,
'planet_moon': timer.planet_moon,
'structure': timer.structure,
'objective': timer.objective,
'important': timer.important,
'corp_timer': timer.corp_timer,
'days_left': tddays,
'hours_left': tdhours,
'minutes_left': tdminutes,
render_items = {
'timers': Timer.objects.filter(corp_timer=False),
'corp_timers': Timer.objects.filter(corp_timer=True, eve_corp=corp),
'future_timers': Timer.objects.filter(corp_timer=False, eve_time__gte=timezone.now()),
'past_timers': Timer.objects.filter(corp_timer=False, eve_time__lt=timezone.now()),
}
form = TimerForm(initial=data)
return render(request, 'timerboard/update.html', context={'form': form})
return render(request, self.template_name, context=render_items)
class TimerManagementView(BaseTimerView):
permission_required = 'auth.timer_management'
index_redirect = 'timerboard:view'
success_url = reverse_lazy(index_redirect)
model = Timer
form_class = TimerForm
def get_timer(self, timer_id):
return get_object_or_404(self.model, id=timer_id)
class AddUpdateMixin:
def get_form_kwargs(self):
"""
Inject the request user into the kwargs passed to the form
"""
kwargs = super(AddUpdateMixin, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
class AddTimerView(TimerManagementView, AddUpdateMixin, CreateView):
template_name_suffix = '_create_form'
def form_valid(self, form):
result = super(AddTimerView, self).form_valid(form)
timer = self.object
logger.info("Created new timer in {} at {} by user {}".format(timer.system, timer.eve_time, self.request.user))
messages.success(self.request, _('Added new timer in %(system)s at %(time)s.') % {"system": timer.system,
"time": timer.eve_time})
return result
class EditTimerView(TimerManagementView, AddUpdateMixin, UpdateView):
template_name_suffix = '_update_form'
def form_valid(self, form):
messages.success(self.request, _('Saved changes to the timer.'))
return super(EditTimerView, self).form_valid(form)
class RemoveTimerView(TimerManagementView, DeleteView):
pass

View File

@ -23,15 +23,15 @@ urlpatterns = [
url(r'^i18n/', include('django.conf.urls.i18n')),
# Authentication
url(r'', include(allianceauth.authentication.urls, namespace='authentication')),
url(r'', include(allianceauth.authentication.urls)),
url(r'^account/login/$', TemplateView.as_view(template_name='public/login.html'), name='auth_login_user'),
url(r'account/', include(hmac_urls)),
# Admin urls
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls),
# SSO
url(r'^sso/', include(esi.urls, namespace='esi')),
url(r'^sso/', include((esi.urls, 'esi'), namespace='esi')),
url(r'^sso/login$', allianceauth.authentication.views.sso_login, name='auth_sso_login'),
# Notifications

View File

@ -16,7 +16,6 @@ install_requires = [
'django>=1.11',
'django-bootstrap-form',
'django-bootstrap-pagination',
'django-registration',
'django-sortedm2m',
'django-redis-cache>=1.7.1',
@ -33,6 +32,7 @@ testing_extras = [
'coverage>=4.3.1',
'requests-mock>=1.2.0',
'django-nose',
'django-webtest',
]
setup(

View File

@ -14,5 +14,7 @@ basepython =
deps=
dj111: Django>=1.11.1,<2.0
dj20: Django>=2.0a1
dj20: https://github.com/celery/django-celery-beat/zipball/master#egg=django-celery-beat
dj20: https://github.com/Adarnof/adarnauth-esi/zipball/master#egg=adarnauth-esi
install_command = pip install -e ".[testing]" -U {opts} {packages}
commands=coverage run runtests.py -v 2