This commit is contained in:
Ariel Rin 2021-12-02 02:26:33 +10:00
commit ff7c9c48f3
365 changed files with 7117 additions and 3310 deletions

View File

@ -22,3 +22,7 @@ indent_style = tab
[*.bat] [*.bat]
indent_style = tab indent_style = tab
[{Dockerfile,*.dockerfile}]
indent_style = space
indent_size = 4

1
.gitignore vendored
View File

@ -38,7 +38,6 @@ htmlcov/
.tox/ .tox/
.coverage .coverage
.cache .cache
nosetests.xml
coverage.xml coverage.xml
# Translations # Translations

View File

@ -1,8 +1,15 @@
.only-default: &only-default
only:
- master
- branches
- merge_requests
stages: stages:
- pre-commit - pre-commit
- gitlab - gitlab
- test - test
- deploy - deploy
- docker
include: include:
- template: Dependency-Scanning.gitlab-ci.yml - template: Dependency-Scanning.gitlab-ci.yml
@ -15,6 +22,7 @@ before_script:
- pip install wheel tox - pip install wheel tox
pre-commit-check: pre-commit-check:
<<: *only-default
stage: pre-commit stage: pre-commit
image: python:3.6-buster image: python:3.6-buster
variables: variables:
@ -39,6 +47,7 @@ dependency_scanning:
- pip install wheel tox - pip install wheel tox
test-3.7-core: test-3.7-core:
<<: *only-default
image: python:3.7-bullseye image: python:3.7-bullseye
script: script:
- tox -e py37-core - tox -e py37-core
@ -48,6 +57,7 @@ test-3.7-core:
cobertura: coverage.xml cobertura: coverage.xml
test-3.8-core: test-3.8-core:
<<: *only-default
image: python:3.8-bullseye image: python:3.8-bullseye
script: script:
- tox -e py38-core - tox -e py38-core
@ -57,6 +67,7 @@ test-3.8-core:
cobertura: coverage.xml cobertura: coverage.xml
test-3.9-core: test-3.9-core:
<<: *only-default
image: python:3.9-bullseye image: python:3.9-bullseye
script: script:
- tox -e py39-core - tox -e py39-core
@ -65,7 +76,18 @@ test-3.9-core:
reports: reports:
cobertura: coverage.xml cobertura: coverage.xml
test-3.10-core:
<<: *only-default
image: python:3.10-bullseye
script:
- tox -e py310-core
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.7-all: test-3.7-all:
<<: *only-default
image: python:3.7-bullseye image: python:3.7-bullseye
script: script:
- tox -e py37-all - tox -e py37-all
@ -75,6 +97,7 @@ test-3.7-all:
cobertura: coverage.xml cobertura: coverage.xml
test-3.8-all: test-3.8-all:
<<: *only-default
image: python:3.8-bullseye image: python:3.8-bullseye
script: script:
- tox -e py38-all - tox -e py38-all
@ -84,6 +107,7 @@ test-3.8-all:
cobertura: coverage.xml cobertura: coverage.xml
test-3.9-all: test-3.9-all:
<<: *only-default
image: python:3.9-bullseye image: python:3.9-bullseye
script: script:
- tox -e py39-all - tox -e py39-all
@ -92,9 +116,19 @@ test-3.9-all:
reports: reports:
cobertura: coverage.xml cobertura: coverage.xml
test-3.10-all:
<<: *only-default
image: python:3.10-bullseye
script:
- tox -e py310-all
artifacts:
when: always
reports:
cobertura: coverage.xml
deploy_production: deploy_production:
stage: deploy stage: deploy
image: python:3.9-bullseye image: python:3.10-bullseye
before_script: before_script:
- pip install twine wheel - pip install twine wheel
@ -105,3 +139,65 @@ deploy_production:
rules: rules:
- if: $CI_COMMIT_TAG - if: $CI_COMMIT_TAG
build-image:
before_script: []
image: docker:20.10.10
stage: docker
services:
- docker:20.10.10-dind
script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_SHORT_SHA
CURRENT_TAG=$CI_REGISTRY_IMAGE/auth:$CI_COMMIT_TAG
MINOR_TAG=$CI_REGISTRY_IMAGE/auth:$(echo $CI_COMMIT_TAG | cut -d '.' -f 1-2)
MAJOR_TAG=$CI_REGISTRY_IMAGE/auth:$(echo $CI_COMMIT_TAG | cut -d '.' -f 1)
LATEST_TAG=$CI_REGISTRY_IMAGE/auth:latest
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
docker build . -t $IMAGE_TAG -f docker/Dockerfile --build-arg AUTH_VERSION=$(echo $CI_COMMIT_TAG | cut -c 2-)
docker tag $IMAGE_TAG $CURRENT_TAG
docker tag $IMAGE_TAG $MINOR_TAG
docker tag $IMAGE_TAG $MAJOR_TAG
docker tag $IMAGE_TAG $LATEST_TAG
docker image push --all-tags $CI_REGISTRY_IMAGE/auth
rules:
- if: $CI_COMMIT_TAG
build-image-dev:
before_script: []
image: docker:20.10.10
stage: docker
services:
- docker:20.10.10-dind
script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_COMMIT_BRANCH-$CI_COMMIT_SHORT_SHA
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
docker build . -t $IMAGE_TAG -f docker/Dockerfile --build-arg AUTH_PACKAGE=git+https://gitlab.com/allianceauth/allianceauth@$CI_COMMIT_BRANCH
docker push $IMAGE_TAG
rules:
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == ""'
when: manual
- if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME != ""'
when: never
build-image-mr:
before_script: []
image: docker:20.10.10
stage: docker
services:
- docker:20.10.10-dind
script: |
CURRENT_DATE=$(echo $CI_COMMIT_TIMESTAMP | head -c 10 | tr -d -)
IMAGE_TAG=$CI_REGISTRY_IMAGE/auth:$CURRENT_DATE-$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME-$CI_COMMIT_SHORT_SHA
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
docker build . -t $IMAGE_TAG -f docker/Dockerfile --build-arg AUTH_PACKAGE=git+$CI_MERGE_REQUEST_SOURCE_PROJECT_URL@$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
docker push $IMAGE_TAG
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: manual
- if: '$CI_PIPELINE_SOURCE != "merge_request_event"'
when: never

View File

@ -22,7 +22,13 @@ repos:
args: [ '--remove' ] args: [ '--remove' ]
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python - repo: https://github.com/editorconfig-checker/editorconfig-checker.python
rev: 2.3.5 rev: 2.3.54
hooks: hooks:
- id: editorconfig-checker - id: editorconfig-checker
exclude: ^(LICENSE|allianceauth\/static\/css\/themes\/bootstrap-locals.less|allianceauth\/eveonline\/swagger.json|(.*.po)|(.*.mo)) exclude: ^(LICENSE|allianceauth\/static\/css\/themes\/bootstrap-locals.less|allianceauth\/eveonline\/swagger.json|(.*.po)|(.*.mo))
- repo: https://github.com/asottile/pyupgrade
rev: v2.29.0
hooks:
- id: pyupgrade
args: [ --py37-plus ]

View File

@ -1,8 +1,8 @@
# This will make sure the app is always imported when # This will make sure the app is always imported when
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.
__version__ = '2.9.0' __version__ = '2.9.3'
__title__ = 'Alliance Auth' __title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth' __url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = '%s v%s' % (__title__, __version__) NAME = f'{__title__} v{__version__}'
default_app_config = 'allianceauth.apps.AllianceAuthConfig' default_app_config = 'allianceauth.apps.AllianceAuthConfig'

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.8 on 2021-10-17 16:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('analytics', '0004_auto_20211015_0502'),
]
operations = [
migrations.AlterField(
model_name='analyticspath',
name='ignore_path',
field=models.CharField(default='/example/', help_text='Regex Expression, If matched no Analytics Page View is sent', max_length=254),
),
]

View File

@ -16,7 +16,7 @@ class AnalyticsIdentifier(models.Model):
raise ValidationError('There is can be only one \ raise ValidationError('There is can be only one \
AnalyticsIdentifier instance') AnalyticsIdentifier instance')
self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1 self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1
return super(AnalyticsIdentifier, self).save(*args, **kwargs) return super().save(*args, **kwargs)
class AnalyticsPath(models.Model): class AnalyticsPath(models.Model):

View File

@ -17,7 +17,7 @@ from allianceauth.authentication.models import State, get_guest_state,\
CharacterOwnership, UserProfile, OwnershipRecord CharacterOwnership, UserProfile, OwnershipRecord
from allianceauth.hooks import get_hooks from allianceauth.hooks import get_hooks
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\ from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
EveAllianceInfo EveAllianceInfo, EveFactionInfo
from allianceauth.eveonline.tasks import update_character from allianceauth.eveonline.tasks import update_character
from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_GROUPS, \ from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_GROUPS, \
AUTHENTICATION_ADMIN_USERS_MAX_CHARS AUTHENTICATION_ADMIN_USERS_MAX_CHARS
@ -36,8 +36,8 @@ def make_service_hooks_update_groups_action(service):
for user in queryset: # queryset filtering doesn't work here? for user in queryset: # queryset filtering doesn't work here?
service.update_groups(user) service.update_groups(user)
update_service_groups.__name__ = str('update_{}_groups'.format(slugify(service.name))) update_service_groups.__name__ = str(f'update_{slugify(service.name)}_groups')
update_service_groups.short_description = "Sync groups for selected {} accounts".format(service.title) update_service_groups.short_description = f"Sync groups for selected {service.title} accounts"
return update_service_groups return update_service_groups
@ -54,8 +54,8 @@ def make_service_hooks_sync_nickname_action(service):
for user in queryset: # queryset filtering doesn't work here? for user in queryset: # queryset filtering doesn't work here?
service.sync_nickname(user) service.sync_nickname(user)
sync_nickname.__name__ = str('sync_{}_nickname'.format(slugify(service.name))) sync_nickname.__name__ = str(f'sync_{slugify(service.name)}_nickname')
sync_nickname.short_description = "Sync nicknames for selected {} accounts".format(service.title) sync_nickname.short_description = f"Sync nicknames for selected {service.title} accounts"
return sync_nickname return sync_nickname
@ -159,18 +159,14 @@ def user_main_organization(obj):
""" """
user_obj = obj.user if hasattr(obj, 'user') else obj user_obj = obj.user if hasattr(obj, 'user') else obj
if not user_obj.profile.main_character: if not user_obj.profile.main_character:
result = None result = ''
else: else:
corporation = user_obj.profile.main_character.corporation_name result = user_obj.profile.main_character.corporation_name
if user_obj.profile.main_character.alliance_id: if user_obj.profile.main_character.alliance_id:
result = format_html( result += f'<br>{user_obj.profile.main_character.alliance_name}'
'{}<br>{}', elif user_obj.profile.main_character.faction_name:
corporation, result += f'<br>{user_obj.profile.main_character.faction_name}'
user_obj.profile.main_character.alliance_name return format_html(result)
)
else:
result = corporation
return result
user_main_organization.short_description = 'Corporation / Alliance (Main)' user_main_organization.short_description = 'Corporation / Alliance (Main)'
@ -194,7 +190,7 @@ class MainCorporationsFilter(admin.SimpleListFilter):
.distinct()\ .distinct()\
.order_by(Lower('corporation_name')) .order_by(Lower('corporation_name'))
return tuple( return tuple(
[(x['corporation_id'], x['corporation_name']) for x in qs] (x['corporation_id'], x['corporation_name']) for x in qs
) )
def queryset(self, request, qs): def queryset(self, request, qs):
@ -228,7 +224,7 @@ class MainAllianceFilter(admin.SimpleListFilter):
.distinct()\ .distinct()\
.order_by(Lower('alliance_name')) .order_by(Lower('alliance_name'))
return tuple( return tuple(
[(x['alliance_id'], x['alliance_name']) for x in qs] (x['alliance_id'], x['alliance_name']) for x in qs
) )
def queryset(self, request, qs): def queryset(self, request, qs):
@ -243,6 +239,38 @@ class MainAllianceFilter(admin.SimpleListFilter):
) )
class MainFactionFilter(admin.SimpleListFilter):
"""Custom filter to filter on factions from mains only
works for both User objects and objects with `user` as FK to User
To be used for all user based admin lists
"""
title = 'faction'
parameter_name = 'main_faction_id__exact'
def lookups(self, request, model_admin):
qs = EveCharacter.objects\
.exclude(faction_id=None)\
.exclude(userprofile=None)\
.values('faction_id', 'faction_name')\
.distinct()\
.order_by(Lower('faction_name'))
return tuple(
(x['faction_id'], x['faction_name']) for x in qs
)
def queryset(self, request, qs):
if self.value() is None:
return qs.all()
else:
if qs.model == User:
return qs.filter(profile__main_character__faction_id=self.value())
else:
return qs.filter(
user__profile__main_character__faction_id=self.value()
)
def update_main_character_model(modeladmin, request, queryset): def update_main_character_model(modeladmin, request, queryset):
tasks_count = 0 tasks_count = 0
for obj in queryset: for obj in queryset:
@ -252,7 +280,7 @@ def update_main_character_model(modeladmin, request, queryset):
modeladmin.message_user( modeladmin.message_user(
request, request,
'Update from ESI started for {} characters'.format(tasks_count) f'Update from ESI started for {tasks_count} characters'
) )
@ -342,6 +370,7 @@ class UserAdmin(BaseUserAdmin):
'groups', 'groups',
MainCorporationsFilter, MainCorporationsFilter,
MainAllianceFilter, MainAllianceFilter,
MainFactionFilter,
'is_active', 'is_active',
'date_joined', 'date_joined',
'is_staff', 'is_staff',
@ -369,7 +398,7 @@ class UserAdmin(BaseUserAdmin):
_state.admin_order_field = 'profile__state' _state.admin_order_field = 'profile__state'
def _groups(self, obj): def _groups(self, obj):
my_groups = sorted([group.name for group in list(obj.groups.all())]) my_groups = sorted(group.name for group in list(obj.groups.all()))
return self._list_2_html_w_tooltips( return self._list_2_html_w_tooltips(
my_groups, AUTHENTICATION_ADMIN_USERS_MAX_GROUPS my_groups, AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
) )
@ -426,7 +455,8 @@ class StateAdmin(admin.ModelAdmin):
'public', 'public',
'member_characters', 'member_characters',
'member_corporations', 'member_corporations',
'member_alliances' 'member_alliances',
'member_factions'
), ),
}) })
) )
@ -434,6 +464,7 @@ class StateAdmin(admin.ModelAdmin):
'member_characters', 'member_characters',
'member_corporations', 'member_corporations',
'member_alliances', 'member_alliances',
'member_factions',
'permissions' 'permissions'
] ]
@ -448,6 +479,9 @@ class StateAdmin(admin.ModelAdmin):
elif db_field.name == "member_alliances": elif db_field.name == "member_alliances":
kwargs["queryset"] = EveAllianceInfo.objects.all()\ kwargs["queryset"] = EveAllianceInfo.objects.all()\
.order_by(Lower('alliance_name')) .order_by(Lower('alliance_name'))
elif db_field.name == "member_factions":
kwargs["queryset"] = EveFactionInfo.objects.all()\
.order_by(Lower('faction_name'))
elif db_field.name == "permissions": elif db_field.name == "permissions":
kwargs["queryset"] = Permission.objects.select_related("content_type").all() kwargs["queryset"] = Permission.objects.select_related("content_type").all()
return super().formfield_for_manytomany(db_field, request, **kwargs) return super().formfield_for_manytomany(db_field, request, **kwargs)
@ -455,7 +489,7 @@ class StateAdmin(admin.ModelAdmin):
def has_delete_permission(self, request, obj=None): def has_delete_permission(self, request, obj=None):
if obj == get_guest_state(): if obj == get_guest_state():
return False return False
return super(StateAdmin, self).has_delete_permission(request, obj=obj) return super().has_delete_permission(request, obj=obj)
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
if obj == get_guest_state(): if obj == get_guest_state():
@ -464,7 +498,7 @@ class StateAdmin(admin.ModelAdmin):
'fields': ('permissions', 'priority'), 'fields': ('permissions', 'priority'),
}), }),
) )
return super(StateAdmin, self).get_fieldsets(request, obj=obj) return super().get_fieldsets(request, obj=obj)
class BaseOwnershipAdmin(admin.ModelAdmin): class BaseOwnershipAdmin(admin.ModelAdmin):
@ -485,7 +519,8 @@ class BaseOwnershipAdmin(admin.ModelAdmin):
'user__username', 'user__username',
'character__character_name', 'character__character_name',
'character__corporation_name', 'character__corporation_name',
'character__alliance_name' 'character__alliance_name',
'character__faction_name'
) )
list_filter = ( list_filter = (
MainCorporationsFilter, MainCorporationsFilter,

View File

@ -7,6 +7,6 @@ class AuthenticationConfig(AppConfig):
label = 'authentication' label = 'authentication'
def ready(self): def ready(self):
super(AuthenticationConfig, self).ready() super().ready()
from allianceauth.authentication import checks, signals from allianceauth.authentication import checks, signals
register(Tags.security)(checks.check_login_scopes_setting) register(Tags.security)(checks.check_login_scopes_setting)

View File

@ -36,17 +36,17 @@ class StateBackend(ModelBackend):
try: try:
ownership = CharacterOwnership.objects.get(character__character_id=token.character_id) ownership = CharacterOwnership.objects.get(character__character_id=token.character_id)
if ownership.owner_hash == token.character_owner_hash: if ownership.owner_hash == token.character_owner_hash:
logger.debug('Authenticating {0} by ownership of character {1}'.format(ownership.user, token.character_name)) logger.debug(f'Authenticating {ownership.user} by ownership of character {token.character_name}')
return ownership.user return ownership.user
else: else:
logger.debug('{0} has changed ownership. Creating new user account.'.format(token.character_name)) logger.debug(f'{token.character_name} has changed ownership. Creating new user account.')
ownership.delete() ownership.delete()
return self.create_user(token) return self.create_user(token)
except CharacterOwnership.DoesNotExist: except CharacterOwnership.DoesNotExist:
try: try:
# insecure legacy main check for pre-sso registration auth installs # insecure legacy main check for pre-sso registration auth installs
profile = UserProfile.objects.get(main_character__character_id=token.character_id) profile = UserProfile.objects.get(main_character__character_id=token.character_id)
logger.debug('Authenticating {0} by their main character {1} without active ownership.'.format(profile.user, profile.main_character)) logger.debug(f'Authenticating {profile.user} by their main character {profile.main_character} without active ownership.')
# attach an ownership # attach an ownership
token.user = profile.user token.user = profile.user
CharacterOwnership.objects.create_by_token(token) CharacterOwnership.objects.create_by_token(token)
@ -59,13 +59,13 @@ class StateBackend(ModelBackend):
user = records[0].user user = records[0].user
token.user = user token.user = user
co = CharacterOwnership.objects.create_by_token(token) co = CharacterOwnership.objects.create_by_token(token)
logger.debug('Authenticating {0} by matching owner hash record of character {1}'.format(user, co.character)) logger.debug(f'Authenticating {user} by matching owner hash record of character {co.character}')
if not user.profile.main_character: if not user.profile.main_character:
# set this as their main by default if they have none # set this as their main by default if they have none
user.profile.main_character = co.character user.profile.main_character = co.character
user.profile.save() user.profile.save()
return user return user
logger.debug('Unable to authenticate character {0}. Creating new user.'.format(token.character_name)) logger.debug(f'Unable to authenticate character {token.character_name}. Creating new user.')
return self.create_user(token) return self.create_user(token)
def create_user(self, token): def create_user(self, token):
@ -77,7 +77,7 @@ class StateBackend(ModelBackend):
co = CharacterOwnership.objects.create_by_token(token) # assign ownership to this user co = CharacterOwnership.objects.create_by_token(token) # assign ownership to this user
user.profile.main_character = co.character # assign main character as token character user.profile.main_character = co.character # assign main character as token character
user.profile.save() user.profile.save()
logger.debug('Created new user {0}'.format(user)) logger.debug(f'Created new user {user}')
return user return user
@staticmethod @staticmethod
@ -87,10 +87,10 @@ class StateBackend(ModelBackend):
if User.objects.filter(username__startswith=name).exists(): if User.objects.filter(username__startswith=name).exists():
u = User.objects.filter(username__startswith=name) u = User.objects.filter(username__startswith=name)
num = len(u) num = len(u)
username = "%s_%s" % (name, num) username = f"{name}_{num}"
while u.filter(username=username).exists(): while u.filter(username=username).exists():
num += 1 num += 1
username = "%s_%s" % (name, num) username = f"{name}_{num}"
else: else:
username = name username = name
return username return username

View File

@ -11,10 +11,10 @@ class Command(BaseCommand):
if profiles.exists(): if profiles.exists():
for profile in profiles: for profile in profiles:
self.stdout.write(self.style.ERROR( self.stdout.write(self.style.ERROR(
'{0} does not have an ownership. Resetting user {1} main character.'.format(profile.main_character, '{} does not have an ownership. Resetting user {} main character.'.format(profile.main_character,
profile.user))) profile.user)))
profile.main_character = None profile.main_character = None
profile.save() profile.save()
self.stdout.write(self.style.WARNING('Reset {0} main characters.'.format(profiles.count()))) self.stdout.write(self.style.WARNING(f'Reset {profiles.count()} main characters.'))
else: else:
self.stdout.write(self.style.SUCCESS('All main characters have active ownership.')) self.stdout.write(self.style.SUCCESS('All main characters have active ownership.'))

View File

@ -16,6 +16,8 @@ def available_states_query(character):
query |= Q(member_corporations__corporation_id=character.corporation_id) query |= Q(member_corporations__corporation_id=character.corporation_id)
if character.alliance_id: if character.alliance_id:
query |= Q(member_alliances__alliance_id=character.alliance_id) query |= Q(member_alliances__alliance_id=character.alliance_id)
if character.faction_id:
query |= Q(member_factions__faction_id=character.faction_id)
return query return query
@ -49,7 +51,7 @@ class StateQuerySet(QuerySet):
for state in self: for state in self:
for profile in state.userprofile_set.all(): for profile in state.userprofile_set.all():
profile.assign_state(state=self.model.objects.exclude(pk=state.pk).get_for_user(profile.user)) profile.assign_state(state=self.model.objects.exclude(pk=state.pk).get_for_user(profile.user))
super(StateQuerySet, self).delete() super().delete()
class StateManager(Manager): class StateManager(Manager):

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-05 21:38 # Generated by Django 1.10.1 on 2016-09-05 21:38
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-07 19:14 # Generated by Django 1.10.1 on 2016-09-07 19:14
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-09 20:29 # Generated by Django 1.10.1 on 2016-09-09 20:29
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-09 23:19 # Generated by Django 1.10.1 on 2016-09-09 23:19
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-09 23:11 # Generated by Django 1.10.1 on 2016-09-09 23:11
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-10 05:42 # Generated by Django 1.10.1 on 2016-09-10 05:42
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-10 21:50 # Generated by Django 1.10.1 on 2016-09-10 21:50
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-12 13:04 # Generated by Django 1.10.1 on 2016-09-12 13:04
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.2 on 2016-10-21 02:28 # Generated by Django 1.10.2 on 2016-10-21 02:28
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2017-01-07 06:47 # Generated by Django 1.10.1 on 2017-01-07 06:47
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations
@ -9,7 +8,7 @@ def count_completed_fields(model):
def forward(apps, schema_editor): def forward(apps, schema_editor):
# this ensures only one model exists per user # this ensures only one model exists per user
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo') AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
users = set([a.user for a in AuthServicesInfo.objects.all()]) users = {a.user for a in AuthServicesInfo.objects.all()}
for u in users: for u in users:
auths = AuthServicesInfo.objects.filter(user=u) auths = AuthServicesInfo.objects.filter(user=u)
if auths.count() > 1: if auths.count() > 1:

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2017-01-07 07:11 # Generated by Django 1.10.1 on 2017-01-07 07:11
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.5 on 2017-01-12 00:59 # Generated by Django 1.10.5 on 2017-01-12 00:59
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.2 on 2016-12-11 23:14 # Generated by Django 1.10.2 on 2016-12-11 23:14
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-09 23:19 # Generated by Django 1.10.1 on 2016-09-09 23:19
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.5 on 2017-03-22 23:09 # Generated by Django 1.10.5 on 2017-03-22 23:09
from __future__ import unicode_literals
import allianceauth.authentication.models import allianceauth.authentication.models
import django.db.models.deletion import django.db.models.deletion

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.8 on 2021-10-20 05:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0017_remove_fleetup_permission'),
]
operations = [
migrations.AlterField(
model_name='state',
name='name',
field=models.CharField(max_length=32, unique=True),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.1.13 on 2021-10-12 20:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0015_factions'),
('authentication', '0017_remove_fleetup_permission'),
]
operations = [
migrations.AddField(
model_name='state',
name='member_factions',
field=models.ManyToManyField(blank=True, help_text='Factions to whose members this state is available.', to='eveonline.EveFactionInfo'),
),
]

View File

@ -0,0 +1,14 @@
# Generated by Django 3.2.8 on 2021-10-26 09:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('authentication', '0018_alter_state_name_length'),
('authentication', '0018_state_member_factions'),
]
operations = [
]

View File

@ -3,7 +3,7 @@ import logging
from django.contrib.auth.models import User, Permission from django.contrib.auth.models import User, Permission
from django.db import models, transaction from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
from allianceauth.notifications import notify from allianceauth.notifications import notify
from .managers import CharacterOwnershipManager, StateManager from .managers import CharacterOwnershipManager, StateManager
@ -12,13 +12,18 @@ logger = logging.getLogger(__name__)
class State(models.Model): class State(models.Model):
name = models.CharField(max_length=20, unique=True) name = models.CharField(max_length=32, unique=True)
permissions = models.ManyToManyField(Permission, blank=True) permissions = models.ManyToManyField(Permission, blank=True)
priority = models.IntegerField(unique=True, help_text="Users get assigned the state with the highest priority available to them.") priority = models.IntegerField(unique=True, help_text="Users get assigned the state with the highest priority available to them.")
member_characters = models.ManyToManyField(EveCharacter, blank=True, help_text="Characters to which this state is available.") member_characters = models.ManyToManyField(EveCharacter, blank=True,
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True, help_text="Corporations to whose members this state is available.") help_text="Characters to which this state is available.")
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True, help_text="Alliances to whose members this state is available.") member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True,
help_text="Corporations to whose members this state is available.")
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True,
help_text="Alliances to whose members this state is available.")
member_factions = models.ManyToManyField(EveFactionInfo, blank=True,
help_text="Factions to whose members this state is available.")
public = models.BooleanField(default=False, help_text="Make this state available to any character.") public = models.BooleanField(default=False, help_text="Make this state available to any character.")
objects = StateManager() objects = StateManager()
@ -39,7 +44,7 @@ class State(models.Model):
with transaction.atomic(): with transaction.atomic():
for profile in self.userprofile_set.all(): for profile in self.userprofile_set.all():
profile.assign_state(state=State.objects.exclude(pk=self.pk).get_for_user(profile.user)) profile.assign_state(state=State.objects.exclude(pk=self.pk).get_for_user(profile.user))
super(State, self).delete(**kwargs) super().delete(**kwargs)
def get_guest_state(): def get_guest_state():
@ -67,7 +72,7 @@ class UserProfile(models.Model):
if self.state != state: if self.state != state:
self.state = state self.state = state
if commit: if commit:
logger.info('Updating {} state to {}'.format(self.user, self.state)) logger.info(f'Updating {self.user} state to {self.state}')
self.save(update_fields=['state']) self.save(update_fields=['state'])
notify( notify(
self.user, self.user,
@ -102,7 +107,7 @@ class CharacterOwnership(models.Model):
objects = CharacterOwnershipManager() objects = CharacterOwnershipManager()
def __str__(self): def __str__(self):
return "%s: %s" % (self.user, self.character) return f"{self.user}: {self.character}"
class OwnershipRecord(models.Model): class OwnershipRecord(models.Model):
@ -115,4 +120,4 @@ class OwnershipRecord(models.Model):
ordering = ['-created'] ordering = ['-created']
def __str__(self): def __str__(self):
return "%s: %s on %s" % (self.user, self.character, self.created) return f"{self.user}: {self.character} on {self.created}"

View File

@ -29,27 +29,32 @@ def trigger_state_check(state):
@receiver(m2m_changed, sender=State.member_characters.through) @receiver(m2m_changed, sender=State.member_characters.through)
def state_member_characters_changed(sender, instance, action, *args, **kwargs): def state_member_characters_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'): if action.startswith('post_'):
logger.debug('State {} member characters changed. Re-evaluating membership.'.format(instance)) logger.debug(f'State {instance} member characters changed. Re-evaluating membership.')
trigger_state_check(instance) trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_corporations.through) @receiver(m2m_changed, sender=State.member_corporations.through)
def state_member_corporations_changed(sender, instance, action, *args, **kwargs): def state_member_corporations_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'): if action.startswith('post_'):
logger.debug('State {} member corporations changed. Re-evaluating membership.'.format(instance)) logger.debug(f'State {instance} member corporations changed. Re-evaluating membership.')
trigger_state_check(instance) trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_alliances.through) @receiver(m2m_changed, sender=State.member_alliances.through)
def state_member_alliances_changed(sender, instance, action, *args, **kwargs): def state_member_alliances_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'): if action.startswith('post_'):
logger.debug('State {} member alliances changed. Re-evaluating membership.'.format(instance)) logger.debug(f'State {instance} member alliances changed. Re-evaluating membership.')
trigger_state_check(instance) trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_factions.through)
def state_member_factions_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
logger.debug(f'State {instance} member factions changed. Re-evaluating membership.')
trigger_state_check(instance)
@receiver(post_save, sender=State) @receiver(post_save, sender=State)
def state_saved(sender, instance, *args, **kwargs): def state_saved(sender, instance, *args, **kwargs):
logger.debug('State {} saved. Re-evaluating membership.'.format(instance)) logger.debug(f'State {instance} saved. Re-evaluating membership.')
trigger_state_check(instance) trigger_state_check(instance)
@ -60,7 +65,7 @@ def reassess_on_profile_save(sender, instance, created, *args, **kwargs):
if not created: if not created:
update_fields = kwargs.pop('update_fields', []) or [] update_fields = kwargs.pop('update_fields', []) or []
if 'state' not in update_fields: if 'state' not in update_fields:
logger.debug('Profile for {} saved without state change. Re-evaluating state.'.format(instance.user)) logger.debug(f'Profile for {instance.user} saved without state change. Re-evaluating state.')
instance.assign_state() instance.assign_state()
@ -68,14 +73,14 @@ def reassess_on_profile_save(sender, instance, created, *args, **kwargs):
def create_required_models(sender, instance, created, *args, **kwargs): def create_required_models(sender, instance, created, *args, **kwargs):
# ensure all users have a model # ensure all users have a model
if created: if created:
logger.debug('User {} created. Creating default UserProfile.'.format(instance)) logger.debug(f'User {instance} created. Creating default UserProfile.')
UserProfile.objects.get_or_create(user=instance) UserProfile.objects.get_or_create(user=instance)
@receiver(post_save, sender=Token) @receiver(post_save, sender=Token)
def record_character_ownership(sender, instance, created, *args, **kwargs): def record_character_ownership(sender, instance, created, *args, **kwargs):
if created: if created:
logger.debug('New token for {0} character {1} saved. Evaluating ownership.'.format(instance.user, instance.character_name)) logger.debug(f'New token for {instance.user} character {instance.character_name} saved. Evaluating ownership.')
if instance.user: if instance.user:
query = Q(owner_hash=instance.character_owner_hash) & Q(user=instance.user) query = Q(owner_hash=instance.character_owner_hash) & Q(user=instance.user)
else: else:
@ -84,13 +89,13 @@ def record_character_ownership(sender, instance, created, *args, **kwargs):
CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(query).delete() CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(query).delete()
# create character if needed # create character if needed
if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False: if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False:
logger.debug('Token is for a new character. Creating model for {0} ({1})'.format(instance.character_name, instance.character_id)) logger.debug(f'Token is for a new character. Creating model for {instance.character_name} ({instance.character_id})')
EveCharacter.objects.create_character(instance.character_id) EveCharacter.objects.create_character(instance.character_id)
char = EveCharacter.objects.get(character_id=instance.character_id) char = EveCharacter.objects.get(character_id=instance.character_id)
# check if we need to create ownership # check if we need to create ownership
if instance.user and not CharacterOwnership.objects.filter( if instance.user and not CharacterOwnership.objects.filter(
character__character_id=instance.character_id).exists(): character__character_id=instance.character_id).exists():
logger.debug("Character {0} is not yet owned. Assigning ownership to {1}".format(instance.character_name, instance.user)) logger.debug(f"Character {instance.character_name} is not yet owned. Assigning ownership to {instance.user}")
CharacterOwnership.objects.update_or_create(character=char, defaults={'owner_hash': instance.character_owner_hash, 'user': instance.user}) CharacterOwnership.objects.update_or_create(character=char, defaults={'owner_hash': instance.character_owner_hash, 'user': instance.user})
@ -98,7 +103,7 @@ def record_character_ownership(sender, instance, created, *args, **kwargs):
def validate_main_character(sender, instance, *args, **kwargs): def validate_main_character(sender, instance, *args, **kwargs):
try: try:
if instance.user.profile.main_character == instance.character: if instance.user.profile.main_character == instance.character:
logger.info("Ownership of a main character {0} has been revoked. Resetting {1} main character.".format( logger.info("Ownership of a main character {} has been revoked. Resetting {} main character.".format(
instance.character, instance.user)) instance.character, instance.user))
# clear main character as user no longer owns them # clear main character as user no longer owns them
instance.user.profile.main_character = None instance.user.profile.main_character = None
@ -111,7 +116,7 @@ def validate_main_character(sender, instance, *args, **kwargs):
@receiver(post_delete, sender=Token) @receiver(post_delete, sender=Token)
def validate_ownership(sender, instance, *args, **kwargs): def validate_ownership(sender, instance, *args, **kwargs):
if not Token.objects.filter(character_owner_hash=instance.character_owner_hash).filter(refresh_token__isnull=False).exists(): if not Token.objects.filter(character_owner_hash=instance.character_owner_hash).filter(refresh_token__isnull=False).exists():
logger.info("No remaining tokens to validate ownership of character {0}. Revoking ownership.".format(instance.character_name)) logger.info(f"No remaining tokens to validate ownership of character {instance.character_name}. Revoking ownership.")
CharacterOwnership.objects.filter(owner_hash=instance.character_owner_hash).delete() CharacterOwnership.objects.filter(owner_hash=instance.character_owner_hash).delete()
@ -122,11 +127,11 @@ def assign_state_on_active_change(sender, instance, *args, **kwargs):
old_instance = User.objects.get(pk=instance.pk) old_instance = User.objects.get(pk=instance.pk)
if old_instance.is_active != instance.is_active: if old_instance.is_active != instance.is_active:
if instance.is_active: if instance.is_active:
logger.debug("User {0} has been activated. Assigning state.".format(instance)) logger.debug(f"User {instance} has been activated. Assigning state.")
instance.profile.assign_state() instance.profile.assign_state()
else: else:
logger.debug( logger.debug(
"User {0} has been deactivated. Revoking state and assigning to guest state.".format(instance)) f"User {instance} has been deactivated. Revoking state and assigning to guest state.")
instance.profile.state = get_guest_state() instance.profile.state = get_guest_state()
instance.profile.save(update_fields=['state']) instance.profile.save(update_fields=['state'])
@ -135,10 +140,10 @@ def assign_state_on_active_change(sender, instance, *args, **kwargs):
def check_state_on_character_update(sender, instance, *args, **kwargs): def check_state_on_character_update(sender, instance, *args, **kwargs):
# if this is a main character updating, check that user's state # if this is a main character updating, check that user's state
try: try:
logger.debug("Character {0} has been saved. Assessing owner's state for changes.".format(instance)) logger.debug(f"Character {instance} has been saved. Assessing owner's state for changes.")
instance.userprofile.assign_state() instance.userprofile.assign_state()
except UserProfile.DoesNotExist: except UserProfile.DoesNotExist:
logger.debug("Character {0} is not a main character. No state assessment required.".format(instance)) logger.debug(f"Character {instance} is not a main character. No state assessment required.")
pass pass
@ -148,7 +153,7 @@ def ownership_record_creation(sender, instance, created, *args, **kwargs):
records = OwnershipRecord.objects.filter(owner_hash=instance.owner_hash).filter(character=instance.character) records = OwnershipRecord.objects.filter(owner_hash=instance.owner_hash).filter(character=instance.character)
if records.exists(): if records.exists():
if records[0].user == instance.user: # most recent record is sorted first if records[0].user == instance.user: # most recent record is sorted first
logger.debug("Already have ownership record of {0} by user {1}".format(instance.character, instance.user)) logger.debug(f"Already have ownership record of {instance.character} by user {instance.user}")
return return
logger.info("Character {0} has a new owner {1}. Creating ownership record.".format(instance.character, instance.user)) logger.info(f"Character {instance.character} has a new owner {instance.user}. Creating ownership record.")
OwnershipRecord.objects.create(user=instance.user, character=instance.character, owner_hash=instance.owner_hash) OwnershipRecord.objects.create(user=instance.user, character=instance.character, owner_hash=instance.owner_hash)

View File

@ -22,13 +22,13 @@ def check_character_ownership(owner_hash):
continue continue
except (KeyError, IncompleteResponseError): except (KeyError, IncompleteResponseError):
# We can't validate the hash hasn't changed but also can't assume it has. Abort for now. # We can't validate the hash hasn't changed but also can't assume it has. Abort for now.
logger.warning("Failed to validate owner hash of {0} due to problems contacting SSO servers.".format( logger.warning("Failed to validate owner hash of {} due to problems contacting SSO servers.".format(
tokens[0].character_name)) tokens[0].character_name))
break break
if not t.character_owner_hash == old_hash: if not t.character_owner_hash == old_hash:
logger.info( logger.info(
'Character %s has changed ownership. Revoking %s tokens.' % (t.character_name, tokens.count())) f'Character {t.character_name} has changed ownership. Revoking {tokens.count()} tokens.')
tokens.delete() tokens.delete()
break break

View File

@ -2,10 +2,10 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Dashboard" %}{% endblock %} {% block page_title %}{% translate "Dashboard" %}{% endblock %}
{% block content %} {% block content %}
<h1 class="page-header text-center">{% trans "Dashboard" %}</h1> <h1 class="page-header text-center">{% translate "Dashboard" %}</h1>
{% if user.is_staff %} {% if user.is_staff %}
{% include 'allianceauth/admin-status/include.html' %} {% include 'allianceauth/admin-status/include.html' %}
{% endif %} {% endif %}
@ -60,6 +60,17 @@
<td class="text-center">{{ main.alliance_name }}</td> <td class="text-center">{{ main.alliance_name }}</td>
<tr> <tr>
</table> </table>
{% elif main.faction_id %}
<table class="table">
<tr>
<td class="text-center">
<img class="ra-avatar"src="{{ main.faction_logo_url_128 }}">
</td>
</tr>
<tr>
<td class="text-center">{{ main.faction_name }}</td>
<tr>
</table>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -67,29 +78,39 @@
<p> <p>
<img class="ra-avatar" src="{{ main.portrait_url_64 }}"> <img class="ra-avatar" src="{{ main.portrait_url_64 }}">
<img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}"> <img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}">
{% if main.alliance_id %}
<img class="ra-avatar" src="{{ main.alliance_logo_url_64 }}"> <img class="ra-avatar" src="{{ main.alliance_logo_url_64 }}">
{% endif %}
{% if main.faction_id %}
<img class="ra-avatar" src="{{ main.faction_logo_url_64 }}">
{% endif %}
</p> </p>
<p> <p>
<strong>{{ main.character_name }}</strong><br> <strong>{{ main.character_name }}</strong><br>
{{ main.corporation_name }}<br> {{ main.corporation_name }}<br>
{{ main.alliance_name }} {% if main.alliance_id %}
{{ main.alliance_name }}<br>
{% endif %}
{% if main.faction_id %}
{{ main.faction_name }}
{% endif %}
</p> </p>
</div> </div>
{% endwith %} {% endwith %}
{% else %} {% else %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
{% trans "No main character set." %} {% translate "No main character set." %}
</div> </div>
{% endif %} {% endif %}
<div class="clearfix"></div> <div class="clearfix"></div>
<div class="row"> <div class="row">
<div class="col-sm-6 button-wrapper"> <div class="col-sm-6 button-wrapper">
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info" <a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info"
title="Add Character">{% trans 'Add Character' %}</a> title="Add Character">{% translate 'Add Character' %}</a>
</div> </div>
<div class="col-sm-6 button-wrapper"> <div class="col-sm-6 button-wrapper">
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info" <a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info"
title="Change Main Character">{% trans "Change Main" %}</a> title="Change Main Character">{% translate "Change Main" %}</a>
</div> </div>
</div> </div>
</div> </div>
@ -98,7 +119,7 @@
<div class="col-sm-6 text-center"> <div class="col-sm-6 text-center">
<div class="panel panel-success" style="height:100%"> <div class="panel panel-success" style="height:100%">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{% trans "Group Memberships" %}</h3> <h3 class="panel-title">{% translate "Group Memberships" %}</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div style="height: 240px;overflow:-moz-scrollbars-vertical;overflow-y:auto;"> <div style="height: 240px;overflow:-moz-scrollbars-vertical;overflow-y:auto;">
@ -118,7 +139,7 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title text-center" style="text-align: center"> <h3 class="panel-title text-center" style="text-align: center">
{% trans 'Characters' %} {% translate 'Characters' %}
</h3> </h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
@ -126,9 +147,9 @@
<thead> <thead>
<tr> <tr>
<th class="text-center"></th> <th class="text-center"></th>
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% translate 'Name' %}</th>
<th class="text-center">{% trans 'Corp' %}</th> <th class="text-center">{% translate 'Corp' %}</th>
<th class="text-center">{% trans 'Alliance' %}</th> <th class="text-center">{% translate 'Alliance' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -12,7 +12,7 @@
{% include 'allianceauth/icons.html' %} {% include 'allianceauth/icons.html' %}
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title> <title>{% block title %}{% block page_title %}{% endblock page_title %} - {{ SITE_NAME }}{% endblock title %}</title>
{% include 'bundles/bootstrap-css.html' %} {% include 'bundles/bootstrap-css.html' %}
{% include 'bundles/fontawesome.html' %} {% include 'bundles/fontawesome.html' %}

View File

@ -1,6 +1,10 @@
{% extends 'public/middle_box.html' %} {% extends 'public/middle_box.html' %}
{% load i18n %}
{% load static %} {% load static %}
{% block page_title %}Login{% endblock %}
{% block page_title %}{% translate "Login" %}{% endblock %}
{% block middle_box_content %} {% block middle_box_content %}
<a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}"> <a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}">
<img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0> <img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>

View File

@ -1,6 +1,5 @@
{% extends 'public/base.html' %} {% extends 'public/base.html' %}
{% load static %} {% load static %}
{% block title %}Login{% endblock %}
{% block content %} {% block content %}
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">
{% if messages %} {% if messages %}

View File

@ -1,13 +1,17 @@
{% extends 'public/base.html' %} {% extends 'public/base.html' %}
{% load static %} {% load static %}
{% load bootstrap %} {% load bootstrap %}
{% load i18n %} {% load i18n %}
{% block page_title %}Registration{% endblock %}
{% block page_title %}{% translate "Registration" %}{% endblock %}
{% block extra_include %} {% block extra_include %}
{% include 'bundles/bootstrap-css.html' %} {% include 'bundles/bootstrap-css.html' %}
{% include 'bundles/fontawesome.html' %} {% include 'bundles/fontawesome.html' %}
{% include 'bundles/bootstrap-js.html' %} {% include 'bundles/bootstrap-js.html' %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">
<div class="panel panel-default panel-transparent"> <div class="panel panel-default panel-transparent">
@ -15,7 +19,7 @@
<form method="POST"> <form method="POST">
{% csrf_token %} {% csrf_token %}
{{ form|bootstrap }} {{ form|bootstrap }}
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Register" %}</button> <button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Register" %}</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
{% extends 'public/middle_box.html' %} {% extends 'public/middle_box.html' %}
{% load i18n %} {% load i18n %}
{% block middle_box_content %} {% block middle_box_content %}
<div class="alert alert-danger">{% trans 'Invalid or expired activation link.' %}</div> <div class="alert alert-danger">{% translate 'Invalid or expired activation link.' %}</div>
{% endblock %} {% endblock %}

View File

@ -2,10 +2,6 @@ You're receiving this email because someone has entered this email address while
If this was you, please click on the link below to confirm your email address: If this was you, please click on the link below to confirm your email address:
<a href="{{ scheme }}://{{ url }}">Confirm email address</a>
Link not working? Try copy/pasting this URL into your browser:
{{ scheme }}://{{ url }} {{ scheme }}://{{ url }}
This link will expire in {{ expiration_days }} day(s). This link will expire in {{ expiration_days }} day(s).

View File

@ -0,0 +1,19 @@
<p>
You're receiving this email because someone has entered this email address while registering for an account on {{ site.domain }}
</p>
<p>
If this was you, please click on the link below to confirm your email address:
<p>
<p>
<a href="{{ scheme }}://{{ url }}">Confirm email address</a>
</p>
<p>
This link will expire in {{ expiration_days }} day(s).
</p>
<p>
If this was not you, it is safe to ignore this email.
</p>

View File

@ -2,13 +2,13 @@
{% blocktrans trimmed %}You're receiving this email because you requested a password reset for your {% blocktrans trimmed %}You're receiving this email because you requested a password reset for your
user account.{% endblocktrans %} user account.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %} {% translate "Please go to the following page and choose a new password:" %}
{% block reset_link %} {% block reset_link %}
{{domain}}{% url 'password_reset_confirm' uidb64=uid token=token %} {{domain}}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %} {% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }} {% translate "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %} {% translate "Thanks for using our site!" %}
{% blocktrans %}Your IT Team{% endblocktrans %} {% blocktrans %}Your IT Team{% endblocktrans %}

View File

@ -2,13 +2,13 @@
{% load bootstrap %} {% load bootstrap %}
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
{% block page_title %}Register{% endblock %} {% block page_title %}{% translate "Register" %}{% endblock %}
{% block middle_box_content %} {% block middle_box_content %}
<form class="form-signin" role="form" action="" method="POST"> <form class="form-signin" role="form" action="" method="POST">
{% csrf_token %} {% csrf_token %}
{{ form|bootstrap }} {{ form|bootstrap }}
<br/> <br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Submit" %}</button> <button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Submit" %}</button>
<br/> <br/>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -9,7 +9,7 @@ from allianceauth.authentication.models import (
CharacterOwnership, State, OwnershipRecord CharacterOwnership, State, OwnershipRecord
) )
from allianceauth.eveonline.models import ( from allianceauth.eveonline.models import (
EveCharacter, EveCorporationInfo, EveAllianceInfo EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
) )
from allianceauth.services.hooks import ServicesHook from allianceauth.services.hooks import ServicesHook
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
@ -20,6 +20,7 @@ from ..admin import (
StateAdmin, StateAdmin,
MainCorporationsFilter, MainCorporationsFilter,
MainAllianceFilter, MainAllianceFilter,
MainFactionFilter,
OwnershipRecordAdmin, OwnershipRecordAdmin,
User, User,
UserAdmin, UserAdmin,
@ -36,7 +37,7 @@ from . import get_admin_change_view_url, get_admin_search_url
MODULE_PATH = 'allianceauth.authentication.admin' MODULE_PATH = 'allianceauth.authentication.admin'
class MockRequest(object): class MockRequest:
def __init__(self, user=None): def __init__(self, user=None):
self.user = user self.user = user
@ -180,6 +181,30 @@ class TestCaseWithTestData(TestCase):
cls.user_3.is_superuser = True cls.user_3.is_superuser = True
cls.user_3.save() cls.user_3.save()
# user 4 - corp and faction, no alliance
cls.character_4 = EveCharacter.objects.create(
character_id=4321,
character_name='Professor X',
corporation_id=5432,
corporation_name="Xavier's School for Gifted Youngsters",
corporation_ticker='MUTNT',
alliance_id = None,
faction_id=999,
faction_name='The X-Men',
)
cls.user_4 = User.objects.create_user(
cls.character_4.character_name.replace(' ', '_'),
'abc@example.com',
'password'
)
CharacterOwnership.objects.create(
character=cls.character_4,
owner_hash='x1' + cls.character_4.character_name,
user=cls.user_4
)
cls.user_4.profile.main_character = cls.character_4
cls.user_4.profile.save()
EveFactionInfo.objects.create(faction_id=999, faction_name='The X-Men')
def make_generic_search_request(ModelClass: type, search_term: str): def make_generic_search_request(ModelClass: type, search_term: str):
User.objects.create_superuser( User.objects.create_superuser(
@ -188,7 +213,7 @@ def make_generic_search_request(ModelClass: type, search_term: str):
c = Client() c = Client()
c.login(username='superuser', password='secret') c.login(username='superuser', password='secret')
return c.get( return c.get(
'%s?q=%s' % (get_admin_search_url(ModelClass), quote(search_term)) f'{get_admin_search_url(ModelClass)}?q={quote(search_term)}'
) )
@ -315,9 +340,13 @@ class TestUserAdmin(TestCaseWithTestData):
self.assertEqual(user_main_organization(self.user_2), expected) self.assertEqual(user_main_organization(self.user_2), expected)
def test_user_main_organization_u3(self): def test_user_main_organization_u3(self):
expected = None expected = ''
self.assertEqual(user_main_organization(self.user_3), expected) self.assertEqual(user_main_organization(self.user_3), expected)
def test_user_main_organization_u4(self):
expected="Xavier's School for Gifted Youngsters<br>The X-Men"
self.assertEqual(user_main_organization(self.user_4), expected)
def test_characters_u1(self): def test_characters_u1(self):
expected = 'Batman, Bruce Wayne' expected = 'Batman, Bruce Wayne'
result = self.modeladmin._characters(self.user_1) result = self.modeladmin._characters(self.user_1)
@ -390,7 +419,7 @@ class TestUserAdmin(TestCaseWithTestData):
# actions # actions
@patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True) @patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True, unsafe=True)
@patch(MODULE_PATH + '.update_character') @patch(MODULE_PATH + '.update_character')
def test_action_update_main_character_model( def test_action_update_main_character_model(
self, mock_task, mock_message_user self, mock_task, mock_message_user
@ -420,6 +449,7 @@ class TestUserAdmin(TestCaseWithTestData):
expected = [ expected = [
(2002, 'Daily Planet'), (2002, 'Daily Planet'),
(2001, 'Wayne Technologies'), (2001, 'Wayne Technologies'),
(5432, "Xavier's School for Gifted Youngsters"),
] ]
self.assertEqual(filterspec.lookup_choices, expected) self.assertEqual(filterspec.lookup_choices, expected)
@ -463,6 +493,34 @@ class TestUserAdmin(TestCaseWithTestData):
expected = [self.user_1] expected = [self.user_1]
self.assertSetEqual(set(queryset), set(expected)) self.assertSetEqual(set(queryset), set(expected))
def test_filter_main_factions(self):
class UserAdminTest(BaseUserAdmin):
list_filter = (MainFactionFilter,)
my_modeladmin = UserAdminTest(User, AdminSite())
# Make sure the lookups are correct
request = self.factory.get('/')
request.user = self.user_4
changelist = my_modeladmin.get_changelist_instance(request)
filters = changelist.get_filters(request)
filterspec = filters[0][0]
expected = [
(999, 'The X-Men'),
]
self.assertEqual(filterspec.lookup_choices, expected)
# Make sure the correct queryset is returned
request = self.factory.get(
'/',
{'main_faction_id__exact': self.character_4.faction_id}
)
request.user = self.user_4
changelist = my_modeladmin.get_changelist_instance(request)
queryset = changelist.get_queryset(request)
expected = [self.user_4]
self.assertSetEqual(set(queryset), set(expected))
def test_change_view_loads_normally(self): def test_change_view_loads_normally(self):
User.objects.create_superuser( User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com' username='superuser', password='secret', email='admin@example.com'

View File

@ -114,12 +114,12 @@ class TestAuthenticate(TestCase):
def test_authenticate_main_character(self): def test_authenticate_main_character(self):
t = Token(character_id=self.main_character.character_id, character_owner_hash='1') t = Token(character_id=self.main_character.character_id, character_owner_hash='1')
user = StateBackend().authenticate(token=t) user = StateBackend().authenticate(token=t)
self.assertEquals(user, self.user) self.assertEqual(user, self.user)
def test_authenticate_alt_character(self): def test_authenticate_alt_character(self):
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2') t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
user = StateBackend().authenticate(token=t) user = StateBackend().authenticate(token=t)
self.assertEquals(user, self.user) self.assertEqual(user, self.user)
def test_authenticate_unclaimed_character(self): def test_authenticate_unclaimed_character(self):
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3') t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3')

View File

@ -4,7 +4,7 @@ from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\ from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
EveAllianceInfo EveAllianceInfo, EveFactionInfo
from allianceauth.tests.auth_utils import AuthUtils from allianceauth.tests.auth_utils import AuthUtils
from esi.errors import IncompleteResponseError from esi.errors import IncompleteResponseError
from esi.models import Token from esi.models import Token
@ -36,8 +36,8 @@ class CharacterOwnershipTestCase(TestCase):
character_owner_hash='1', character_owner_hash='1',
) )
co = CharacterOwnership.objects.get(character=self.character) co = CharacterOwnership.objects.get(character=self.character)
self.assertEquals(co.user, self.user) self.assertEqual(co.user, self.user)
self.assertEquals(co.owner_hash, '1') self.assertEqual(co.owner_hash, '1')
def test_transfer_ownership(self): def test_transfer_ownership(self):
Token.objects.create( Token.objects.create(
@ -54,7 +54,7 @@ class CharacterOwnershipTestCase(TestCase):
) )
co = CharacterOwnership.objects.get(character=self.character) co = CharacterOwnership.objects.get(character=self.character)
self.assertNotEqual(self.user, co.user) self.assertNotEqual(self.user, co.user)
self.assertEquals(self.alt_user, co.user) self.assertEqual(self.alt_user, co.user)
def test_clear_main_character(self): def test_clear_main_character(self):
Token.objects.create( Token.objects.create(
@ -80,13 +80,15 @@ class StateTestCase(TestCase):
def setUpTestData(cls): def setUpTestData(cls):
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True) cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1', AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1',
corp_name='Test Corp', alliance_name='Test Alliance') corp_name='Test Corp', alliance_name='Test Alliance', faction_id=1337,
faction_name='Permabanned')
cls.guest_state = get_guest_state() cls.guest_state = get_guest_state()
cls.test_character = EveCharacter.objects.get(character_id='1') cls.test_character = EveCharacter.objects.get(character_id='1')
cls.test_corporation = EveCorporationInfo.objects.create(corporation_id='1', corporation_name='Test Corp', cls.test_corporation = EveCorporationInfo.objects.create(corporation_id='1', corporation_name='Test Corp',
corporation_ticker='TEST', member_count=1) corporation_ticker='TEST', member_count=1)
cls.test_alliance = EveAllianceInfo.objects.create(alliance_id='1', alliance_name='Test Alliance', cls.test_alliance = EveAllianceInfo.objects.create(alliance_id='1', alliance_name='Test Alliance',
alliance_ticker='TEST', executor_corp_id='1') alliance_ticker='TEST', executor_corp_id='1')
cls.test_faction = EveFactionInfo.objects.create(faction_id=1337, faction_name='Permabanned')
cls.member_state = State.objects.create( cls.member_state = State.objects.create(
name='Test Member', name='Test Member',
priority=150, priority=150,
@ -98,29 +100,38 @@ class StateTestCase(TestCase):
def test_state_assignment_on_character_change(self): def test_state_assignment_on_character_change(self):
self.member_state.member_characters.add(self.test_character) self.member_state.member_characters.add(self.test_character)
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state) self.assertEqual(self.user.profile.state, self.member_state)
self.member_state.member_characters.remove(self.test_character) self.member_state.member_characters.remove(self.test_character)
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state) self.assertEqual(self.user.profile.state, self.guest_state)
def test_state_assignment_on_corporation_change(self): def test_state_assignment_on_corporation_change(self):
self.member_state.member_corporations.add(self.test_corporation) self.member_state.member_corporations.add(self.test_corporation)
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state) self.assertEqual(self.user.profile.state, self.member_state)
self.member_state.member_corporations.remove(self.test_corporation) self.member_state.member_corporations.remove(self.test_corporation)
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state) self.assertEqual(self.user.profile.state, self.guest_state)
def test_state_assignment_on_alliance_addition(self): def test_state_assignment_on_alliance_addition(self):
self.member_state.member_alliances.add(self.test_alliance) self.member_state.member_alliances.add(self.test_alliance)
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state) self.assertEqual(self.user.profile.state, self.member_state)
self.member_state.member_alliances.remove(self.test_alliance) self.member_state.member_alliances.remove(self.test_alliance)
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state) self.assertEqual(self.user.profile.state, self.guest_state)
def test_state_assignment_on_faction_change(self):
self.member_state.member_factions.add(self.test_faction)
self._refresh_user()
self.assertEqual(self.user.profile.state, self.member_state)
self.member_state.member_factions.remove(self.test_faction)
self._refresh_user()
self.assertEqual(self.user.profile.state, self.guest_state)
def test_state_assignment_on_higher_priority_state_creation(self): def test_state_assignment_on_higher_priority_state_creation(self):
self.member_state.member_characters.add(self.test_character) self.member_state.member_characters.add(self.test_character)
@ -130,10 +141,10 @@ class StateTestCase(TestCase):
) )
higher_state.member_characters.add(self.test_character) higher_state.member_characters.add(self.test_character)
self._refresh_user() self._refresh_user()
self.assertEquals(higher_state, self.user.profile.state) self.assertEqual(higher_state, self.user.profile.state)
higher_state.member_characters.clear() higher_state.member_characters.clear()
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
self.member_state.member_characters.clear() self.member_state.member_characters.clear()
def test_state_assignment_on_lower_priority_state_creation(self): def test_state_assignment_on_lower_priority_state_creation(self):
@ -144,10 +155,10 @@ class StateTestCase(TestCase):
) )
lower_state.member_characters.add(self.test_character) lower_state.member_characters.add(self.test_character)
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
lower_state.member_characters.clear() lower_state.member_characters.clear()
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
self.member_state.member_characters.clear() self.member_state.member_characters.clear()
def test_state_assignment_on_priority_change(self): def test_state_assignment_on_priority_change(self):
@ -161,11 +172,11 @@ class StateTestCase(TestCase):
lower_state.priority = 500 lower_state.priority = 500
lower_state.save() lower_state.save()
self._refresh_user() self._refresh_user()
self.assertEquals(lower_state, self.user.profile.state) self.assertEqual(lower_state, self.user.profile.state)
lower_state.priority = 125 lower_state.priority = 125
lower_state.save() lower_state.save()
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
def test_state_assignment_on_state_deletion(self): def test_state_assignment_on_state_deletion(self):
self.member_state.member_characters.add(self.test_character) self.member_state.member_characters.add(self.test_character)
@ -175,11 +186,11 @@ class StateTestCase(TestCase):
) )
higher_state.member_characters.add(self.test_character) higher_state.member_characters.add(self.test_character)
self._refresh_user() self._refresh_user()
self.assertEquals(higher_state, self.user.profile.state) self.assertEqual(higher_state, self.user.profile.state)
higher_state.delete() higher_state.delete()
self.assertFalse(State.objects.filter(name='Higher State').count()) self.assertFalse(State.objects.filter(name='Higher State').count())
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
def test_state_assignment_on_public_toggle(self): def test_state_assignment_on_public_toggle(self):
self.member_state.member_characters.add(self.test_character) self.member_state.member_characters.add(self.test_character)
@ -188,26 +199,26 @@ class StateTestCase(TestCase):
priority=200, priority=200,
) )
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
higher_state.public = True higher_state.public = True
higher_state.save() higher_state.save()
self._refresh_user() self._refresh_user()
self.assertEquals(higher_state, self.user.profile.state) self.assertEqual(higher_state, self.user.profile.state)
higher_state.public = False higher_state.public = False
higher_state.save() higher_state.save()
self._refresh_user() self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state) self.assertEqual(self.member_state, self.user.profile.state)
def test_state_assignment_on_active_changed(self): def test_state_assignment_on_active_changed(self):
self.member_state.member_characters.add(self.test_character) self.member_state.member_characters.add(self.test_character)
self.user.is_active = False self.user.is_active = False
self.user.save() self.user.save()
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state) self.assertEqual(self.user.profile.state, self.guest_state)
self.user.is_active = True self.user.is_active = True
self.user.save() self.user.save()
self._refresh_user() self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state) self.assertEqual(self.user.profile.state, self.member_state)
class CharacterOwnershipCheckTestCase(TestCase): class CharacterOwnershipCheckTestCase(TestCase):

View File

@ -1,6 +1,7 @@
from math import ceil from math import ceil
from unittest.mock import patch from unittest.mock import patch
import requests
import requests_mock import requests_mock
from packaging.version import Version as Pep440Version from packaging.version import Version as Pep440Version
@ -169,11 +170,7 @@ class TestVersionTags(TestCase):
# when # when
result = _current_version_summary() result = _current_version_summary()
# then # then
self.assertTrue(result['latest_major'])
self.assertTrue(result['latest_minor'])
self.assertTrue(result['latest_patch']) self.assertTrue(result['latest_patch'])
self.assertEqual(result['latest_major_version'], '2.0.0')
self.assertEqual(result['latest_minor_version'], '2.4.0')
self.assertEqual(result['latest_patch_version'], '2.4.5') self.assertEqual(result['latest_patch_version'], '2.4.5')
self.assertEqual(result['latest_beta_version'], '2.4.6a1') self.assertEqual(result['latest_beta_version'], '2.4.6a1')
@ -222,9 +219,7 @@ class TestLatestsVersion(TestCase):
tags = create_tags_list( tags = create_tags_list(
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0'] ['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
) )
major, minor, patch, beta = _latests_versions(tags) patch, beta = _latests_versions(tags)
self.assertEqual(major, Pep440Version('2.0.0'))
self.assertEqual(minor, Pep440Version('2.1.0'))
self.assertEqual(patch, Pep440Version('2.1.1')) self.assertEqual(patch, Pep440Version('2.1.1'))
self.assertEqual(beta, Pep440Version('2.1.1a1')) self.assertEqual(beta, Pep440Version('2.1.1a1'))
@ -233,9 +228,7 @@ class TestLatestsVersion(TestCase):
tags = create_tags_list( tags = create_tags_list(
['2.1.2', '2.1.1', '2.0.1', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0'] ['2.1.2', '2.1.1', '2.0.1', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
) )
major, minor, patch, beta = _latests_versions(tags) patch, beta = _latests_versions(tags)
self.assertEqual(major, Pep440Version('2.0.1'))
self.assertEqual(minor, Pep440Version('2.1.1'))
self.assertEqual(patch, Pep440Version('2.1.2')) self.assertEqual(patch, Pep440Version('2.1.2'))
self.assertEqual(beta, Pep440Version('2.1.1a1')) self.assertEqual(beta, Pep440Version('2.1.1a1'))
@ -244,9 +237,7 @@ class TestLatestsVersion(TestCase):
tags = create_tags_list( tags = create_tags_list(
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', 'invalid'] ['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', 'invalid']
) )
major, minor, patch, beta = _latests_versions(tags) patch, beta = _latests_versions(tags)
self.assertEqual(major, Pep440Version('2.0.0'))
self.assertEqual(minor, Pep440Version('2.1.0'))
self.assertEqual(patch, Pep440Version('2.1.1')) self.assertEqual(patch, Pep440Version('2.1.1'))
self.assertEqual(beta, Pep440Version('2.1.1a1')) self.assertEqual(beta, Pep440Version('2.1.1a1'))
@ -317,3 +308,25 @@ class TestFetchListFromGitlab(TestCase):
result = _fetch_list_from_gitlab(self.url, max_pages=max_pages) result = _fetch_list_from_gitlab(self.url, max_pages=max_pages)
self.assertEqual(result, GITHUB_TAGS[:4]) self.assertEqual(result, GITHUB_TAGS[:4])
self.assertEqual(requests_mocker.call_count, max_pages) self.assertEqual(requests_mocker.call_count, max_pages)
@requests_mock.mock()
@patch(MODULE_PATH + '.admin_status.logger')
def test_should_not_raise_any_exception_from_github_request_but_log_as_warning(
self, requests_mocker, mock_logger
):
for my_exception in [
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
requests.exceptions.URLRequired,
requests.exceptions.TooManyRedirects,
requests.exceptions.ConnectTimeout,
requests.exceptions.Timeout,
]:
requests_mocker.get(self.url, exc=my_exception)
try:
result = _fetch_list_from_gitlab(self.url)
except Exception as ex:
self.fail(f"Unexpected exception raised: {ex}")
self.assertTrue(mock_logger.warning.called)
self.assertListEqual(result, [])

View File

@ -6,8 +6,10 @@ from django.contrib.auth import login, authenticate
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core import signing from django.core import signing
from django.core.mail import EmailMultiAlternatives
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -63,7 +65,7 @@ def dashboard(request):
@login_required @login_required
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES) @token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
def main_character_change(request, token): def main_character_change(request, token):
logger.debug("main_character_change called by user %s for character %s" % (request.user, token.character_name)) logger.debug(f"main_character_change called by user {request.user} for character {token.character_name}")
try: try:
co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user) co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user)
except CharacterOwnership.DoesNotExist: except CharacterOwnership.DoesNotExist:
@ -137,9 +139,52 @@ class RegistrationView(BaseRegistrationView):
form_class = RegistrationForm form_class = RegistrationForm
template_name = "public/register.html" template_name = "public/register.html"
email_body_template = "registration/activation_email.txt" email_body_template = "registration/activation_email.txt"
email_body_template_html = "registration/activation_email_html.txt"
email_subject_template = "registration/activation_email_subject.txt" email_subject_template = "registration/activation_email_subject.txt"
success_url = reverse_lazy('registration_complete') success_url = reverse_lazy('registration_complete')
def send_activation_email(self, user):
"""
Implement our own way to send a mail to make sure we
send a RFC conform multipart email
:param user:
:type user:
"""
activation_key = self.get_activation_key(user)
context = self.get_email_context(activation_key)
context["user"] = user
# email subject
subject = render_to_string(
template_name=self.email_subject_template,
context=context,
request=self.request,
)
subject = "".join(subject.splitlines())
# plaintext email body part
message = render_to_string(
template_name=self.email_body_template,
context=context,
request=self.request,
)
# html email body part
message_html = render_to_string(
template_name=self.email_body_template_html,
context=context,
request=self.request,
)
# send it
user.email_user(
subject,
message,
settings.DEFAULT_FROM_EMAIL,
**{'html_message': message_html},
)
def get_success_url(self, user): def get_success_url(self, user):
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True): if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
return reverse_lazy('authentication:dashboard') return reverse_lazy('authentication:dashboard')
@ -154,7 +199,7 @@ class RegistrationView(BaseRegistrationView):
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True): if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
# Keep the request so the user can be automagically logged in. # Keep the request so the user can be automagically logged in.
setattr(self, 'request', request) setattr(self, 'request', request)
return super(RegistrationView, self).dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def register(self, form): def register(self, form):
user = User.objects.get(pk=self.request.session.get('registration_uid')) user = User.objects.get(pk=self.request.session.get('registration_uid'))
@ -173,7 +218,7 @@ class RegistrationView(BaseRegistrationView):
return signing.dumps(obj=[getattr(user, User.USERNAME_FIELD), user.email], salt=REGISTRATION_SALT) return signing.dumps(obj=[getattr(user, User.USERNAME_FIELD), user.email], salt=REGISTRATION_SALT)
def get_email_context(self, activation_key): def get_email_context(self, activation_key):
context = super(RegistrationView, self).get_email_context(activation_key) context = super().get_email_context(activation_key)
context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key]) context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key])
return context return context

View File

@ -41,7 +41,7 @@ def create_project(parser, options, args):
# Call the command with extra context # Call the command with extra context
call_command(StartProject(), *args, **command_options) call_command(StartProject(), *args, **command_options)
print("Success! %(project_name)s has been created." % {'project_name': args[0]}) # noqa print(f"Success! {args[0]} has been created.") # noqa
def update_settings(parser, options, args): def update_settings(parser, options, args):
@ -69,10 +69,10 @@ def update_settings(parser, options, args):
template_settings_path = os.path.join(template_path, 'project_name/settings/base.py') template_settings_path = os.path.join(template_path, 'project_name/settings/base.py')
# overwrite the local project's base settings # overwrite the local project's base settings
with open(template_settings_path, 'r') as template, open(settings_path, 'w') as target: with open(template_settings_path) as template, open(settings_path, 'w') as target:
target.write(template.read()) target.write(template.read())
print("Successfully updated %(project_name)s settings." % {'project_name': project_name}) print(f"Successfully updated {project_name} settings.")
COMMANDS = { COMMANDS = {

View File

@ -29,7 +29,7 @@ class CorpStatsQuerySet(models.QuerySet):
if user.has_perm('corputils.view_state_corpstats'): if user.has_perm('corputils.view_state_corpstats'):
queries.append(models.Q(corp__in=user.profile.state.member_corporations.all())) queries.append(models.Q(corp__in=user.profile.state.member_corporations.all()))
queries.append(models.Q(corp__alliance__in=user.profile.state.member_alliances.all())) queries.append(models.Q(corp__alliance__in=user.profile.state.member_alliances.all()))
logger.debug('%s queries for user %s visible corpstats.' % (len(queries), user)) logger.debug(f'{len(queries)} queries for user {user} visible corpstats.')
# filter based on queries # filter based on queries
query = queries.pop() query = queries.pop()
for q in queries: for q in queries:

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-12-14 21:36 # Generated by Django 1.10.1 on 2016-12-14 21:36
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-12-14 21:48 # Generated by Django 1.10.1 on 2016-12-14 21:48
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.5 on 2017-03-22 23:35 # Generated by Django 1.10.5 on 2017-03-22 23:35
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.5 on 2017-03-26 20:13 # Generated by Django 1.10.5 on 2017-03-26 20:13
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -48,7 +47,7 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='corpmember', name='corpmember',
unique_together=set([('corpstats', 'character_id')]), unique_together={('corpstats', 'character_id')},
), ),
migrations.RunPython(convert_json_to_members, convert_members_to_json), migrations.RunPython(convert_json_to_members, convert_members_to_json),
migrations.RemoveField( migrations.RemoveField(

View File

@ -1,5 +1,4 @@
# Generated by Django 1.11.2 on 2017-06-10 15:34 # Generated by Django 1.11.2 on 2017-06-10 15:34
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -43,7 +43,7 @@ class CorpStats(models.Model):
objects = CorpStatsManager() objects = CorpStatsManager()
def __str__(self): def __str__(self):
return "%s for %s" % (self.__class__.__name__, self.corp) return f"{self.__class__.__name__} for {self.corp}"
def update(self): def update(self):
try: try:
@ -73,7 +73,7 @@ class CorpStats(models.Model):
self.save() self.save()
except TokenError as e: except TokenError as e:
logger.warning("%s failed to update: %s" % (self, e)) logger.warning(f"{self} failed to update: {e}")
if self.token.user: if self.token.user:
notify( notify(
self.token.user, "%s failed to update with your ESI token." % self, self.token.user, "%s failed to update with your ESI token." % self,
@ -81,9 +81,9 @@ class CorpStats(models.Model):
level="error") level="error")
self.delete() self.delete()
except HTTPForbidden as e: except HTTPForbidden as e:
logger.warning("%s failed to update: %s" % (self, e)) logger.warning(f"{self} failed to update: {e}")
if self.token.user: if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self, message="%s: %s" % (e.status_code, e.message), level="error") notify(self.token.user, "%s failed to update with your ESI token." % self, message=f"{e.status_code}: {e.message}", level="error")
self.delete() self.delete()
except AssertionError: except AssertionError:
logger.warning("%s token character no longer in corp." % self) logger.warning("%s token character no longer in corp." % self)
@ -99,7 +99,7 @@ class CorpStats(models.Model):
@property @property
def user_count(self): def user_count(self):
return len(set([m.main_character for m in self.members.all() if m.main_character])) return len({m.main_character for m in self.members.all() if m.main_character})
@property @property
def registered_member_count(self): def registered_member_count(self):

View File

@ -1,15 +1,15 @@
{% extends 'allianceauth/base.html' %} {% extends 'allianceauth/base.html' %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Corporation Member Data" %}{% endblock %} {% block page_title %}{% translate "Corporation Member Data" %}{% endblock %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Corporation Member Data" %}</h1> <h1 class="page-header text-center">{% translate "Corporation Member Data" %}</h1>
<div class="col-lg-10 col-lg-offset-1 container"> <div class="col-lg-10 col-lg-offset-1 container">
<nav class="navbar navbar-default"> <nav class="navbar navbar-default">
<div class="container-fluid"> <div class="container-fluid">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="dropdown"> <li class="dropdown">
<a href="#" id="dLabel" class="dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">{% trans "Corporations" %}<span class="caret"></span></a> <a href="#" id="dLabel" class="dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">{% translate "Corporations" %}<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
{% for corpstat in available %} {% for corpstat in available %}
<li> <li>
@ -20,13 +20,13 @@
</li> </li>
{% if perms.corputils.add_corpstats %} {% if perms.corputils.add_corpstats %}
<li> <li>
<a href="{% url 'corputils:add' %}">{% trans "Add" %}</a> <a href="{% url 'corputils:add' %}">{% translate "Add" %}</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
<form class="navbar-form navbar-right" role="search" action="{% url 'corputils:search' %}" method="GET"> <form class="navbar-form navbar-right" role="search" action="{% url 'corputils:search' %}" method="GET">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" name="search_string" placeholder="{% if search_string %}{{ search_string }}{% else %}{% trans "Search all corporations..." %}{% endif %}"> <input type="text" class="form-control" name="search_string" placeholder="{% if search_string %}{{ search_string }}{% else %}{% translate "Search all corporations..." %}{% endif %}">
</div> </div>
</form> </form>
</div> </div>

View File

@ -30,12 +30,12 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<ul class="nav nav-pills pull-left"> <ul class="nav nav-pills pull-left">
<li class="active"><a href="#mains" data-toggle="pill">{% trans 'Mains' %} ({{ total_mains }})</a></li> <li class="active"><a href="#mains" data-toggle="pill">{% translate 'Mains' %} ({{ total_mains }})</a></li>
<li><a href="#members" data-toggle="pill">{% trans 'Members' %} ({{ corpstats.member_count }})</a></li> <li><a href="#members" data-toggle="pill">{% translate 'Members' %} ({{ corpstats.member_count }})</a></li>
<li><a href="#unregistered" data-toggle="pill">{% trans 'Unregistered' %} ({{ unregistered.count }})</a></li> <li><a href="#unregistered" data-toggle="pill">{% translate 'Unregistered' %} ({{ unregistered.count }})</a></li>
</ul> </ul>
<div class="pull-right hidden-xs"> <div class="pull-right hidden-xs">
{% trans "Last update:" %} {{ corpstats.last_update|naturaltime }}&nbsp; {% translate "Last update:" %} {{ corpstats.last_update|naturaltime }}&nbsp;
<a class="btn btn-success" type="button" href="{% url 'corputils:update' corpstats.corp.corporation_id %}" title="Update Now"> <a class="btn btn-success" type="button" href="{% url 'corputils:update' corpstats.corp.corporation_id %}" title="Update Now">
<span class="glyphicon glyphicon-refresh"></span> <span class="glyphicon glyphicon-refresh"></span>
</a> </a>
@ -71,9 +71,9 @@
{% if forloop.first %} {% if forloop.first %}
<tr> <tr>
<th></th> <th></th>
<th class="text-center">{% trans "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center">{% trans "Corporation" %}</th> <th class="text-center">{% translate "Corporation" %}</th>
<th class="text-center">{% trans "Alliance" %}</th> <th class="text-center">{% translate "Alliance" %}</th>
<th class="text-center"></th> <th class="text-center"></th>
</tr> </tr>
{% endif %} {% endif %}
@ -88,7 +88,7 @@
<td class="text-center" style="width:30%">{{ alt.alliance_name }}</td> <td class="text-center" style="width:30%">{{ alt.alliance_name }}</td>
<td class="text-center" style="width:5%"> <td class="text-center" style="width:5%">
<a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="label label-danger" target="_blank"> <a href="https://zkillboard.com/character/{{ alt.character_id }}/" class="label label-danger" target="_blank">
{% trans "Killboard" %} {% translate "Killboard" %}
</a> </a>
</td> </td>
</tr> </tr>
@ -109,11 +109,11 @@
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th class="text-center">{% trans "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center"></th> <th class="text-center"></th>
<th class="text-center">{% trans "Main Character" %}</th> <th class="text-center">{% translate "Main Character" %}</th>
<th class="text-center">{% trans "Main Corporation" %}</th> <th class="text-center">{% translate "Main Corporation" %}</th>
<th class="text-center">{% trans "Main Alliance" %}</th> <th class="text-center">{% translate "Main Alliance" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -122,7 +122,7 @@
<td><img src="{{ member.portrait_url }}" class="img-circle"></td> <td><img src="{{ member.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ member }}</td> <td class="text-center">{{ member }}</td>
<td class="text-center"> <td class="text-center">
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a> <a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a>
</td> </td>
<td class="text-center">{{ member.character_ownership.user.profile.main_character.character_name }}</td> <td class="text-center">{{ member.character_ownership.user.profile.main_character.character_name }}</td>
<td class="text-center">{{ member.character_ownership.user.profile.main_character.corporation_name }}</td> <td class="text-center">{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
@ -134,7 +134,7 @@
<td><img src="{{ member.portrait_url }}" class="img-circle"></td> <td><img src="{{ member.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ member.character_name }}</td> <td class="text-center">{{ member.character_name }}</td>
<td class="text-center"> <td class="text-center">
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a> <a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a>
</td> </td>
<td class="text-center"></td> <td class="text-center"></td>
<td class="text-center"></td> <td class="text-center"></td>
@ -153,7 +153,7 @@
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th class="text-center">{% trans "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center"></th> <th class="text-center"></th>
</tr> </tr>
</thead> </thead>
@ -164,7 +164,7 @@
<td class="text-center">{{ member.character_name }}</td> <td class="text-center">{{ member.character_name }}</td>
<td class="text-center"> <td class="text-center">
<a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank"> <a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">
{% trans "Killboard" %} {% translate "Killboard" %}
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -3,19 +3,19 @@
{% block member_data %} {% block member_data %}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading clearfix"> <div class="panel-heading clearfix">
<div class="panel-title pull-left">{% trans "Search Results" %}</div> <div class="panel-title pull-left">{% translate "Search Results" %}</div>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table table-hover" id="table-search"> <table class="table table-hover" id="table-search">
<thead> <thead>
<tr> <tr>
<th class="text-center"></th> <th class="text-center"></th>
<th class="text-center">{% trans "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center">{% trans "Corporation" %}</th> <th class="text-center">{% translate "Corporation" %}</th>
<th class="text-center">{% trans "zKillboard" %}</th> <th class="text-center">{% translate "zKillboard" %}</th>
<th class="text-center">{% trans "Main Character" %}</th> <th class="text-center">{% translate "Main Character" %}</th>
<th class="text-center">{% trans "Main Corporation" %}</th> <th class="text-center">{% translate "Main Corporation" %}</th>
<th class="text-center">{% trans "Main Alliance" %}</th> <th class="text-center">{% translate "Main Alliance" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -24,7 +24,7 @@
<td class="text-center"><img src="{{ result.1.portrait_url }}" class="img-circle"></td> <td class="text-center"><img src="{{ result.1.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ result.1.character_name }}</td> <td class="text-center">{{ result.1.character_name }}</td>
<td class="text-center">{{ result.0.corp.corporation_name }}</td> <td class="text-center">{{ result.0.corp.corporation_name }}</td>
<td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a></td> <td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="label label-danger" target="_blank">{% translate "Killboard" %}</a></td>
<td class="text-center">{{ result.1.main_character.character_name }}</td> <td class="text-center">{{ result.1.main_character.character_name }}</td>
<td class="text-center">{{ result.1.main_character.corporation_name }}</td> <td class="text-center">{{ result.1.main_character.corporation_name }}</td>
<td class="text-center">{{ result.1.main_character.alliance_name }}</td> <td class="text-center">{{ result.1.main_character.alliance_name }}</td>

View File

@ -243,7 +243,7 @@ class CorpMemberTestCase(TestCase):
CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b') CharacterOwnership.objects.create(character=character, user=self.user, owner_hash='b')
self.member.refresh_from_db() self.member.refresh_from_db()
self.assertNotEqual(self.member.main_character, self.member.character) self.assertNotEqual(self.member.main_character, self.member.character)
self.assertEquals(self.member.main_character, self.user.profile.main_character) self.assertEqual(self.member.main_character, self.user.profile.main_character)
# test when is main # test when is main
old_main = self.user.profile.main_character old_main = self.user.profile.main_character
@ -274,7 +274,7 @@ class CorpMemberTestCase(TestCase):
AuthUtils.connect_signals() AuthUtils.connect_signals()
def test_portrait_url(self): def test_portrait_url(self):
self.assertEquals(self.member.portrait_url(size=32), 'https://images.evetech.net/characters/2/portrait?size=32') self.assertEqual(self.member.portrait_url(size=32), 'https://images.evetech.net/characters/2/portrait?size=32')
self.assertEquals(self.member.portrait_url(size=32), self.member.portrait_url_32) self.assertEqual(self.member.portrait_url(size=32), self.member.portrait_url_32)
self.assertEquals(self.member.portrait_url(size=64), self.member.portrait_url_64) self.assertEqual(self.member.portrait_url(size=64), self.member.portrait_url_64)
self.assertEquals(self.member.portrait_url(size=128), self.member.portrait_url_128) self.assertEqual(self.member.portrait_url(size=128), self.member.portrait_url_128)

View File

@ -6,18 +6,19 @@ from .providers import ObjectNotFound
from .models import EveAllianceInfo from .models import EveAllianceInfo
from .models import EveCharacter from .models import EveCharacter
from .models import EveCorporationInfo from .models import EveCorporationInfo
from .models import EveFactionInfo
class EveEntityExistsError(forms.ValidationError): class EveEntityExistsError(forms.ValidationError):
def __init__(self, entity_type_name, id): def __init__(self, entity_type_name, id):
super(EveEntityExistsError, self).__init__( super().__init__(
message='{} with ID {} already exists.'.format(entity_type_name, id)) message=f'{entity_type_name} with ID {id} already exists.')
class EveEntityNotFoundError(forms.ValidationError): class EveEntityNotFoundError(forms.ValidationError):
def __init__(self, entity_type_name, id): def __init__(self, entity_type_name, id):
super(EveEntityNotFoundError, self).__init__( super().__init__(
message='{} with ID {} not found.'.format(entity_type_name, id)) message=f'{entity_type_name} with ID {id} not found.')
class EveEntityForm(forms.ModelForm): class EveEntityForm(forms.ModelForm):
@ -34,6 +35,38 @@ class EveEntityForm(forms.ModelForm):
pass pass
def get_faction_choices():
# use a method to avoid making an ESI call when the app loads
# restrict to only those factions a player can join for faction warfare
player_factions = [x for x in EveFactionInfo.provider.get_all_factions() if x['militia_corporation_id']]
return [(x['faction_id'], x['name']) for x in player_factions]
class EveFactionForm(EveEntityForm):
id = forms.ChoiceField(
choices=get_faction_choices,
label="Name"
)
def clean_id(self):
try:
assert self.Meta.model.provider.get_faction(self.cleaned_data['id'])
except (AssertionError, ObjectNotFound):
raise EveEntityNotFoundError('faction', self.cleaned_data['id'])
if self.Meta.model.objects.filter(faction_id=self.cleaned_data['id']).exists():
raise EveEntityExistsError('faction', self.cleaned_data['id'])
return self.cleaned_data['id']
def save(self, commit=True):
faction = self.Meta.model.provider.get_faction(self.cleaned_data['id'])
return self.Meta.model.objects.create(faction_id=faction.id, faction_name=faction.name)
class Meta:
fields = ['id']
model = EveFactionInfo
class EveCharacterForm(EveEntityForm): class EveCharacterForm(EveEntityForm):
entity_type_name = 'character' entity_type_name = 'character'
@ -94,6 +127,21 @@ class EveAllianceForm(EveEntityForm):
model = EveAllianceInfo model = EveAllianceInfo
@admin.register(EveFactionInfo)
class EveFactionInfoAdmin(admin.ModelAdmin):
search_fields = ['faction_name']
list_display = ('faction_name',)
ordering = ('faction_name',)
def has_change_permission(self, request, obj=None):
return False
def get_form(self, request, obj=None, **kwargs):
if not obj or not obj.pk:
return EveFactionForm
return super().get_form(request, obj=obj, **kwargs)
@admin.register(EveCorporationInfo) @admin.register(EveCorporationInfo)
class EveCorporationInfoAdmin(admin.ModelAdmin): class EveCorporationInfoAdmin(admin.ModelAdmin):
search_fields = ['corporation_name'] search_fields = ['corporation_name']
@ -135,7 +183,7 @@ class EveCharacterAdmin(admin.ModelAdmin):
'character_ownership__user__username' 'character_ownership__user__username'
] ]
list_display = ( list_display = (
'character_name', 'corporation_name', 'alliance_name', 'user', 'main_character' 'character_name', 'corporation_name', 'alliance_name', 'faction_name', 'user', 'main_character'
) )
list_select_related = ( list_select_related = (
'character_ownership', 'character_ownership__user__profile__main_character' 'character_ownership', 'character_ownership__user__profile__main_character'
@ -143,6 +191,7 @@ class EveCharacterAdmin(admin.ModelAdmin):
list_filter = ( list_filter = (
'corporation_name', 'corporation_name',
'alliance_name', 'alliance_name',
'faction_name',
( (
'character_ownership__user__profile__main_character', 'character_ownership__user__profile__main_character',
admin.RelatedOnlyFieldListFilter admin.RelatedOnlyFieldListFilter
@ -170,4 +219,4 @@ class EveCharacterAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
if not obj or not obj.pk: if not obj or not obj.pk:
return EveCharacterForm return EveCharacterForm
return super(EveCharacterAdmin, self).get_form(request, obj=obj, **kwargs) return super().get_form(request, obj=obj, **kwargs)

View File

@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
def sync_user_groups(modeladmin, request, queryset): def sync_user_groups(modeladmin, request, queryset):
for agc in queryset: for agc in queryset:
logger.debug("update_all_states_group_membership for {}".format(agc)) logger.debug(f"update_all_states_group_membership for {agc}")
agc.update_all_states_group_membership() agc.update_all_states_group_membership()
@ -29,7 +29,7 @@ class AutogroupsConfigAdmin(admin.ModelAdmin):
return [] return []
def get_actions(self, request): def get_actions(self, request):
actions = super(AutogroupsConfigAdmin, self).get_actions(request) actions = super().get_actions(request)
actions['sync_user_groups'] = (sync_user_groups, actions['sync_user_groups'] = (sync_user_groups,
'sync_user_groups', 'sync_user_groups',
'Sync all users groups for this Autogroup Config') 'Sync all users groups for this Autogroup Config')

View File

@ -1,5 +1,4 @@
# Generated by Django 1.11.6 on 2017-12-23 04:30 # Generated by Django 1.11.6 on 2017-12-23 04:30
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion

View File

@ -26,7 +26,7 @@ class AutogroupsConfigManager(models.Manager):
for config in self.filter(states=state): for config in self.filter(states=state):
logger.debug("in state loop") logger.debug("in state loop")
for user in users: for user in users:
logger.debug("in user loop for {}".format(user)) logger.debug(f"in user loop for {user}")
config.update_group_membership_for_user(user) config.update_group_membership_for_user(user)
def update_groups_for_user(self, user: User, state: State = None): def update_groups_for_user(self, user: User, state: State = None):
@ -81,7 +81,7 @@ class AutogroupsConfig(models.Model):
objects = AutogroupsConfigManager() objects = AutogroupsConfigManager()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AutogroupsConfig, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def __repr__(self): def __repr__(self):
return self.__class__.__name__ return self.__class__.__name__
@ -111,24 +111,24 @@ class AutogroupsConfig(models.Model):
group = None group = None
try: try:
if not self.alliance_groups or not self.user_entitled_to_groups(user): if not self.alliance_groups or not self.user_entitled_to_groups(user):
logger.debug('User {} does not have required state for alliance group membership'.format(user)) logger.debug(f'User {user} does not have required state for alliance group membership')
return return
else: else:
alliance = user.profile.main_character.alliance alliance = user.profile.main_character.alliance
if alliance is None: if alliance is None:
logger.debug('User {} alliance is None, cannot update group membership'.format(user)) logger.debug(f'User {user} alliance is None, cannot update group membership')
return return
group = self.get_alliance_group(alliance) group = self.get_alliance_group(alliance)
except EveAllianceInfo.DoesNotExist: except EveAllianceInfo.DoesNotExist:
logger.debug('User {} main characters alliance does not exist in the database. Creating.'.format(user)) logger.debug(f'User {user} main characters alliance does not exist in the database. Creating.')
alliance = EveAllianceInfo.objects.create_alliance(user.profile.main_character.alliance_id) alliance = EveAllianceInfo.objects.create_alliance(user.profile.main_character.alliance_id)
group = self.get_alliance_group(alliance) group = self.get_alliance_group(alliance)
except AttributeError: except AttributeError:
logger.warning('User {} does not have a main character. Group membership not updated'.format(user)) logger.warning(f'User {user} does not have a main character. Group membership not updated')
finally: finally:
self.remove_user_from_alliance_groups(user, except_group=group) self.remove_user_from_alliance_groups(user, except_group=group)
if group is not None: if group is not None:
logger.debug('Adding user {} to alliance group {}'.format(user, group)) logger.debug(f'Adding user {user} to alliance group {group}')
user.groups.add(group) user.groups.add(group)
@transaction.atomic @transaction.atomic
@ -136,20 +136,20 @@ class AutogroupsConfig(models.Model):
group = None group = None
try: try:
if not self.corp_groups or not self.user_entitled_to_groups(user): if not self.corp_groups or not self.user_entitled_to_groups(user):
logger.debug('User {} does not have required state for corp group membership'.format(user)) logger.debug(f'User {user} does not have required state for corp group membership')
else: else:
corp = user.profile.main_character.corporation corp = user.profile.main_character.corporation
group = self.get_corp_group(corp) group = self.get_corp_group(corp)
except EveCorporationInfo.DoesNotExist: except EveCorporationInfo.DoesNotExist:
logger.debug('User {} main characters corporation does not exist in the database. Creating.'.format(user)) logger.debug(f'User {user} main characters corporation does not exist in the database. Creating.')
corp = EveCorporationInfo.objects.create_corporation(user.profile.main_character.corporation_id) corp = EveCorporationInfo.objects.create_corporation(user.profile.main_character.corporation_id)
group = self.get_corp_group(corp) group = self.get_corp_group(corp)
except AttributeError: except AttributeError:
logger.warning('User {} does not have a main character. Group membership not updated'.format(user)) logger.warning(f'User {user} does not have a main character. Group membership not updated')
finally: finally:
self.remove_user_from_corp_groups(user, except_group=group) self.remove_user_from_corp_groups(user, except_group=group)
if group is not None: if group is not None:
logger.debug('Adding user {} to corp group {}'.format(user, group)) logger.debug(f'Adding user {user} to corp group {group}')
user.groups.add(group) user.groups.add(group)
@transaction.atomic @transaction.atomic

View File

@ -15,7 +15,7 @@ def pre_save_config(sender, instance, *args, **kwargs):
Checks if enable was toggled on group config and Checks if enable was toggled on group config and
deletes groups if necessary. deletes groups if necessary.
""" """
logger.debug("Received pre_save from {}".format(instance)) logger.debug(f"Received pre_save from {instance}")
if not instance.pk: if not instance.pk:
# new model being created # new model being created
return return

View File

@ -9,7 +9,7 @@ MODULE_PATH = 'allianceauth.eveonline.autogroups'
def patch(target, *args, **kwargs): def patch(target, *args, **kwargs):
return mock.patch('{}{}'.format(MODULE_PATH, target), *args, **kwargs) return mock.patch(f'{MODULE_PATH}{target}', *args, **kwargs)
def connect_signals(): def connect_signals():

View File

@ -56,15 +56,15 @@ def _eve_entity_image_url(
tenants = ['tranquility', 'singularity'] tenants = ['tranquility', 'singularity']
if not entity_id: if not entity_id:
raise ValueError('Invalid entity_id: {}'.format(entity_id)) raise ValueError(f'Invalid entity_id: {entity_id}')
else: else:
entity_id = int(entity_id) entity_id = int(entity_id)
if not size or size < 32 or size > 1024 or (size & (size - 1) != 0): if not size or size < 32 or size > 1024 or (size & (size - 1) != 0):
raise ValueError('Invalid size: {}'.format(size)) raise ValueError(f'Invalid size: {size}')
if category not in categories: if category not in categories:
raise ValueError('Invalid category {}'.format(category)) raise ValueError(f'Invalid category {category}')
else: else:
endpoint = categories[category]['endpoint'] endpoint = categories[category]['endpoint']
@ -78,7 +78,7 @@ def _eve_entity_image_url(
variant = categories[category]['variants'][0] variant = categories[category]['variants'][0]
if tenant and tenant not in tenants: if tenant and tenant not in tenants:
raise ValueError('Invalid tenant {}'.format(tenant)) raise ValueError(f'Invalid tenant {tenant}')
# compose result URL # compose result URL
result = '{}/{}/{}/{}?size={}'.format( result = '{}/{}/{}/{}?size={}'.format(
@ -89,7 +89,7 @@ def _eve_entity_image_url(
size size
) )
if tenant: if tenant:
result += '&tenant={}'.format(tenant) result += f'&tenant={tenant}'
return result return result

View File

@ -31,7 +31,7 @@ def _build_url(category: str, eve_id: int) -> str:
url = urljoin( url = urljoin(
_BASE_URL, _BASE_URL,
'{}/{}'.format(partial, int(eve_id)) f'{partial}/{int(eve_id)}'
) )
return url return url

View File

@ -39,7 +39,7 @@ def _build_url(category: str, eve_id: int) -> str:
url = urljoin( url = urljoin(
_BASE_URL, _BASE_URL,
'{}/{}/'.format(partial, int(eve_id)) f'{partial}/{int(eve_id)}/'
) )
return url return url

View File

@ -27,6 +27,8 @@ class EveCharacterManager(models.Manager):
alliance_id=character.alliance.id, alliance_id=character.alliance.id,
alliance_name=character.alliance.name, alliance_name=character.alliance.name,
alliance_ticker=getattr(character.alliance, 'ticker', None), alliance_ticker=getattr(character.alliance, 'ticker', None),
faction_id=character.faction.id,
faction_name=character.faction.name
) )
def update_character(self, character_id): def update_character(self, character_id):

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-05 21:39 # Generated by Django 1.10.1 on 2016-09-05 21:39
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-10 20:20 # Generated by Django 1.10.1 on 2016-09-10 20:20
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.2 on 2016-10-26 01:49 # Generated by Django 1.10.2 on 2016-10-26 01:49
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.2 on 2016-11-01 04:20 # Generated by Django 1.10.2 on 2016-11-01 04:20
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-12-16 23:22 # Generated by Django 1.10.1 on 2016-12-16 23:22
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2017-01-02 19:23 # Generated by Django 1.10.1 on 2017-01-02 19:23
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,11 +1,10 @@
# Generated by Django 1.10.5 on 2017-01-18 13:20 # Generated by Django 1.10.5 on 2017-01-18 13:20
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
def get_duplicates(items): def get_duplicates(items):
return set([item for item in items if items.count(item) > 1]) return {item for item in items if items.count(item) > 1}
def enforce_unique_characters(apps, schema_editor): def enforce_unique_characters(apps, schema_editor):

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.5 on 2017-03-22 23:09 # Generated by Django 1.10.5 on 2017-03-22 23:09
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,5 +1,4 @@
# Generated by Django 1.11.5 on 2017-09-28 02:16 # Generated by Django 1.11.5 on 2017-09-28 02:16
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion

View File

@ -0,0 +1,35 @@
# Generated by Django 3.1.13 on 2021-10-12 01:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0014_auto_20210105_1413'),
]
operations = [
migrations.CreateModel(
name='EveFactionInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('faction_id', models.PositiveIntegerField(db_index=True, unique=True)),
('faction_name', models.CharField(max_length=254, unique=True)),
],
),
migrations.AddField(
model_name='evecharacter',
name='faction_id',
field=models.PositiveIntegerField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='evecharacter',
name='faction_name',
field=models.CharField(blank=True, default='', max_length=254, null=True),
),
migrations.AddIndex(
model_name='evecharacter',
index=models.Index(fields=['faction_id'], name='eveonline_e_faction_d5274e_idx'),
),
]

View File

@ -10,6 +10,47 @@ from .evelinks import eveimageserver
_DEFAULT_IMAGE_SIZE = 32 _DEFAULT_IMAGE_SIZE = 32
class EveFactionInfo(models.Model):
faction_id = models.PositiveIntegerField(unique=True, db_index=True)
faction_name = models.CharField(max_length=254, unique=True)
provider = providers.provider
def __str__(self):
return self.faction_name
@staticmethod
def generic_logo_url(
faction_id: int, size: int = _DEFAULT_IMAGE_SIZE
) -> str:
"""image URL for the given faction ID"""
return eveimageserver.corporation_logo_url(faction_id, size)
def logo_url(self, size: int = _DEFAULT_IMAGE_SIZE) -> str:
"""image URL of this faction"""
return self.generic_logo_url(self.faction_id, size)
@property
def logo_url_32(self) -> str:
"""image URL for this faction"""
return self.logo_url(32)
@property
def logo_url_64(self) -> str:
"""image URL for this faction"""
return self.logo_url(64)
@property
def logo_url_128(self) -> str:
"""image URL for this faction"""
return self.logo_url(128)
@property
def logo_url_256(self) -> str:
"""image URL for this faction"""
return self.logo_url(256)
class EveAllianceInfo(models.Model): class EveAllianceInfo(models.Model):
alliance_id = models.PositiveIntegerField(unique=True) alliance_id = models.PositiveIntegerField(unique=True)
alliance_name = models.CharField(max_length=254, unique=True) alliance_name = models.CharField(max_length=254, unique=True)
@ -149,6 +190,8 @@ class EveCharacter(models.Model):
alliance_id = models.PositiveIntegerField(blank=True, null=True, default=None) alliance_id = models.PositiveIntegerField(blank=True, null=True, default=None)
alliance_name = models.CharField(max_length=254, blank=True, null=True, default='') alliance_name = models.CharField(max_length=254, blank=True, null=True, default='')
alliance_ticker = models.CharField(max_length=5, blank=True, null=True, default='') alliance_ticker = models.CharField(max_length=5, blank=True, null=True, default='')
faction_id = models.PositiveIntegerField(blank=True, null=True, default=None)
faction_name = models.CharField(max_length=254, blank=True, null=True, default='')
objects = EveCharacterManager() objects = EveCharacterManager()
provider = EveCharacterProviderManager() provider = EveCharacterProviderManager()
@ -159,6 +202,7 @@ class EveCharacter(models.Model):
models.Index(fields=['alliance_id',]), models.Index(fields=['alliance_id',]),
models.Index(fields=['corporation_name',]), models.Index(fields=['corporation_name',]),
models.Index(fields=['alliance_name',]), models.Index(fields=['alliance_name',]),
models.Index(fields=['faction_id',]),
] ]
@property @property
@ -181,6 +225,17 @@ class EveCharacter(models.Model):
""" """
return EveCorporationInfo.objects.get(corporation_id=self.corporation_id) return EveCorporationInfo.objects.get(corporation_id=self.corporation_id)
@property
def faction(self) -> Union[EveFactionInfo, None]:
"""
Pseudo foreign key from faction_id to EveFactionInfo
:raises: EveFactionInfo.DoesNotExist
:return: EveFactionInfo
"""
if self.faction_id is None:
return None
return EveFactionInfo.objects.get(faction_id=self.faction_id)
def update_character(self, character: providers.Character = None): def update_character(self, character: providers.Character = None):
if character is None: if character is None:
character = self.provider.get_character(self.character_id) character = self.provider.get_character(self.character_id)
@ -191,6 +246,8 @@ class EveCharacter(models.Model):
self.alliance_id = character.alliance.id self.alliance_id = character.alliance.id
self.alliance_name = character.alliance.name self.alliance_name = character.alliance.name
self.alliance_ticker = getattr(character.alliance, 'ticker', None) self.alliance_ticker = getattr(character.alliance, 'ticker', None)
self.faction_id = character.faction.id
self.faction_name = character.faction.name
self.save() self.save()
return self return self
@ -278,3 +335,31 @@ class EveCharacter(models.Model):
def alliance_logo_url_256(self) -> str: def alliance_logo_url_256(self) -> str:
"""image URL for alliance of this character or empty string""" """image URL for alliance of this character or empty string"""
return self.alliance_logo_url(256) return self.alliance_logo_url(256)
def faction_logo_url(self, size=_DEFAULT_IMAGE_SIZE) -> str:
"""image URL for alliance of this character or empty string"""
if self.faction_id:
return EveFactionInfo.generic_logo_url(self.faction_id, size)
else:
return ''
@property
def faction_logo_url_32(self) -> str:
"""image URL for alliance of this character or empty string"""
return self.faction_logo_url(32)
@property
def faction_logo_url_64(self) -> str:
"""image URL for alliance of this character or empty string"""
return self.faction_logo_url(64)
@property
def faction_logo_url_128(self) -> str:
"""image URL for alliance of this character or empty string"""
return self.faction_logo_url(128)
@property
def faction_logo_url_256(self) -> str:
"""image URL for alliance of this character or empty string"""
return self.faction_logo_url(256)

View File

@ -22,6 +22,7 @@ get_corporations_corporation_id
get_characters_character_id get_characters_character_id
get_universe_types_type_id get_universe_types_type_id
post_character_affiliation post_character_affiliation
get_universe_factions
""" """
@ -34,11 +35,12 @@ class ObjectNotFound(Exception):
self.type = type_name self.type = type_name
def __str__(self): def __str__(self):
return '%s with ID %s not found.' % (self.type, self.id) return f'{self.type} with ID {self.id} not found.'
class Entity(object): class Entity:
def __init__(self, id=None, name=None): def __init__(self, id=None, name=None, **kwargs):
super().__init__(**kwargs)
self.id = id self.id = id
self.name = name self.name = name
@ -46,7 +48,7 @@ class Entity(object):
return self.name return self.name
def __repr__(self): def __repr__(self):
return "<{} ({}): {}>".format(self.__class__.__name__, self.id, self.name) return f"<{self.__class__.__name__} ({self.id}): {self.name}>"
def __bool__(self): def __bool__(self):
return bool(self.id) return bool(self.id)
@ -55,15 +57,11 @@ class Entity(object):
return self.id == other.id return self.id == other.id
class Corporation(Entity): class AllianceMixin:
def __init__(self, ticker=None, ceo_id=None, members=None, alliance_id=None, **kwargs): def __init__(self, alliance_id=None, **kwargs):
super(Corporation, self).__init__(**kwargs) super().__init__(**kwargs)
self.ticker = ticker
self.ceo_id = ceo_id
self.members = members
self.alliance_id = alliance_id self.alliance_id = alliance_id
self._alliance = None self._alliance = None
self._ceo = None
@property @property
def alliance(self): def alliance(self):
@ -73,6 +71,30 @@ class Corporation(Entity):
return self._alliance return self._alliance
return Entity(None, None) return Entity(None, None)
class FactionMixin:
def __init__(self, faction_id=None, **kwargs):
super().__init__(**kwargs)
self.faction_id = faction_id
self._faction = None
@property
def faction(self):
if self.faction_id:
if not self._faction:
self._faction = provider.get_faction(self.faction_id)
return self._faction
return Entity(None, None)
class Corporation(Entity, AllianceMixin, FactionMixin):
def __init__(self, ticker=None, ceo_id=None, members=None, **kwargs):
super().__init__(**kwargs)
self.ticker = ticker
self.ceo_id = ceo_id
self.members = members
self._ceo = None
@property @property
def ceo(self): def ceo(self):
if not self._ceo: if not self._ceo:
@ -80,9 +102,9 @@ class Corporation(Entity):
return self._ceo return self._ceo
class Alliance(Entity): class Alliance(Entity, FactionMixin):
def __init__(self, ticker=None, corp_ids=None, executor_corp_id=None, **kwargs): def __init__(self, ticker=None, corp_ids=None, executor_corp_id=None, **kwargs):
super(Alliance, self).__init__(**kwargs) super().__init__(**kwargs)
self.ticker = ticker self.ticker = ticker
self.corp_ids = corp_ids self.corp_ids = corp_ids
self.executor_corp_id = executor_corp_id self.executor_corp_id = executor_corp_id
@ -97,7 +119,7 @@ class Alliance(Entity):
@property @property
def corps(self): def corps(self):
return sorted([self.corp(c_id) for c_id in self.corp_ids], key=lambda x: x.name) return sorted((self.corp(c_id) for c_id in self.corp_ids), key=lambda x: x.name)
@property @property
def executor_corp(self): def executor_corp(self):
@ -106,13 +128,11 @@ class Alliance(Entity):
return Entity(None, None) return Entity(None, None)
class Character(Entity): class Character(Entity, AllianceMixin, FactionMixin):
def __init__(self, corp_id=None, alliance_id=None, **kwargs): def __init__(self, corp_id=None, **kwargs):
super(Character, self).__init__(**kwargs) super().__init__(**kwargs)
self.corp_id = corp_id self.corp_id = corp_id
self.alliance_id = alliance_id
self._corp = None self._corp = None
self._alliance = None
@property @property
def corp(self): def corp(self):
@ -120,19 +140,13 @@ class Character(Entity):
self._corp = provider.get_corp(self.corp_id) self._corp = provider.get_corp(self.corp_id)
return self._corp return self._corp
@property
def alliance(self):
if self.alliance_id:
return self.corp.alliance
return Entity(None, None)
class ItemType(Entity): class ItemType(Entity):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(ItemType, self).__init__(**kwargs) super().__init__(**kwargs)
class EveProvider(object): class EveProvider:
def get_alliance(self, alliance_id): def get_alliance(self, alliance_id):
""" """
:return: an Alliance object for the given ID :return: an Alliance object for the given ID
@ -179,6 +193,7 @@ class EveSwaggerProvider(EveProvider):
self._token = token self._token = token
self.adapter = adapter or self self.adapter = adapter or self
self._faction_list = None # what are the odds this will change? could cache forever!
@property @property
def client(self): def client(self):
@ -201,6 +216,7 @@ class EveSwaggerProvider(EveProvider):
ticker=data['ticker'], ticker=data['ticker'],
corp_ids=corps, corp_ids=corps,
executor_corp_id=data['executor_corporation_id'] if 'executor_corporation_id' in data else None, executor_corp_id=data['executor_corporation_id'] if 'executor_corporation_id' in data else None,
faction_id=data['faction_id'] if 'faction_id' in data else None,
) )
return model return model
except HTTPNotFound: except HTTPNotFound:
@ -216,6 +232,7 @@ class EveSwaggerProvider(EveProvider):
ceo_id=data['ceo_id'], ceo_id=data['ceo_id'],
members=data['member_count'], members=data['member_count'],
alliance_id=data['alliance_id'] if 'alliance_id' in data else None, alliance_id=data['alliance_id'] if 'alliance_id' in data else None,
faction_id=data['faction_id'] if 'faction_id' in data else None,
) )
return model return model
except HTTPNotFound: except HTTPNotFound:
@ -231,11 +248,30 @@ class EveSwaggerProvider(EveProvider):
name=data['name'], name=data['name'],
corp_id=affiliation['corporation_id'], corp_id=affiliation['corporation_id'],
alliance_id=affiliation['alliance_id'] if 'alliance_id' in affiliation else None, alliance_id=affiliation['alliance_id'] if 'alliance_id' in affiliation else None,
faction_id=affiliation['faction_id'] if 'faction_id' in affiliation else None,
) )
return model return model
except (HTTPNotFound, HTTPUnprocessableEntity): except (HTTPNotFound, HTTPUnprocessableEntity):
raise ObjectNotFound(character_id, 'character') raise ObjectNotFound(character_id, 'character')
def get_all_factions(self):
if not self._faction_list:
self._faction_list = self.client.Universe.get_universe_factions().result()
return self._faction_list
def get_faction(self, faction_id):
faction_id=int(faction_id)
try:
if not self._faction_list:
_ = self.get_all_factions()
for f in self._faction_list:
if f['faction_id'] == faction_id:
return Entity(id=f['faction_id'], name=f['name'])
else:
raise KeyError()
except (HTTPNotFound, HTTPUnprocessableEntity, KeyError):
raise ObjectNotFound(faction_id, 'faction')
def get_itemtype(self, type_id): def get_itemtype(self, type_id):
try: try:
data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result() data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result()

File diff suppressed because one or more lines are too long

View File

@ -67,7 +67,7 @@ def update_character_chunk(character_ids_chunk: list):
character_names = providers.provider.client.Universe\ character_names = providers.provider.client.Universe\
.post_universe_names(ids=character_ids_chunk).result() .post_universe_names(ids=character_ids_chunk).result()
except: except:
logger.error("Failed to bulk update characters. Attempting single updates") logger.info("Failed to bulk update characters. Attempting single updates")
for character_id in character_ids_chunk: for character_id in character_ids_chunk:
update_character.apply_async( update_character.apply_async(
args=[character_id], priority=TASK_PRIORITY args=[character_id], priority=TASK_PRIORITY

View File

@ -18,7 +18,7 @@ def set_logger(logger_name: str, name: str) -> object:
'%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s' '%(asctime)s - %(levelname)s - %(module)s:%(funcName)s - %(message)s'
) )
f_handler = logging.FileHandler( f_handler = logging.FileHandler(
'{}.log'.format(os.path.splitext(name)[0]), f'{os.path.splitext(name)[0]}.log',
'w+' 'w+'
) )
f_handler.setFormatter(f_format) f_handler.setFormatter(f_format)

View File

@ -3,7 +3,7 @@ from unittest.mock import Mock, patch
from django.test import TestCase from django.test import TestCase
from ..models import ( from ..models import (
EveCharacter, EveCorporationInfo, EveAllianceInfo EveCharacter, EveCorporationInfo, EveAllianceInfo, EveFactionInfo
) )
from ..providers import Alliance, Corporation, Character from ..providers import Alliance, Corporation, Character
from ..evelinks import eveimageserver from ..evelinks import eveimageserver
@ -126,6 +126,66 @@ class EveCharacterTestCase(TestCase):
self.assertIsNone(character.alliance) self.assertIsNone(character.alliance)
def test_faction_prop(self):
"""
Test that the correct faction 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='cc1',
alliance_id=3456,
alliance_name='character.alliance.name',
faction_id=1337,
faction_name='character.faction.name'
)
expected = EveFactionInfo.objects.create(faction_id=1337, faction_name='faction.name')
incorrect = EveFactionInfo.objects.create(faction_id=8008, faction_name='faction.badname')
self.assertEqual(character.faction, expected)
self.assertNotEqual(character.faction, incorrect)
def test_faction_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='cc1',
alliance_id=3456,
alliance_name='character.alliance.name',
faction_id=1337,
faction_name='character.faction.name'
)
with self.assertRaises(EveFactionInfo.DoesNotExist):
character.faction
def test_faction_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='cc1',
alliance_id=None,
alliance_name=None,
faction_id=None,
faction_name=None,
)
self.assertIsNone(character.faction)
@patch('allianceauth.eveonline.providers.provider') @patch('allianceauth.eveonline.providers.provider')
def test_update_character(self, mock_provider): def test_update_character(self, mock_provider):
mock_provider.get_corp.return_value = Corporation( mock_provider.get_corp.return_value = Corporation(
@ -144,13 +204,17 @@ class EveCharacterTestCase(TestCase):
corporation_ticker='DC1', corporation_ticker='DC1',
alliance_id=3001, alliance_id=3001,
alliance_name='Dummy Alliance 1', alliance_name='Dummy Alliance 1',
faction_id=1337,
faction_name='Dummy Faction 1',
) )
my_updated_character = Character( my_updated_character = Character(
name='Bruce X. Wayne', name='Bruce X. Wayne',
corp_id=2002 corp_id=2002,
faction_id=None,
) )
my_character.update_character(my_updated_character) my_character.update_character(my_updated_character)
self.assertEqual(my_character.character_name, 'Bruce X. Wayne') self.assertEqual(my_character.character_name, 'Bruce X. Wayne')
self.assertFalse(my_character.faction_id)
# todo: add test cases not yet covered, e.g. with alliance # todo: add test cases not yet covered, e.g. with alliance

View File

@ -10,6 +10,8 @@ from . import set_logger
from ..providers import ( from ..providers import (
ObjectNotFound, ObjectNotFound,
Entity, Entity,
AllianceMixin,
FactionMixin,
Character, Character,
Corporation, Corporation,
Alliance, Alliance,
@ -18,7 +20,6 @@ from ..providers import (
EveSwaggerProvider EveSwaggerProvider
) )
MODULE_PATH = 'allianceauth.eveonline.providers' MODULE_PATH = 'allianceauth.eveonline.providers'
SWAGGER_OLD_SPEC_PATH = os.path.join(os.path.dirname( SWAGGER_OLD_SPEC_PATH = os.path.join(os.path.dirname(
os.path.abspath(__file__)), 'swagger_old.json' os.path.abspath(__file__)), 'swagger_old.json'
@ -80,6 +81,72 @@ class TestEntity(TestCase):
# bug: missing _neq_ in Equity to compliment _eq_ # bug: missing _neq_ in Equity to compliment _eq_
class TestAllianceMixin(TestCase):
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
def test_alliance_defined(self, mock_provider_get_alliance):
my_alliance = Alliance(
id=3001,
name='Dummy Alliance',
ticker='Dummy',
corp_ids=[2001, 2002, 2003],
executor_corp_id=2001
)
mock_provider_get_alliance.return_value = my_alliance
x = AllianceMixin(alliance_id=3001)
self.assertEqual(
x.alliance,
my_alliance
)
self.assertEqual(
x.alliance,
my_alliance
)
# should fetch alliance once only
self.assertEqual(mock_provider_get_alliance.call_count, 1)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
def test_alliance_not_defined(self, mock_provider_get_alliance):
mock_provider_get_alliance.return_value = None
x = AllianceMixin()
self.assertEqual(
x.alliance,
Entity(None, None)
)
self.assertEqual(mock_provider_get_alliance.call_count, 0)
class TestFactionMixin(TestCase):
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_defined(self, mock_provider_get_faction):
my_faction = Entity(id=1337, name='Permabanned')
mock_provider_get_faction.return_value = my_faction
x = FactionMixin(faction_id=3001)
self.assertEqual(
x.faction,
my_faction
)
self.assertEqual(
x.faction,
my_faction
)
# should fetch alliance once only
self.assertEqual(mock_provider_get_faction.call_count, 1)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
def test_faction_not_defined(self, mock_provider_get_faction):
mock_provider_get_faction.return_value = None
x = FactionMixin()
self.assertEqual(
x.faction,
Entity(None, None)
)
self.assertEqual(mock_provider_get_faction.call_count, 0)
class TestCorporation(TestCase): class TestCorporation(TestCase):
@patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance') @patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
@ -114,6 +181,7 @@ class TestCorporation(TestCase):
x.alliance, x.alliance,
Entity(None, None) Entity(None, None)
) )
self.assertEqual(mock_provider_get_alliance.call_count, 0)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_character') @patch(MODULE_PATH + '.EveSwaggerProvider.get_character')
def test_ceo(self, mock_provider_get_character): def test_ceo(self, mock_provider_get_character):
@ -142,6 +210,26 @@ class TestCorporation(TestCase):
# bug in ceo(): will try to fetch character even if ceo_id is None # bug in ceo(): will try to fetch character even if ceo_id is None
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_defined(self, mock_provider_get_faction):
my_faction = Entity(id=1337, name='Permabanned')
mock_provider_get_faction.return_value = my_faction
# fetch from provider if not defined
x = Corporation(faction_id=1337)
self.assertEqual(x.faction, my_faction)
# return existing if defined
mock_provider_get_faction.return_value = None
self.assertEqual(x.faction, my_faction)
self.assertEqual(mock_provider_get_faction.call_count, 1)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_undefined(self, mock_provider_get_faction):
x = Corporation()
self.assertEqual(x.faction, Entity())
self.assertEqual(mock_provider_get_faction.call_count, 0)
class TestAlliance(TestCase): class TestAlliance(TestCase):
@ -151,7 +239,8 @@ class TestAlliance(TestCase):
name='Dummy Alliance', name='Dummy Alliance',
ticker='Dummy', ticker='Dummy',
corp_ids=[2001, 2002, 2003], corp_ids=[2001, 2002, 2003],
executor_corp_id=2001 executor_corp_id=2001,
faction_id=1337
) )
@staticmethod @staticmethod
@ -232,6 +321,25 @@ class TestAlliance(TestCase):
Entity(None, None), Entity(None, None),
) )
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_defined(self, mock_provider_get_faction):
my_faction = Entity(id=1337, name='Permabanned')
mock_provider_get_faction.return_value = my_faction
# fetch from provider if not defined
self.assertEqual(self.my_alliance.faction, my_faction)
# return existing if defined
mock_provider_get_faction.return_value = None
self.assertEqual(self.my_alliance.faction, my_faction)
self.assertEqual(mock_provider_get_faction.call_count, 1)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_undefined(self, mock_provider_get_faction):
self.my_alliance.faction_id = None
self.assertEqual(self.my_alliance.faction, Entity())
self.assertEqual(mock_provider_get_faction.call_count, 0)
class TestCharacter(TestCase): class TestCharacter(TestCase):
@ -240,7 +348,8 @@ class TestCharacter(TestCase):
id=1001, id=1001,
name='Bruce Wayne', name='Bruce Wayne',
corp_id=2001, corp_id=2001,
alliance_id=3001 alliance_id=3001,
faction_id=1337,
) )
@patch(MODULE_PATH + '.EveSwaggerProvider.get_corp') @patch(MODULE_PATH + '.EveSwaggerProvider.get_corp')
@ -282,12 +391,32 @@ class TestCharacter(TestCase):
self.assertEqual(self.my_character.alliance, my_alliance) self.assertEqual(self.my_character.alliance, my_alliance)
# should call the provider one time only # should call the provider one time only
self.assertEqual(mock_provider_get_corp.call_count, 1)
self.assertEqual(mock_provider_get_alliance.call_count, 1) self.assertEqual(mock_provider_get_alliance.call_count, 1)
def test_alliance_has_none(self): @patch(MODULE_PATH + '.EveSwaggerProvider.get_alliance')
def test_alliance_has_none(self, mock_provider_get_alliance):
self.my_character.alliance_id = None self.my_character.alliance_id = None
self.assertEqual(self.my_character.alliance, Entity(None, None)) self.assertEqual(self.my_character.alliance, Entity(None, None))
self.assertEqual(mock_provider_get_alliance.call_count, 0)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_defined(self, mock_provider_get_faction):
my_faction = Entity(id=1337, name='Permabanned')
mock_provider_get_faction.return_value = my_faction
# fetch from provider if not defined
self.assertEqual(self.my_character.faction, my_faction)
# return existing if defined
mock_provider_get_faction.return_value = None
self.assertEqual(self.my_character.faction, my_faction)
self.assertEqual(mock_provider_get_faction.call_count, 1)
@patch(MODULE_PATH + '.EveSwaggerProvider.get_faction')
def test_faction_undefined(self, mock_provider_get_faction):
self.my_character.faction_id = None
self.assertEqual(self.my_character.faction, Entity())
self.assertEqual(mock_provider_get_faction.call_count, 0)
class TestItemType(TestCase): class TestItemType(TestCase):
@ -449,10 +578,10 @@ class TestEveSwaggerProvider(TestCase):
@patch(MODULE_PATH + '.esi_client_factory') @patch(MODULE_PATH + '.esi_client_factory')
def test_get_alliance(self, mock_esi_client_factory): def test_get_alliance(self, mock_esi_client_factory):
mock_esi_client_factory.return_value\ mock_esi_client_factory.return_value \
.Alliance.get_alliances_alliance_id \ .Alliance.get_alliances_alliance_id \
= TestEveSwaggerProvider.esi_get_alliances_alliance_id = TestEveSwaggerProvider.esi_get_alliances_alliance_id
mock_esi_client_factory.return_value\ mock_esi_client_factory.return_value \
.Alliance.get_alliances_alliance_id_corporations \ .Alliance.get_alliances_alliance_id_corporations \
= TestEveSwaggerProvider.esi_get_alliances_alliance_id_corporations = TestEveSwaggerProvider.esi_get_alliances_alliance_id_corporations
@ -477,7 +606,7 @@ class TestEveSwaggerProvider(TestCase):
@patch(MODULE_PATH + '.esi_client_factory') @patch(MODULE_PATH + '.esi_client_factory')
def test_get_corp(self, mock_esi_client_factory): def test_get_corp(self, mock_esi_client_factory):
mock_esi_client_factory.return_value\ mock_esi_client_factory.return_value \
.Corporation.get_corporations_corporation_id \ .Corporation.get_corporations_corporation_id \
= TestEveSwaggerProvider.esi_get_corporations_corporation_id = TestEveSwaggerProvider.esi_get_corporations_corporation_id
@ -503,10 +632,10 @@ class TestEveSwaggerProvider(TestCase):
@patch(MODULE_PATH + '.esi_client_factory') @patch(MODULE_PATH + '.esi_client_factory')
def test_get_character(self, mock_esi_client_factory): def test_get_character(self, mock_esi_client_factory):
mock_esi_client_factory.return_value\ mock_esi_client_factory.return_value \
.Character.get_characters_character_id \ .Character.get_characters_character_id \
= TestEveSwaggerProvider.esi_get_characters_character_id = TestEveSwaggerProvider.esi_get_characters_character_id
mock_esi_client_factory.return_value\ mock_esi_client_factory.return_value \
.Character.post_characters_affiliation \ .Character.post_characters_affiliation \
= TestEveSwaggerProvider.esi_post_characters_affiliation = TestEveSwaggerProvider.esi_post_characters_affiliation
@ -530,7 +659,7 @@ class TestEveSwaggerProvider(TestCase):
@patch(MODULE_PATH + '.esi_client_factory') @patch(MODULE_PATH + '.esi_client_factory')
def test_get_itemtype(self, mock_esi_client_factory): def test_get_itemtype(self, mock_esi_client_factory):
mock_esi_client_factory.return_value\ mock_esi_client_factory.return_value \
.Universe.get_universe_types_type_id \ .Universe.get_universe_types_type_id \
= TestEveSwaggerProvider.esi_get_universe_types_type_id = TestEveSwaggerProvider.esi_get_universe_types_type_id
@ -597,7 +726,7 @@ class TestEveSwaggerProvider(TestCase):
def test_user_agent_header(self): def test_user_agent_header(self):
my_provider = EveSwaggerProvider() my_provider = EveSwaggerProvider()
my_client = my_provider.client my_client = my_provider.client
operation = my_client.Status.get_status() operation = my_client.Universe.get_universe_factions()
self.assertEqual( self.assertEqual(
operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0' operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0'
) )

View File

@ -1,4 +1,3 @@
from django.contrib import admin from django.contrib import admin
from allianceauth.fleetactivitytracking.models import Fatlink, Fat from allianceauth.fleetactivitytracking.models import Fatlink, Fat

View File

@ -1,4 +1,3 @@
from django.apps import AppConfig from django.apps import AppConfig

View File

@ -1,4 +1,3 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-05 21:39 # Generated by Django 1.10.1 on 2016-09-05 21:39
from __future__ import unicode_literals
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -54,6 +53,6 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='fat', name='fat',
unique_together=set([('character', 'fatlink')]), unique_together={('character', 'fatlink')},
), ),
] ]

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-05 22:20 # Generated by Django 1.10.1 on 2016-09-05 22:20
from __future__ import unicode_literals
import datetime import datetime
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.1 on 2016-09-06 23:54 # Generated by Django 1.10.1 on 2016-09-06 23:54
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.utils.timezone import django.utils.timezone

View File

@ -1,5 +1,4 @@
# Generated by Django 1.10.5 on 2017-03-22 23:35 # Generated by Django 1.10.5 on 2017-03-22 23:35
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,10 +1,10 @@
{% extends 'allianceauth/base.html' %} {% extends 'allianceauth/base.html' %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Fleet Participation" %}{% endblock %} {% block page_title %}{% translate "Fleet Participation" %}{% endblock %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Character not found!" %}</h1> <h1 class="page-header text-center">{% translate "Character not found!" %}</h1>
<div class="col-lg-12 container" id="example"> <div class="col-lg-12 container" id="example">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
@ -15,8 +15,8 @@
<img class="ra-avatar img-responsive" src="{{ character_portrait_url }}"> <img class="ra-avatar img-responsive" src="{{ character_portrait_url }}">
</div> </div>
<div class="col-lg-10 col-sm-2"> <div class="col-lg-10 col-sm-2">
<div class="alert alert-danger" role="alert">{% trans "Character not registered!" %}</div> <div class="alert alert-danger" role="alert">{% translate "Character not registered!" %}</div>
{% trans "This character is not associated with an auth account." %} <a href=" {% url 'authentication:add_character' %}">{% trans "Add it here" %}</a> {% trans "before attempting to click fleet attendance links." %} {% translate "This character is not associated with an auth account." %} <a href=" {% url 'authentication:add_character' %}">{% translate "Add it here" %}</a> {% translate "before attempting to click fleet attendance links." %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,15 +3,15 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Create Fatlink" %}{% endblock page_title %} {% block page_title %}{% translate "Create Fatlink" %}{% endblock page_title %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Create Fleet Operation" %}</h1> <h1 class="page-header text-center">{% translate "Create Fleet Operation" %}</h1>
<div class="container-fluid"> <div class="container-fluid">
{% if badrequest %} {% if badrequest %}
<div class="alert alert-danger" role="alert">{% trans "Bad request!" %}</div> <div class="alert alert-danger" role="alert">{% translate "Bad request!" %}</div>
{% endif %} {% endif %}
{% for message in errormessages %} {% for message in errormessages %}
<div class="alert alert-danger" role="alert">{{ message }}</div> <div class="alert alert-danger" role="alert">{{ message }}</div>
@ -22,7 +22,7 @@
{% csrf_token %} {% csrf_token %}
{{ form|bootstrap }} {{ form|bootstrap }}
<br/> <br/>
<button class="btn btn-lg btn-primary btn-block" type="submit" name="submit_fat">{% trans "Create fatlink" %}</button> <button class="btn btn-lg btn-primary btn-block" type="submit" name="submit_fat">{% translate "Create fatlink" %}</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -2,29 +2,29 @@
{% load bootstrap %} {% load bootstrap %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Fatlink view" %}{% endblock page_title %} {% block page_title %}{% translate "Fatlink view" %}{% endblock page_title %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Edit fatlink" %} "{{ fatlink }}" <h1 class="page-header text-center">{% translate "Edit fatlink" %} "{{ fatlink }}"
<div class="text-right"> <div class="text-right">
<form> <form>
<button type="submit" onclick="return confirm('Are you sure?')" class="btn btn-danger" name="deletefat" value="True"> <button type="submit" onclick="return confirm('Are you sure?')" class="btn btn-danger" name="deletefat" value="True">
{% trans "Delete fat" %} {% translate "Delete fat" %}
</button> </button>
</form> </form>
</div> </div>
</h1> </h1>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading">{% trans "Registered characters" %}</div> <div class="panel-heading">{% translate "Registered characters" %}</div>
<div class="panel-body"> <div class="panel-body">
<table class="table table-responsive table-hover"> <table class="table table-responsive table-hover">
<tr> <tr>
<th class="text-center">{% trans "User" %}</th> <th class="text-center">{% translate "User" %}</th>
<th class="text-center">{% trans "Character" %}</th> <th class="text-center">{% translate "Character" %}</th>
<th class="text-center">{% trans "System" %}</th> <th class="text-center">{% translate "System" %}</th>
<th class="text-center">{% trans "Ship" %}</th> <th class="text-center">{% translate "Ship" %}</th>
<th class="text-center">{% trans "Eve Time" %}</th> <th class="text-center">{% translate "Eve Time" %}</th>
<th></th> <th></th>
</tr> </tr>
{% for fat in registered_fats %} {% for fat in registered_fats %}

View File

@ -3,15 +3,15 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Personal fatlink statistics" %}{% endblock page_title %} {% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ month }}, {{ year }}{% endblocktrans %} <h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ month }}, {{ year }}{% endblocktrans %}
{% if char_id %} {% if char_id %}
<div class="text-right"> <div class="text-right">
<a href="{% url 'fatlink:user_statistics_month' char_id previous_month|date:'Y' previous_month|date:'m' %}" class="btn btn-info">{% trans "Previous month" %}</a> <a href="{% url 'fatlink:user_statistics_month' char_id previous_month|date:'Y' previous_month|date:'m' %}" class="btn btn-info">{% translate "Previous month" %}</a>
<a href="{% url 'fatlink:user_statistics_month' char_id next_month|date:'Y' next_month|date:'m' %}" class="btn btn-info">{% trans "Next month" %}</a> <a href="{% url 'fatlink:user_statistics_month' char_id next_month|date:'Y' next_month|date:'m' %}" class="btn btn-info">{% translate "Next month" %}</a>
</div> </div>
{% endif %} {% endif %}
</h1> </h1>
@ -24,8 +24,8 @@
</h2> </h2>
<table class="table table-responsive"> <table class="table table-responsive">
<tr> <tr>
<th class="col-md-2 text-center">{% trans "Ship" %}</th> <th class="col-md-2 text-center">{% translate "Ship" %}</th>
<th class="col-md-2 text-center">{% trans "Times used" %}</th> <th class="col-md-2 text-center">{% translate "Times used" %}</th>
</tr> </tr>
{% for ship, n_fats in shipStats %} {% for ship, n_fats in shipStats %}
<tr> <tr>
@ -45,11 +45,11 @@
{% if created_fats %} {% if created_fats %}
<table class="table"> <table class="table">
<tr> <tr>
<th class="text-center">{% trans "Fleet" %}</th> <th class="text-center">{% translate "Fleet" %}</th>
<th class="text-center">{% trans "Creator" %}</th> <th class="text-center">{% translate "Creator" %}</th>
<th class="text-center">{% trans "Eve Time" %}</th> <th class="text-center">{% translate "Eve Time" %}</th>
<th class="text-center">{% trans "Duration" %}</th> <th class="text-center">{% translate "Duration" %}</th>
<th class="text-center">{% trans "Edit" %}</th> <th class="text-center">{% translate "Edit" %}</th>
</tr> </tr>
{% for link in created_fats %} {% for link in created_fats %}
<tr> <tr>

View File

@ -3,23 +3,23 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block page_title %}{% trans "Personal fatlink statistics" %}{% endblock page_title %} {% block page_title %}{% translate "Personal fatlink statistics" %}{% endblock page_title %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ year }}{% endblocktrans %} <h1 class="page-header text-center">{% blocktrans %}Participation data statistics for {{ year }}{% endblocktrans %}
<div class="text-right"> <div class="text-right">
<a href="{% url 'fatlink:personal_statistics_year' previous_year %}" class="btn btn-info">{% trans "Previous year" %}</a> <a href="{% url 'fatlink:personal_statistics_year' previous_year %}" class="btn btn-info">{% translate "Previous year" %}</a>
{% if next_year %} {% if next_year %}
<a href="{% url 'fatlink:personal_statistics_year' next_year %}" class="btn btn-info">{% trans "Next year" %}</a> <a href="{% url 'fatlink:personal_statistics_year' next_year %}" class="btn btn-info">{% translate "Next year" %}</a>
{% endif %} {% endif %}
</div> </div>
</h1> </h1>
<div class="col-lg-2 col-lg-offset-5"> <div class="col-lg-2 col-lg-offset-5">
<table class="table table-responsive"> <table class="table table-responsive">
<tr> <tr>
<th class="col-md-2 text-center">{% trans "Month" %}</th> <th class="col-md-2 text-center">{% translate "Month" %}</th>
<th class="col-md-2 text-center">{% trans "Fats" %}</th> <th class="col-md-2 text-center">{% translate "Fats" %}</th>
</tr> </tr>
{% for monthnr, month, n_fats in monthlystats %} {% for monthnr, month, n_fats in monthlystats %}
<tr> <tr>

Some files were not shown because too many files have changed in this diff Show More