mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-06 15:16:20 +01:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a02e5f400a | ||
|
|
65c168939d | ||
|
|
313cac6ac7 | ||
|
|
0145ea82c8 | ||
|
|
0cdc5ffbd5 | ||
|
|
0bdd044378 | ||
|
|
ad266ea2ee | ||
|
|
7ea8c9e50d | ||
|
|
9a015fd582 | ||
|
|
7ca1c87c87 | ||
|
|
eee6a9132d | ||
|
|
9d90af4a3d | ||
|
|
72305de2d8 | ||
|
|
8f58f76001 | ||
|
|
a969b6117b | ||
|
|
97762119b3 |
22
.idea/allianceauth.iml
generated
22
.idea/allianceauth.iml
generated
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="FacetManager">
|
|
||||||
<facet type="django" name="Django">
|
|
||||||
<configuration>
|
|
||||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
|
||||||
<option name="settingsModule" value="alliance_auth/settings.py.example" />
|
|
||||||
<option name="manageScript" value="manage.py" />
|
|
||||||
<option name="environment" value="<map/>" />
|
|
||||||
<option name="commandsToSkip" value="" />
|
|
||||||
</configuration>
|
|
||||||
</facet>
|
|
||||||
</component>
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="jdk" jdkName="Python 2.7.11 virtualenv at ~/1.6" jdkType="Python SDK" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
<component name="TemplatesService">
|
|
||||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
12
.idea/dataSources.ids
generated
12
.idea/dataSources.ids
generated
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component name="dataSourceStorage">
|
|
||||||
<data-source source="LOCAL" name="Django default" uuid="3eb61453-647a-4832-8320-f3561f039abc">
|
|
||||||
<database-info product="" version="" jdbc-version="" driver-name="" driver-version=""/>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="Django phpbb3" uuid="2de247c2-1951-4e74-8276-6a1c89c396fa">
|
|
||||||
<database-info product="" version="" jdbc-version="" driver-name="" driver-version=""/>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="Django mumble" uuid="9963e5ca-7f2f-4dd3-9175-bc7102dfd48c">
|
|
||||||
<database-info product="" version="" jdbc-version="" driver-name="" driver-version=""/>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
20
.idea/dataSources.xml
generated
20
.idea/dataSources.xml
generated
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
|
||||||
<data-source source="LOCAL" name="Django default" uuid="3eb61453-647a-4832-8320-f3561f039abc">
|
|
||||||
<driver-ref>mysql</driver-ref>
|
|
||||||
<jdbc-driver>com.mysql.jdbc.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:mysql://127.0.0.1:3306/alliance_auth</jdbc-url>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="Django phpbb3" uuid="2de247c2-1951-4e74-8276-6a1c89c396fa">
|
|
||||||
<driver-ref>mysql</driver-ref>
|
|
||||||
<jdbc-driver>com.mysql.jdbc.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:mysql://127.0.0.1:3306/alliance_forum</jdbc-url>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="Django mumble" uuid="9963e5ca-7f2f-4dd3-9175-bc7102dfd48c">
|
|
||||||
<driver-ref>mysql</driver-ref>
|
|
||||||
<jdbc-driver>com.mysql.jdbc.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:mysql://127.0.0.1:3306/alliance_mumble</jdbc-url>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
5
.idea/encodings.xml
generated
5
.idea/encodings.xml
generated
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
|
||||||
</project>
|
|
||||||
|
|
||||||
11
.idea/inspectionProfiles/Project_Default.xml
generated
11
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,11 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0" is_locked="false">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<option name="myLocal" value="false" />
|
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
|
||||||
<option name="processCode" value="true" />
|
|
||||||
<option name="processLiterals" value="true" />
|
|
||||||
<option name="processComments" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
7
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="PROJECT_PROFILE" />
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.11 virtualenv at ~/1.6" project-jdk-type="Python SDK" />
|
|
||||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
|
||||||
<option name="version" value="1" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
9
.idea/modules.xml
generated
9
.idea/modules.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/allianceauth.iml" filepath="$PROJECT_DIR$/.idea/allianceauth.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
||||||
5
.idea/scopes/scope_settings.xml
generated
5
.idea/scopes/scope_settings.xml
generated
@@ -1,5 +0,0 @@
|
|||||||
<component name="DependencyValidationManager">
|
|
||||||
<state>
|
|
||||||
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
||||||
7
.idea/vcs.xml
generated
7
.idea/vcs.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
||||||
@@ -4,5 +4,5 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
# Django starts so that shared_task will use this app.
|
# Django starts so that shared_task will use this app.
|
||||||
from .celeryapp import app as celery_app # noqa
|
from .celeryapp import app as celery_app # noqa
|
||||||
|
|
||||||
__version__ = '1.15.4'
|
__version__ = '1.15.5'
|
||||||
NAME = 'Alliance Auth v%s' % __version__
|
NAME = 'Alliance Auth v%s' % __version__
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ from corputils.managers import CorpStatsManager
|
|||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -41,7 +45,7 @@ class CorpStats(models.Model):
|
|||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
try:
|
try:
|
||||||
c = self.token.get_esi_client(Character='v4', Corporation='v2')
|
c = self.token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
||||||
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
|
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
|
||||||
'corporation_id'] == int(self.corp.corporation_id)
|
'corporation_id'] == int(self.corp.corporation_id)
|
||||||
members = c.Corporation.get_corporations_corporation_id_members(
|
members = c.Corporation.get_corporations_corporation_id_members(
|
||||||
@@ -52,7 +56,6 @@ class CorpStats(models.Model):
|
|||||||
# the swagger spec doesn't have a maxItems count
|
# the swagger spec doesn't have a maxItems count
|
||||||
# manual testing says we can do over 350, but let's not risk it
|
# manual testing says we can do over 350, but let's not risk it
|
||||||
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
|
||||||
c = self.token.get_esi_client(Character='v1') # ccplease bump versions of whole resources
|
|
||||||
member_name_chunks = [c.Character.get_characters_names(character_ids=id_chunk).result() for id_chunk in
|
member_name_chunks = [c.Character.get_characters_names(character_ids=id_chunk).result() for id_chunk in
|
||||||
member_id_chunks]
|
member_id_chunks]
|
||||||
member_list = {}
|
member_list = {}
|
||||||
|
|||||||
1
corputils/swagger.json
Normal file
1
corputils/swagger.json
Normal file
File diff suppressed because one or more lines are too long
@@ -12,6 +12,9 @@ from eveonline.models import EveCharacter, EveCorporationInfo
|
|||||||
from corputils.models import CorpStats
|
from corputils.models import CorpStats
|
||||||
from esi.decorators import token_required
|
from esi.decorators import token_required
|
||||||
from bravado.exception import HTTPError
|
from bravado.exception import HTTPError
|
||||||
|
import os
|
||||||
|
|
||||||
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
|
|
||||||
MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20))
|
MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20))
|
||||||
|
|
||||||
@@ -41,9 +44,8 @@ def corpstats_add(request, token):
|
|||||||
if EveCharacter.objects.filter(character_id=token.character_id).exists():
|
if EveCharacter.objects.filter(character_id=token.character_id).exists():
|
||||||
corp_id = EveCharacter.objects.get(character_id=token.character_id).corporation_id
|
corp_id = EveCharacter.objects.get(character_id=token.character_id).corporation_id
|
||||||
else:
|
else:
|
||||||
corp_id = \
|
corp_id = token.get_esi_client(spec_file=SWAGGER_SPEC_PATH).Character.get_characters_character_id(
|
||||||
token.get_esi_client(Character='v4').Character.get_characters_character_id(character_id=token.character_id).result()[
|
character_id=token.character_id).result()['corporation_id']
|
||||||
'corporation_id']
|
|
||||||
corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
|
corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
|
||||||
cs = CorpStats.objects.create(token=token, corp=corp)
|
cs = CorpStats.objects.create(token=token, corp=corp)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import json
|
|||||||
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
|
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
|
||||||
import evelink
|
import evelink
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -228,7 +231,7 @@ class EveProvider(object):
|
|||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class EveSwaggerProvider(EveProvider):
|
class EveSwaggerProvider(EveProvider):
|
||||||
def __init__(self, token=None, adapter=None):
|
def __init__(self, token=None, adapter=None):
|
||||||
self.client = esi_client_factory(token=token, Alliance='v3', Character='v4', Corporation='v2', Universe='v2')
|
self.client = esi_client_factory(token=token, spec_file=SWAGGER_SPEC_PATH)
|
||||||
self.adapter = adapter or self
|
self.adapter = adapter or self
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -244,7 +247,7 @@ class EveSwaggerProvider(EveProvider):
|
|||||||
data['alliance_name'],
|
data['alliance_name'],
|
||||||
data['ticker'],
|
data['ticker'],
|
||||||
corps,
|
corps,
|
||||||
data['executor_corporation_id'],
|
data['executor_corp'],
|
||||||
)
|
)
|
||||||
return model
|
return model
|
||||||
except HTTPNotFound:
|
except HTTPNotFound:
|
||||||
|
|||||||
1
eveonline/swagger.json
Normal file
1
eveonline/swagger.json
Normal file
File diff suppressed because one or more lines are too long
@@ -36,7 +36,7 @@ def refresh_api(api):
|
|||||||
EveManager.create_character_obj(c, api.user, api.api_id)
|
EveManager.create_character_obj(c, api.user, api.api_id)
|
||||||
current_chars = EveCharacter.objects.filter(api_id=api.api_id)
|
current_chars = EveCharacter.objects.filter(api_id=api.api_id)
|
||||||
for c in current_chars:
|
for c in current_chars:
|
||||||
if not int(c.character_id) in [c.id for c in characters]:
|
if not int(c.character_id) in [d.id for d in characters]:
|
||||||
logger.info("Character %s no longer found on API ID %s" % (c, api.api_id))
|
logger.info("Character %s no longer found on API ID %s" % (c, api.api_id))
|
||||||
c.delete()
|
c.delete()
|
||||||
except evelink.api.APIError as e:
|
except evelink.api.APIError as e:
|
||||||
@@ -60,7 +60,7 @@ def refresh_api(api):
|
|||||||
api.api_id, e.required_mask, e.api_mask), level="danger")
|
api.api_id, e.required_mask, e.api_mask), level="danger")
|
||||||
still_valid = False
|
still_valid = False
|
||||||
except EveApiManager.ApiServerUnreachableError as e:
|
except EveApiManager.ApiServerUnreachableError as e:
|
||||||
logger.warn("Error updating API %s\n%s" % (api.api_id, str(e)))
|
logger.warning("Error updating API %s\n%s" % (api.api_id, str(e)))
|
||||||
finally:
|
finally:
|
||||||
if not still_valid:
|
if not still_valid:
|
||||||
EveManager.delete_characters_by_api_id(api.api_id, api.user.id)
|
EveManager.delete_characters_by_api_id(api.api_id, api.user.id)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# Create your tests here.
|
|
||||||
0
eveonline/tests/__init__.py
Normal file
0
eveonline/tests/__init__.py
Normal file
125
eveonline/tests/test_tasks.py
Normal file
125
eveonline/tests/test_tasks.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Py3
|
||||||
|
from unittest import mock
|
||||||
|
except ImportError:
|
||||||
|
# Py2
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from alliance_auth.tests.auth_utils import AuthUtils
|
||||||
|
from eveonline.providers import Character, Alliance, Corporation
|
||||||
|
from eveonline.managers import EveManager
|
||||||
|
from eveonline import tasks
|
||||||
|
from eveonline.models import EveApiKeyPair, EveCharacter
|
||||||
|
from services.managers.eve_api_manager import EveApiManager
|
||||||
|
|
||||||
|
MODULE_PATH = 'eveonline.tasks'
|
||||||
|
|
||||||
|
|
||||||
|
class EveOnlineTasksTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = AuthUtils.create_member('joebloggs')
|
||||||
|
self.api_key = EveApiKeyPair.objects.create(api_id='0118999',
|
||||||
|
api_key='hunter2',
|
||||||
|
user=self.user,
|
||||||
|
sso_verified=True)
|
||||||
|
|
||||||
|
@mock.patch(MODULE_PATH + '.EveApiManager.validate_api')
|
||||||
|
@mock.patch(MODULE_PATH + '.EveManager.get_characters_from_api')
|
||||||
|
def test_refresh_api_characters(self, get_characters_from_api, validate_api):
|
||||||
|
# Arrange
|
||||||
|
provider = mock.MagicMock()
|
||||||
|
|
||||||
|
provider.get_alliance.return_value = Alliance(provider, 22222, 'Test Alliance', 'TEST', [11111], 11111)
|
||||||
|
provider.get_corp.return_value = Corporation(provider, 11111, 'Test Corp', 'HERP', 12345, [12345, 23456], 22222)
|
||||||
|
|
||||||
|
mock_api_data = [
|
||||||
|
Character(provider, 12345, 'testchar1', 11111, 22222),
|
||||||
|
Character(provider, 23456, 'Will beAdded', 11111, 22222)
|
||||||
|
]
|
||||||
|
|
||||||
|
get_characters_from_api.return_value = mock_api_data
|
||||||
|
validate_api.return_value = True
|
||||||
|
|
||||||
|
EveManager.create_character_obj(mock_api_data[0], self.user, '0118999')
|
||||||
|
EveManager.create_character_obj(Character(provider, 34567, 'deletedcharacter', 11111, 22222),
|
||||||
|
self.user, '0118999')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
tasks.refresh_api(self.api_key)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertTrue(EveCharacter.objects.filter(character_id='12345').exists())
|
||||||
|
self.assertTrue(EveCharacter.objects.filter(character_id='23456').exists())
|
||||||
|
self.assertFalse(EveCharacter.objects.filter(character_id='34567').exists())
|
||||||
|
|
||||||
|
args, kwargs = validate_api.call_args
|
||||||
|
self.assertEqual(args[0], self.api_key.api_id)
|
||||||
|
self.assertEqual(args[1], self.api_key.api_key)
|
||||||
|
self.assertEqual(args[2], self.api_key.user)
|
||||||
|
|
||||||
|
@mock.patch(MODULE_PATH + '.EveApiManager.validate_api')
|
||||||
|
@mock.patch(MODULE_PATH + '.EveManager')
|
||||||
|
def test_refresh_api_evelink_exception(self, evemanager, validate_api):
|
||||||
|
import evelink
|
||||||
|
|
||||||
|
validate_api.side_effect = evelink.api.APIError()
|
||||||
|
|
||||||
|
tasks.refresh_api(self.api_key)
|
||||||
|
|
||||||
|
self.assertTrue(validate_api.called)
|
||||||
|
self.assertFalse(evemanager.get_characters_from_api.called)
|
||||||
|
self.assertFalse(evemanager.delete_characters_by_api_id.called)
|
||||||
|
self.assertFalse(evemanager.delete_api_key_pair.called)
|
||||||
|
|
||||||
|
@mock.patch(MODULE_PATH + '.EveApiManager.validate_api')
|
||||||
|
@mock.patch(MODULE_PATH + '.EveManager')
|
||||||
|
def test_refresh_api_invalid(self, evemanager, validate_api):
|
||||||
|
validate_api.side_effect = EveApiManager.ApiInvalidError(self.api_key.api_id)
|
||||||
|
|
||||||
|
tasks.refresh_api(self.api_key)
|
||||||
|
|
||||||
|
self.assertTrue(validate_api.called)
|
||||||
|
self.assertFalse(evemanager.get_characters_from_api.called)
|
||||||
|
self.assertTrue(evemanager.delete_characters_by_api_id.called)
|
||||||
|
self.assertTrue(evemanager.delete_api_key_pair.called)
|
||||||
|
|
||||||
|
@mock.patch(MODULE_PATH + '.EveApiManager.validate_api')
|
||||||
|
@mock.patch(MODULE_PATH + '.EveManager')
|
||||||
|
def test_refresh_api_accountvalidationerror(self, evemanager, validate_api):
|
||||||
|
validate_api.side_effect = EveApiManager.ApiAccountValidationError(self.api_key.api_id)
|
||||||
|
|
||||||
|
tasks.refresh_api(self.api_key)
|
||||||
|
|
||||||
|
self.assertTrue(validate_api.called)
|
||||||
|
self.assertFalse(evemanager.get_characters_from_api.called)
|
||||||
|
self.assertTrue(evemanager.delete_characters_by_api_id.called)
|
||||||
|
self.assertTrue(evemanager.delete_api_key_pair.called)
|
||||||
|
|
||||||
|
@mock.patch(MODULE_PATH + '.EveApiManager.validate_api')
|
||||||
|
@mock.patch(MODULE_PATH + '.EveManager')
|
||||||
|
def test_refresh_api_maskvalidationerror(self, evemanager, validate_api):
|
||||||
|
validate_api.side_effect = EveApiManager.ApiMaskValidationError('12345', '1111', self.api_key.api_id)
|
||||||
|
|
||||||
|
tasks.refresh_api(self.api_key)
|
||||||
|
|
||||||
|
self.assertTrue(validate_api.called)
|
||||||
|
self.assertFalse(evemanager.get_characters_from_api.called)
|
||||||
|
self.assertTrue(evemanager.delete_characters_by_api_id.called)
|
||||||
|
self.assertTrue(evemanager.delete_api_key_pair.called)
|
||||||
|
|
||||||
|
@mock.patch(MODULE_PATH + '.EveApiManager.validate_api')
|
||||||
|
@mock.patch(MODULE_PATH + '.EveManager')
|
||||||
|
def test_refresh_api_invalid(self, evemanager, validate_api):
|
||||||
|
validate_api.side_effect = EveApiManager.ApiServerUnreachableError(self.api_key.api_id)
|
||||||
|
|
||||||
|
tasks.refresh_api(self.api_key)
|
||||||
|
|
||||||
|
self.assertTrue(validate_api.called)
|
||||||
|
self.assertFalse(evemanager.get_characters_from_api.called)
|
||||||
|
# Lets hope we never see that again
|
||||||
|
self.assertFalse(evemanager.delete_characters_by_api_id.called)
|
||||||
|
self.assertFalse(evemanager.delete_api_key_pair.called)
|
||||||
@@ -36,5 +36,5 @@ class Fat(models.Model):
|
|||||||
unique_together = (('character', 'fatlink'),)
|
unique_together = (('character', 'fatlink'),)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
output = "Fat-link for %s" % self.character.character_name
|
return "Fat-link for %s" % self.character.character_name
|
||||||
return output.encode('utf-8')
|
|
||||||
|
|||||||
1
fleetactivitytracking/swagger.json
Normal file
1
fleetactivitytracking/swagger.json
Normal file
File diff suppressed because one or more lines are too long
@@ -37,7 +37,7 @@
|
|||||||
<td class="text-center">{{ fat.user }}</td>
|
<td class="text-center">{{ fat.user }}</td>
|
||||||
<td class="text-center">{{ fat.character.character_name }}</td>
|
<td class="text-center">{{ fat.character.character_name }}</td>
|
||||||
{% if fat.station != "No Station" %}
|
{% if fat.station != "No Station" %}
|
||||||
<td class="text-center">{% blocktrans %}Docked in {{ fat.system }}{% endblocktrans %}</td>
|
<td class="text-center">{% blocktrans %}Docked in {% endblocktrans %}{{ fat.system }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="text-center">{{ fat.system }}</td>
|
<td class="text-center">{{ fat.system }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<td class="text-center">{{ fat.fatlink.name }}</td>
|
<td class="text-center">{{ fat.fatlink.name }}</td>
|
||||||
<td class="text-center">{{ fat.character.character_name }}</td>
|
<td class="text-center">{{ fat.character.character_name }}</td>
|
||||||
{% if fat.station != "No Station" %}
|
{% if fat.station != "No Station" %}
|
||||||
<td class="text-center">{% blocktrans %}Docked in {{ fat.system }}{% endblocktrans %}</td>
|
<td class="text-center">{% blocktrans %}Docked in {% endblocktrans %}{{ fat.system }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="text-center">{{ fat.system }}</td>
|
<td class="text-center">{{ fat.system }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -16,16 +16,15 @@ from eveonline.managers import EveManager
|
|||||||
from authentication.models import AuthServicesInfo
|
from authentication.models import AuthServicesInfo
|
||||||
from fleetactivitytracking.forms import FatlinkForm
|
from fleetactivitytracking.forms import FatlinkForm
|
||||||
from fleetactivitytracking.models import Fatlink, Fat
|
from fleetactivitytracking.models import Fatlink, Fat
|
||||||
|
|
||||||
from esi.decorators import token_required
|
from esi.decorators import token_required
|
||||||
|
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -53,8 +52,12 @@ class CorpStat(object):
|
|||||||
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
|
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
|
||||||
self.blue = self.corp.is_blue
|
self.blue = self.corp.is_blue
|
||||||
|
|
||||||
|
@property
|
||||||
def avg_fat(self):
|
def avg_fat(self):
|
||||||
return "%.2f" % (float(self.n_fats) / float(self.corp.member_count))
|
try:
|
||||||
|
return "%.2f" % (float(self.n_fats) / float(self.corp.member_count))
|
||||||
|
except ZeroDivisionError:
|
||||||
|
return "%.2f" % 0
|
||||||
|
|
||||||
|
|
||||||
class MemberStat(object):
|
class MemberStat(object):
|
||||||
@@ -70,9 +73,13 @@ class MemberStat(object):
|
|||||||
self.n_chars = nchars
|
self.n_chars = nchars
|
||||||
self.n_fats = Fat.objects.filter(user_id=member['user_id']).filter(
|
self.n_fats = Fat.objects.filter(user_id=member['user_id']).filter(
|
||||||
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
|
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
|
||||||
|
|
||||||
|
@property
|
||||||
def avg_fat(self):
|
def avg_fat(self):
|
||||||
return "%.2f" % (float(self.n_fats) / float(self.n_chars))
|
try:
|
||||||
|
return "%.2f" % (float(self.n_fats) / float(self.n_chars))
|
||||||
|
except ZeroDivisionError:
|
||||||
|
return "%.2f" % 0
|
||||||
|
|
||||||
|
|
||||||
def first_day_of_next_month(year, month):
|
def first_day_of_next_month(year, month):
|
||||||
@@ -133,7 +140,7 @@ def fatlink_statistics_corp_view(request, corpid, year=None, month=None):
|
|||||||
# collect and sort stats
|
# collect and sort stats
|
||||||
stat_list = [fat_stats[x] for x in fat_stats]
|
stat_list = [fat_stats[x] for x in fat_stats]
|
||||||
stat_list.sort(key=lambda stat: stat.mainchar.character_name)
|
stat_list.sort(key=lambda stat: stat.mainchar.character_name)
|
||||||
stat_list.sort(key=lambda stat: (stat.n_fats, stat.n_fats / stat.n_chars), reverse=True)
|
stat_list.sort(key=lambda stat: (stat.n_fats, stat.avg_fat), reverse=True)
|
||||||
|
|
||||||
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
|
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
|
||||||
'previous_month': start_of_previous_month, 'corpid': corpid}
|
'previous_month': start_of_previous_month, 'corpid': corpid}
|
||||||
@@ -171,7 +178,7 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date
|
|||||||
# collect and sort stats
|
# collect and sort stats
|
||||||
stat_list = [fat_stats[x] for x in fat_stats]
|
stat_list = [fat_stats[x] for x in fat_stats]
|
||||||
stat_list.sort(key=lambda stat: stat.corp.corporation_name)
|
stat_list.sort(key=lambda stat: stat.corp.corporation_name)
|
||||||
stat_list.sort(key=lambda stat: (stat.n_fats, stat.n_fats / stat.corp.member_count), reverse=True)
|
stat_list.sort(key=lambda stat: (stat.n_fats, stat.avg_fat), reverse=True)
|
||||||
|
|
||||||
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
|
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
|
||||||
'previous_month': start_of_previous_month}
|
'previous_month': start_of_previous_month}
|
||||||
@@ -256,7 +263,7 @@ def click_fatlink_view(request, token, hash, fatname):
|
|||||||
|
|
||||||
if character:
|
if character:
|
||||||
# get data
|
# get data
|
||||||
c = token.get_esi_client(Location='v1', Universe='v2')
|
c = token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
|
||||||
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
|
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
|
||||||
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
|
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
|
||||||
location['solar_system_name'] = \
|
location['solar_system_name'] = \
|
||||||
@@ -266,7 +273,6 @@ def click_fatlink_view(request, token, hash, fatname):
|
|||||||
location['station_name'] = \
|
location['station_name'] = \
|
||||||
c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()['name']
|
c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()['name']
|
||||||
elif location['structure_id']:
|
elif location['structure_id']:
|
||||||
c = token.get_esi_client(Universe='v1')
|
|
||||||
location['station_name'] = \
|
location['station_name'] = \
|
||||||
c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()[
|
c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()[
|
||||||
'name']
|
'name']
|
||||||
|
|||||||
@@ -21,5 +21,4 @@ class optimer(models.Model):
|
|||||||
eve_character = models.ForeignKey(EveCharacter)
|
eve_character = models.ForeignKey(EveCharacter)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
output = self.operation_name
|
return self.operation_name
|
||||||
return output.encode('utf-8')
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ dnspython
|
|||||||
passlib
|
passlib
|
||||||
requests>=2.9.1
|
requests>=2.9.1
|
||||||
bcrypt
|
bcrypt
|
||||||
slugify
|
python-slugify>=1.2
|
||||||
requests-oauthlib
|
requests-oauthlib
|
||||||
sleekxmpp
|
sleekxmpp
|
||||||
redis
|
redis
|
||||||
@@ -23,4 +23,4 @@ django-celery-beat
|
|||||||
# awating pyghassen/openfire-restapi #1 to fix installation issues
|
# awating pyghassen/openfire-restapi #1 to fix installation issues
|
||||||
git+https://github.com/adarnof/openfire-restapi
|
git+https://github.com/adarnof/openfire-restapi
|
||||||
|
|
||||||
git+https://github.com/adarnof/adarnauth-esi
|
adarnauth-esi>=1.4.1,<2.0
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ def auth_settings(request):
|
|||||||
'IPS4_URL': settings.IPS4_URL,
|
'IPS4_URL': settings.IPS4_URL,
|
||||||
'SMF_URL': settings.SMF_URL,
|
'SMF_URL': settings.SMF_URL,
|
||||||
'MARKET_URL': settings.MARKET_URL,
|
'MARKET_URL': settings.MARKET_URL,
|
||||||
|
'SEAT_URL': settings.SEAT_URL,
|
||||||
'EXTERNAL_MEDIA_URL': settings.EXTERNAL_MEDIA_URL,
|
'EXTERNAL_MEDIA_URL': settings.EXTERNAL_MEDIA_URL,
|
||||||
'CURRENT_UTC_TIME': timezone.now(),
|
'CURRENT_UTC_TIME': timezone.now(),
|
||||||
'BLUE_API_MASK': settings.BLUE_API_MASK,
|
'BLUE_API_MASK': settings.BLUE_API_MASK,
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ class srpManager:
|
|||||||
r = requests.get(url, headers=headers)
|
r = requests.get(url, headers=headers)
|
||||||
result = r.json()[0]
|
result = r.json()[0]
|
||||||
if result:
|
if result:
|
||||||
ship_type = result['victim']['shipTypeID']
|
ship_type = result['victim']['ship_type_id']
|
||||||
logger.debug("Ship type for kill ID %s is determined to be %s" % (kill_id, ship_type))
|
logger.debug("Ship type for kill ID %s is %s" % (kill_id, ship_type))
|
||||||
ship_value = result['zkb']['totalValue']
|
ship_value = result['zkb']['totalValue']
|
||||||
logger.debug("total loss value for kill id %s is %s" % (kill_id, ship_value))
|
logger.debug("Total loss value for kill id %s is %s" % (kill_id, ship_value))
|
||||||
victim_name = result['victim']['characterName']
|
victim_id = result['victim']['character_id']
|
||||||
return ship_type, ship_value, victim_name
|
return ship_type, ship_value, victim_id
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid Kill ID")
|
raise ValueError("Invalid Kill ID")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import math
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from requests_oauthlib import OAuth2Session
|
from requests_oauthlib import OAuth2Session
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
@@ -50,12 +51,20 @@ class DiscordApiTooBusy(DiscordApiException):
|
|||||||
|
|
||||||
class DiscordApiBackoff(DiscordApiException):
|
class DiscordApiBackoff(DiscordApiException):
|
||||||
def __init__(self, retry_after, global_ratelimit):
|
def __init__(self, retry_after, global_ratelimit):
|
||||||
|
"""
|
||||||
|
:param retry_after: int time to retry after in milliseconds
|
||||||
|
:param global_ratelimit: bool Is the API under a global backoff
|
||||||
|
"""
|
||||||
super(DiscordApiException, self).__init__()
|
super(DiscordApiException, self).__init__()
|
||||||
self.retry_after = retry_after
|
self.retry_after = retry_after
|
||||||
self.global_ratelimit = global_ratelimit
|
self.global_ratelimit = global_ratelimit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def retry_after_seconds(self):
|
||||||
|
return math.ceil(self.retry_after / 1000)
|
||||||
|
|
||||||
cache_time_format = '%Y-%m-%d %H:%M:%S'
|
|
||||||
|
cache_time_format = '%Y-%m-%d %H:%M:%S.%f'
|
||||||
|
|
||||||
|
|
||||||
def api_backoff(func):
|
def api_backoff(func):
|
||||||
@@ -117,12 +126,12 @@ def api_backoff(func):
|
|||||||
retry_after = int(e.response.headers['Retry-After'])
|
retry_after = int(e.response.headers['Retry-After'])
|
||||||
except (TypeError, KeyError):
|
except (TypeError, KeyError):
|
||||||
# Pick some random time
|
# Pick some random time
|
||||||
retry_after = 5
|
retry_after = 5000
|
||||||
|
|
||||||
logger.info("Received backoff from API of %s seconds, handling" % retry_after)
|
logger.info("Received backoff from API of %s seconds, handling" % retry_after)
|
||||||
# Store value in redis
|
# Store value in redis
|
||||||
backoff_until = (datetime.datetime.utcnow() +
|
backoff_until = (datetime.datetime.utcnow() +
|
||||||
datetime.timedelta(seconds=retry_after))
|
datetime.timedelta(milliseconds=retry_after))
|
||||||
global_backoff = bool(e.response.headers.get('X-RateLimit-Global', False))
|
global_backoff = bool(e.response.headers.get('X-RateLimit-Global', False))
|
||||||
if global_backoff:
|
if global_backoff:
|
||||||
logger.info("Global backoff!!")
|
logger.info("Global backoff!!")
|
||||||
@@ -138,7 +147,7 @@ def api_backoff(func):
|
|||||||
# Sleep if we're blocking
|
# Sleep if we're blocking
|
||||||
if blocking:
|
if blocking:
|
||||||
logger.info("Blocking Back off from API calls for %s seconds" % bo.retry_after)
|
logger.info("Blocking Back off from API calls for %s seconds" % bo.retry_after)
|
||||||
time.sleep(10 if bo.retry_after > 10 else bo.retry_after)
|
time.sleep((10 if bo.retry_after > 10 else bo.retry_after) / 1000)
|
||||||
else:
|
else:
|
||||||
# Otherwise raise exception and let caller handle the backoff
|
# Otherwise raise exception and let caller handle the backoff
|
||||||
raise DiscordApiBackoff(retry_after=bo.retry_after, global_ratelimit=bo.global_ratelimit)
|
raise DiscordApiBackoff(retry_after=bo.retry_after, global_ratelimit=bo.global_ratelimit)
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ class DiscordTasks:
|
|||||||
DiscordOAuthManager.update_groups(user.discord.uid, groups)
|
DiscordOAuthManager.update_groups(user.discord.uid, groups)
|
||||||
except DiscordApiBackoff as bo:
|
except DiscordApiBackoff as bo:
|
||||||
logger.info("Discord group sync API back off for %s, "
|
logger.info("Discord group sync API back off for %s, "
|
||||||
"retrying in %s seconds" % (user, bo.retry_after))
|
"retrying in %s seconds" % (user, bo.retry_after_seconds))
|
||||||
raise task_self.retry(countdown=bo.retry_after)
|
raise task_self.retry(countdown=bo.retry_after_seconds)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if task_self:
|
if task_self:
|
||||||
logger.exception("Discord group sync failed for %s, retrying in 10 mins" % user)
|
logger.exception("Discord group sync failed for %s, retrying in 10 mins" % user)
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ class DiscordManagerTestCase(TestCase):
|
|||||||
|
|
||||||
m.patch(request_url,
|
m.patch(request_url,
|
||||||
request_headers=headers,
|
request_headers=headers,
|
||||||
headers={'Retry-After': '200'},
|
headers={'Retry-After': '200000'},
|
||||||
status_code=429)
|
status_code=429)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -410,7 +410,7 @@ class DiscordManagerTestCase(TestCase):
|
|||||||
try:
|
try:
|
||||||
DiscordOAuthManager.update_groups(user_id, groups, blocking=False)
|
DiscordOAuthManager.update_groups(user_id, groups, blocking=False)
|
||||||
except manager.DiscordApiBackoff as bo:
|
except manager.DiscordApiBackoff as bo:
|
||||||
self.assertEqual(bo.retry_after, 200, 'Retry-After time must be equal to Retry-After set in header')
|
self.assertEqual(bo.retry_after, 200000, 'Retry-After time must be equal to Retry-After set in header')
|
||||||
self.assertFalse(bo.global_ratelimit, 'global_ratelimit must be False')
|
self.assertFalse(bo.global_ratelimit, 'global_ratelimit must be False')
|
||||||
raise bo
|
raise bo
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@ class DiscordManagerTestCase(TestCase):
|
|||||||
|
|
||||||
m.patch(request_url,
|
m.patch(request_url,
|
||||||
request_headers=headers,
|
request_headers=headers,
|
||||||
headers={'Retry-After': '200', 'X-RateLimit-Global': 'true'},
|
headers={'Retry-After': '200000', 'X-RateLimit-Global': 'true'},
|
||||||
status_code=429)
|
status_code=429)
|
||||||
|
|
||||||
# Act & Assert
|
# Act & Assert
|
||||||
@@ -445,7 +445,7 @@ class DiscordManagerTestCase(TestCase):
|
|||||||
try:
|
try:
|
||||||
DiscordOAuthManager.update_groups(user_id, groups, blocking=False)
|
DiscordOAuthManager.update_groups(user_id, groups, blocking=False)
|
||||||
except manager.DiscordApiBackoff as bo:
|
except manager.DiscordApiBackoff as bo:
|
||||||
self.assertEqual(bo.retry_after, 200, 'Retry-After time must be equal to Retry-After set in header')
|
self.assertEqual(bo.retry_after, 200000, 'Retry-After time must be equal to Retry-After set in header')
|
||||||
self.assertTrue(bo.global_ratelimit, 'global_ratelimit must be True')
|
self.assertTrue(bo.global_ratelimit, 'global_ratelimit must be True')
|
||||||
raise bo
|
raise bo
|
||||||
|
|
||||||
|
|||||||
@@ -264,5 +264,5 @@ class SeatManager:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def username_hash(username):
|
def username_hash(username):
|
||||||
m = hashlib.sha1()
|
m = hashlib.sha1()
|
||||||
m.update(username)
|
m.update(username.encode('utf-8'))
|
||||||
return m.hexdigest()
|
return m.hexdigest()
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ def srp_request_view(request, fleet_srp):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
srp_kill_link = srpManager.get_kill_id(srp_request.killboard_link)
|
srp_kill_link = srpManager.get_kill_id(srp_request.killboard_link)
|
||||||
(ship_type_id, ship_value, victim_name) = srpManager.get_kill_data(srp_kill_link)
|
(ship_type_id, ship_value, victim_id) = srpManager.get_kill_data(srp_kill_link)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.debug("User %s Submitted Invalid Killmail Link %s or server could not be reached" % (
|
logger.debug("User %s Submitted Invalid Killmail Link %s or server could not be reached" % (
|
||||||
request.user, srp_request.killboard_link))
|
request.user, srp_request.killboard_link))
|
||||||
@@ -235,7 +235,7 @@ def srp_request_view(request, fleet_srp):
|
|||||||
|
|
||||||
characters = EveManager.get_characters_by_owner_id(request.user.id)
|
characters = EveManager.get_characters_by_owner_id(request.user.id)
|
||||||
for character in characters:
|
for character in characters:
|
||||||
if character.character_name == victim_name:
|
if character.character_id == str(victim_id):
|
||||||
srp_request.srp_ship_name = EveManager.get_itemtype(ship_type_id).name
|
srp_request.srp_ship_name = EveManager.get_itemtype(ship_type_id).name
|
||||||
srp_request.kb_total_loss = ship_value
|
srp_request.kb_total_loss = ship_value
|
||||||
srp_request.post_time = post_time
|
srp_request.post_time = post_time
|
||||||
@@ -247,8 +247,8 @@ def srp_request_view(request, fleet_srp):
|
|||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
messages.error(request,
|
messages.error(request,
|
||||||
_("%(charname)s does not belong to your Auth account. Please add the API key for this character and try again")
|
_("Character ID %(charid)s does not belong to your Auth account. Please add the API key for this character and try again")
|
||||||
% {"charname": victim_name})
|
% {"charid": victim_id})
|
||||||
return redirect("auth_srp_management_view")
|
return redirect("auth_srp_management_view")
|
||||||
else:
|
else:
|
||||||
logger.debug("Returning blank SrpFleetUserRequestForm")
|
logger.debug("Returning blank SrpFleetUserRequestForm")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<head lang="en">
|
<head lang="en">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{{ SITE_NAME }}</title>
|
<title>{{ SITE_NAME }}</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
background: url('{% static 'img/index_images/index_blank_bg.jpg' %}') no-repeat scroll;
|
background: url('{% static 'img/index_images/index_blank_bg.jpg' %}') no-repeat scroll;
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
margin-top: -100px;
|
margin-top: -100px;
|
||||||
margin-left: -200px;
|
margin-left: -200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 900px;
|
width: 900px;
|
||||||
@@ -31,9 +33,18 @@
|
|||||||
margin-top: -100px;
|
margin-top: -100px;
|
||||||
margin-left: -450px;
|
margin-left: -450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:link, a:hover, a:visited, a:active {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 36px;
|
||||||
|
padding: 10px 20px 10px 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -44,29 +55,31 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<p style="text-align:center">
|
<p style="text-align:center">
|
||||||
<a href="/dashboard/">
|
<a href="/dashboard/">auth</a>
|
||||||
<img src="{% static 'img/index_images/auth.png' %}" alt="Auth">
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
{% if FORUM_URL %}
|
{% if FORUM_URL %}
|
||||||
<p style="text-align:center">
|
<p style="text-align:center">
|
||||||
<a href="{{FORUM_URL}}">
|
<a href="{{FORUM_URL}}">forum</a>
|
||||||
<img src="{% static 'img/index_images/forums.png' %}" alt="Forums">
|
</p>
|
||||||
</a>
|
{% endif %}
|
||||||
|
{% if MARKET_URL %}
|
||||||
|
<p style="text-align:center">
|
||||||
|
<a href="{{MARKET_URL}}">market</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if SEAT_URL %}
|
||||||
|
<p style="text-align:center">
|
||||||
|
<a href="{{SEAT_URL}}">seat</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if KILLBOARD_URL %}
|
{% if KILLBOARD_URL %}
|
||||||
<p style="text-align:center">
|
<p style="text-align:center">
|
||||||
<a href="{{KILLBOARD_URL}}">
|
<a href="{{KILLBOARD_URL}}">killboard</a>
|
||||||
<img src="{% static 'img/index_images/killboard.png' %}" alt="Killboard">
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if EXTERNAL_MEDIA_URL %}
|
{% if EXTERNAL_MEDIA_URL %}
|
||||||
<p style="text-align:center">
|
<p style="text-align:center">
|
||||||
<a href="{{EXTERNAL_MEDIA_URL}}">
|
<a href="{{EXTERNAL_MEDIA_URL}}">media</a>
|
||||||
<img src="{% static 'img/index_images/media.png' %}" alt="External Media">
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user